Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Login with auth server page #417

Merged
merged 20 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:

- name: build
run: npm run build
env:
CI: false

- name: test
run: npm run test
Expand Down Expand Up @@ -87,6 +89,7 @@ jobs:
- name: build for deployment
run: npm run build:full
env:
CI: false
GEN_STATIC_LOCAL: true

- uses: actions/upload-artifact@v2
Expand Down
9 changes: 6 additions & 3 deletions config-overrides.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ if (process.env.NODE_ENV === 'development') {
babelInclude([
// Src has to be included or else your own app won't be transpiled.
path.resolve(__dirname, 'src'),
// WalletConnectV2 modules that need to be transpiled.
path.resolve(__dirname, 'node_modules/decentraland-connect/node_modules/@walletconnect'),
path.resolve(__dirname, 'node_modules/@walletconnect')
// Other dependencies that need to be transpiled.
path.resolve(__dirname, 'node_modules/decentraland-connect/node_modules/ethers'),
path.resolve(__dirname, 'node_modules/@noble'),
path.resolve(__dirname, 'node_modules/@walletconnect'),
path.resolve(__dirname, 'node_modules/unstorage'),
path.resolve(__dirname, 'node_modules/@dcl/single-sign-on-client'),
])
)
}
Expand Down
8,656 changes: 3,242 additions & 5,414 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@
"@dcl/explorer": "^1.0.131583-20230718133624.commit-7106e1f",
"@dcl/feature-flags": "^1.2.0",
"@dcl/kernel-interface": "^2.0.0-20230512115658.commit-b582e05",
"@dcl/schemas": "^8.1.0",
"@dcl/schemas": "^9.10.0",
"@dcl/urn-resolver": "^1.4.0",
"@sentry/browser": "^7.45.0",
"@sentry/tracing": "^7.45.0",
"@types/uuid": "^9.0.7",
fzavalia marked this conversation as resolved.
Show resolved Hide resolved
"@types/validator": "^13.7.3",
"decentraland-connect": "^4.1.3",
"decentraland-dapps": "^15.5.0",
"decentraland-ui": "^4.1.0",
"decentraland-connect": "^5.3.1",
"decentraland-dapps": "^16.21.4",
"decentraland-ui": "^4.31.0",
"detect-browser": "^5.2.0",
"eth-connect": "^6.1.0",
"md5-file": "^5.0.0",
Expand All @@ -57,6 +58,7 @@
"react-scripts": "^4.0.3",
"react-virtualized": "^9.22.5",
"redux": "^4.0.5",
"uuid": "^9.0.1",
"validator": "^13.7.0"
},
"devDependencies": {
Expand Down
31 changes: 20 additions & 11 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import React, { useMemo } from 'react'
import { connect } from 'react-redux'
import { useMobileMediaQuery } from 'decentraland-ui/dist/components/Media'
import ErrorContainer from './errors/ErrorContainer'
import LoginContainer from './auth/LoginContainer'
import { isMobile } from '../integration/browser'
import { StoreType } from '../state/redux'
import { isElectron } from '../integration/desktop'
import { SHOW_WALLET_SELECTOR } from '../integration/url'
import { BeginnersGuide } from './auth/BeginnersGuide'
import { BigFooter } from './common/Layout/BigFooter'
import BannerContainer from './banners/BannerContainer'
import { LoadingRender } from './common/Loading/LoadingRender'
import { Navbar } from './common/Layout/Navbar'
import {
FeatureFlags,
isWaitingForRenderer,
isLoginComplete,
ABTestingVariant,
getFeatureVariantName,
getFeatureVariantValue
getFeatureVariantValue,
isFeatureEnabled
} from '../state/selectors'
import { SHOW_WALLET_SELECTOR } from '../integration/url'
import ErrorContainer from './errors/ErrorContainer'
import LoginContainer from './auth/LoginContainer'
import { BeginnersGuide } from './auth/BeginnersGuide'
import { BigFooter } from './common/Layout/BigFooter'
import BannerContainer from './banners/BannerContainer'
import { LoadingRender } from './common/Loading/LoadingRender'
import { Navbar } from './common/Layout/Navbar'
import StreamContainer from './common/StreamContainer'
import { Audio } from './common/Audio'
import { isMobile } from '../integration/browser'
import MobileContainer from './common/MobileContainer'
import CatalystWarningContainer from './warning/CatalystWarningContainer'
import { LoginWithAuthServerPage } from './auth/LoginWithAuthServerPage'
import './App.css'

function mapStateToProps(state: StoreType): AppProps {
Expand All @@ -41,6 +43,7 @@ function mapStateToProps(state: StoreType): AppProps {
const trustedCatalyst = !!state.catalyst?.trusted
const error = !!state.error.error
const sound = true // TODO: sound must be true after the first click
const isDesktopClientSignInWithAuthDappEnabled = isFeatureEnabled(state, FeatureFlags.DesktopClientSignInWithAuthDapp)

return {
seamlessLogin,
Expand All @@ -52,7 +55,8 @@ function mapStateToProps(state: StoreType): AppProps {
rendererReady,
trustedCatalyst,
error,
sound
sound,
isDesktopClientSignInWithAuthDappEnabled
}
}

Expand All @@ -67,6 +71,7 @@ export interface AppProps {
trustedCatalyst: boolean
error: boolean
sound: boolean
isDesktopClientSignInWithAuthDappEnabled: boolean
}

const App: React.FC<AppProps> = (props) => {
Expand Down Expand Up @@ -97,6 +102,10 @@ const App: React.FC<AppProps> = (props) => {
return <LoadingRender />
}

if (isElectron() && props.isDesktopClientSignInWithAuthDappEnabled) {
return <LoginWithAuthServerPage />
}

return (
<div className={`WebsiteApp ${props.hasBanner ? 'withBanner' : ''}`}>
<BannerContainer />
Expand Down
78 changes: 78 additions & 0 deletions src/components/auth/LoginWithAuthServerPage.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
.LoginWithAuthServerPage .background {
position: fixed;
height: 100%;
width: 100%;
background: url('/src/images/party.png') no-repeat center center fixed;
background-size: cover;
}

.LoginWithAuthServerPage .background::before {
content: '';
position: fixed;
width: 100%;
height: 350%;
background: radial-gradient(ellipse at 0 50%, transparent 10%, #c640cd 40%, #491975 70%);
top: -100%;
transform: rotate(180deg);
overflow: hidden;
}

.LoginWithAuthServerPage .main {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
height: 100%;
margin-left: 80px;
max-width: 500px;
}

.LoginWithAuthServerPage .main .logo {
width: 256px;
height: 128px;
background: url('/src/images/logo-white.svg') no-repeat center;
background-size: contain;
}

.LoginWithAuthServerPage .main .title {
color: var(--text-on-primary);
font-size: 36px;
font-weight: 600;
line-height: 44px;
}

.LoginWithAuthServerPage .main .subtitle {
color: var(--text-on-primary);
font-size: 36px;
font-weight: 400;
line-height: 44px;
}

.LoginWithAuthServerPage .main .button {
margin-top: 48px;
}

.LoginWithAuthServerPage .main .back {
width: 32px;
height: 32px;
cursor: pointer;
background: url('/src/images/back.svg') no-repeat center;
margin-bottom: 16px;
}

.LoginWithAuthServerPage .main .subtitle-sign-in-code {
color: var(--text-on-primary);
font-size: 20px;
font-weight: 400;
line-height: 24px;
margin-top: 16px;
}

.LoginWithAuthServerPage .main .code {
color: var(--text-on-primary);
font-size: 100px;
font-weight: 700;
line-height: 121px;
margin-top: 16px;
}
142 changes: 142 additions & 0 deletions src/components/auth/LoginWithAuthServerPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { useEffect, useRef, useState } from 'react'
import { ProviderType } from '@dcl/schemas'
import { Button } from 'decentraland-ui/dist/components/Button/Button'
import { AuthServerProvider, connection } from 'decentraland-connect'
import { authenticate } from '../../kernel-loader'
import './LoginWithAuthServerPage.css'

enum View {
WELCOME,
SIGN_IN_CODE,
EXPIRED
}

export const LoginWithAuthServerPage = () => {
const [view, setView] = useState<View>(View.WELCOME)
const [disabled, setDisabled] = useState(false)

const initSignInResultRef = useRef<any>()
const expirationTimeoutRef = useRef<NodeJS.Timeout>()

useEffect(() => {
const url = new URL(window.location.href)

switch (url.origin) {
case 'https://decentraland.org':
AuthServerProvider.setAuthServerUrl('https://auth-api.decentraland.org')
AuthServerProvider.setAuthDappUrl('https://decentraland.org/auth')
break
case 'https://decentraland.today':
AuthServerProvider.setAuthServerUrl('https://auth-api.decentraland.today')
AuthServerProvider.setAuthDappUrl('https://decentraland.today/auth')
break
default:
AuthServerProvider.setAuthServerUrl('https://auth-api.decentraland.zone')
AuthServerProvider.setAuthDappUrl('https://decentraland.zone/auth')
}

return () => {
clearTimeout(expirationTimeoutRef.current)
}
}, [])

useEffect(() => {
setDisabled(false)
}, [view])

if (view === View.WELCOME) {
return (
<div className="LoginWithAuthServerPage">
<div className="background"></div>
<div className="main">
<div className="logo"></div>
<div className="title">Discover a virtual social world</div>
<div className="subtitle">shaped by its community of creators & explorers.</div>
<Button
disabled={disabled}
className="button"
primary
onClick={async () => {
fzavalia marked this conversation as resolved.
Show resolved Hide resolved
setDisabled(true)

try {
await connection.tryPreviousConnection()
await authenticate(ProviderType.AUTH_SERVER)
} catch (e) {
// No previous connection.
// Continue with auth server login.
}

const initSignInResult = await AuthServerProvider.initSignIn()
initSignInResultRef.current = initSignInResult

expirationTimeoutRef.current = setTimeout(() => {
setView(View.EXPIRED)
}, new Date(initSignInResult.requestResponse.expiration).getTime() - Date.now())

setView(View.SIGN_IN_CODE)
}}
>
Start
</Button>
</div>
</div>
)
}

if (view === View.SIGN_IN_CODE) {
return (
<div className="LoginWithAuthServerPage">
<div className="background"></div>
<div className="main">
<div
className="back"
onClick={() => {
initSignInResultRef.current = null
setView(View.WELCOME)
}}
></div>
<div className="title">Secure sign-in step</div>
<div className="subtitle-sign-in-code">
Remember the verification number below. You'll be prompted to confirm it in your web browser to securely
link your sign in.
</div>
<div className="code">{initSignInResultRef.current!.requestResponse.code}</div>
<Button
disabled={disabled}
className="button"
primary
onClick={async () => {
setDisabled(true)
await AuthServerProvider.finishSignIn(initSignInResultRef.current!)
await authenticate(ProviderType.AUTH_SERVER)
}}
>
Continue to sign in
</Button>
</div>
</div>
)
}

if (view === View.EXPIRED) {
return (
<div className="LoginWithAuthServerPage">
<div className="background"></div>
<div className="main">
<div
className="back"
onClick={() => {
initSignInResultRef.current = null
setView(View.WELCOME)
}}
></div>
<div className="title">Looks like you took too long and the session expired.</div>
<div className="subtitle-sign-in-code">Please go back and try again.</div>
</div>
</div>
)
}

return <div>asd</div>
}
3 changes: 3 additions & 0 deletions src/images/back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/party.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/integration/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const initializeFeatureFlags = callOnce(async () => {
let ff = defaultFeatureFlagsState as FeatureFlagsResult

try {
ff = await fetchFlags({ applicationName: ['explorer'] })
ff = await fetchFlags({ applicationName: ['explorer', 'dapps'] })
} catch (err) {
console.error('Error fetching feature flags', err)
}
Expand Down
3 changes: 2 additions & 1 deletion src/state/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export function getRequiredAnalyticsContext(state: StoreType): SessionTraits {

export enum FeatureFlags {
Stream = 'explorer-stream',
SeamlessLogin = 'explorer-seamless_login_variant'
SeamlessLogin = 'explorer-seamless_login_variant',
DesktopClientSignInWithAuthDapp = 'dapps-desktop-client-with-auth-dapp'
}

export enum VariantNames {
Expand Down
Loading