Implement authentication with SIWE #4
@ -5,19 +5,6 @@ import { authenticateUser, createUser } from '../turnkey-backend';
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
//
|
|
||||||
// Access Code
|
|
||||||
//
|
|
||||||
router.post('/accesscode', async (req, res) => {
|
|
||||||
console.log('Access Code', req.body);
|
|
||||||
const { accesscode } = req.body;
|
|
||||||
if (accesscode === '44444') {
|
|
||||||
return res.send({ isValid: true });
|
|
||||||
} else {
|
|
||||||
return res.sendStatus(204);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Turnkey
|
// Turnkey
|
||||||
//
|
//
|
||||||
@ -59,10 +46,6 @@ router.post('/authenticate', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
|
||||||
// Lit
|
|
||||||
//
|
|
||||||
|
|
||||||
router.post('/validate', async (req, res) => {
|
router.post('/validate', async (req, res) => {
|
||||||
const { message, signature } = req.body;
|
const { message, signature } = req.body;
|
||||||
const { success, data } = await new SiweMessage(message).verify({
|
const { success, data } = await new SiweMessage(message).verify({
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { ReactNode, Suspense } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import assert from 'assert';
|
||||||
import { SiweMessage, generateNonce } from 'siwe';
|
import { SiweMessage, generateNonce } from 'siwe';
|
||||||
import { WagmiProvider } from 'wagmi';
|
import { WagmiProvider } from 'wagmi';
|
||||||
import { arbitrum, mainnet } from 'wagmi/chains';
|
import { arbitrum, mainnet } from 'wagmi/chains';
|
||||||
@ -12,12 +13,16 @@ import type {
|
|||||||
} from '@web3modal/core';
|
} from '@web3modal/core';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
// TODO: Use environment variable
|
import { VITE_WALLET_CONNECT_ID, baseUrl } from 'utils/constants';
|
||||||
const WALLET_CONNECT_ID="d37f5a2f09d22f5e3ccaff4bbc93d37c"
|
|
||||||
|
if (!VITE_WALLET_CONNECT_ID) {
|
||||||
|
throw new Error('Error: REACT_APP_WALLET_CONNECT_ID env config is not set');
|
||||||
|
}
|
||||||
|
assert(baseUrl, 'VITE_SERVER_URL is not set in env');
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
// TODO: Use environment variable
|
baseURL: baseUrl,
|
||||||
baseURL: 'http://localhost:8000',
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
@ -33,7 +38,7 @@ const metadata = {
|
|||||||
const chains = [mainnet, arbitrum] as const;
|
const chains = [mainnet, arbitrum] as const;
|
||||||
const config = defaultWagmiConfig({
|
const config = defaultWagmiConfig({
|
||||||
chains,
|
chains,
|
||||||
projectId: WALLET_CONNECT_ID,
|
projectId: VITE_WALLET_CONNECT_ID,
|
||||||
metadata,
|
metadata,
|
||||||
});
|
});
|
||||||
const siweConfig = createSIWEConfig({
|
const siweConfig = createSIWEConfig({
|
||||||
@ -95,7 +100,7 @@ const siweConfig = createSIWEConfig({
|
|||||||
createWeb3Modal({
|
createWeb3Modal({
|
||||||
siweConfig,
|
siweConfig,
|
||||||
wagmiConfig: config,
|
wagmiConfig: config,
|
||||||
projectId: WALLET_CONNECT_ID,
|
projectId: VITE_WALLET_CONNECT_ID,
|
||||||
});
|
});
|
||||||
export default function Web3ModalProvider({
|
export default function Web3ModalProvider({
|
||||||
children,
|
children,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CloudyFlow } from 'components/CloudyFlow';
|
import { CloudyFlow } from 'components/CloudyFlow';
|
||||||
import { SnowballAuth } from './auth/SnowballAuth';
|
import { Login } from './auth/Login';
|
||||||
|
|
||||||
const AuthPage = () => {
|
const AuthPage = () => {
|
||||||
return (
|
return (
|
||||||
@ -18,7 +18,7 @@ const AuthPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="pb-12 relative z-10 flex-1 flex-center">
|
<div className="pb-12 relative z-10 flex-1 flex-center">
|
||||||
<div className="max-w-[520px] w-full bg-white rounded-xl shadow">
|
<div className="max-w-[520px] w-full bg-white rounded-xl shadow">
|
||||||
<SnowballAuth />
|
<Login />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CloudyFlow>
|
</CloudyFlow>
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import { Button } from 'components/shared/Button';
|
|
||||||
import {
|
|
||||||
ArrowRightCircleFilledIcon,
|
|
||||||
LoaderIcon,
|
|
||||||
} from 'components/shared/CustomIcon';
|
|
||||||
import { WavyBorder } from 'components/shared/WavyBorder';
|
|
||||||
import { VerifyCodeInput } from 'components/shared/VerifyCodeInput';
|
|
||||||
import { verifyAccessCode } from 'utils/accessCode';
|
|
||||||
|
|
||||||
type AccessMethod = 'accesscode' | 'passkey';
|
|
||||||
|
|
||||||
type Err = { type: AccessMethod; message: string };
|
|
||||||
|
|
||||||
type AccessCodeProps = {
|
|
||||||
onCorrectAccessCode: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccessCode: React.FC<AccessCodeProps> = ({
|
|
||||||
onCorrectAccessCode,
|
|
||||||
}) => {
|
|
||||||
const [accessCode, setAccessCode] = useState(' ');
|
|
||||||
const [error, setError] = useState<Err | null>();
|
|
||||||
const [accessMethod, setAccessMethod] = useState<AccessMethod | false>(false);
|
|
||||||
|
|
||||||
async function validateAccessCode() {
|
|
||||||
setAccessMethod('accesscode');
|
|
||||||
try {
|
|
||||||
const isValidAccessCode = await verifyAccessCode(accessCode);
|
|
||||||
|
|
||||||
// add a pause for ux
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
||||||
if (isValidAccessCode) {
|
|
||||||
localStorage.setItem('accessCode', accessCode);
|
|
||||||
onCorrectAccessCode();
|
|
||||||
} else {
|
|
||||||
setError({
|
|
||||||
type: 'accesscode',
|
|
||||||
message: 'Invalid access code',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setError({ type: 'accesscode', message: err.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loading = accessMethod;
|
|
||||||
const isValidAccessCodeLength = accessCode.trim().length === 5;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="self-stretch p-3 xs:p-6 flex-col justify-center items-center gap-5 flex">
|
|
||||||
<div className="self-stretch text-center text-sky-950 text-2xl font-medium font-display leading-tight">
|
|
||||||
Access Code
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<WavyBorder className="self-stretch" variant="stroke" />
|
|
||||||
<div className="self-stretch p-4 xs:p-6 flex-col justify-center items-center gap-8 flex">
|
|
||||||
<div className="self-stretch flex-col gap-8 flex">
|
|
||||||
<div className="flex-col justify-start items-start gap-2 inline-flex">
|
|
||||||
<VerifyCodeInput
|
|
||||||
loading={!!loading}
|
|
||||||
code={accessCode}
|
|
||||||
setCode={setAccessCode}
|
|
||||||
submitCode={validateAccessCode}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
rightIcon={
|
|
||||||
loading && loading === 'accesscode' ? (
|
|
||||||
<LoaderIcon className="animate-spin" />
|
|
||||||
) : (
|
|
||||||
<ArrowRightCircleFilledIcon height="16" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={validateAccessCode}
|
|
||||||
variant={'secondary'}
|
|
||||||
disabled={!accessCode || !isValidAccessCodeLength || !!loading}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
{error && error.type === 'accesscode' && (
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
<div className="justify-center items-center gap-2 inline-flex">
|
|
||||||
<div className="text-red-500 text-sm">
|
|
||||||
Error: {error.message}.{' '}
|
|
||||||
<a href="/signup" className="underline">
|
|
||||||
Try again?
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,20 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import { AccessCode } from './AccessCode';
|
|
||||||
import { SignUp } from './SignUp';
|
|
||||||
|
|
||||||
type AccessSignUpProps = {
|
|
||||||
onDone: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccessSignUp: React.FC<AccessSignUpProps> = ({ onDone }) => {
|
|
||||||
const [isValidAccessCode, setIsValidAccessCode] = useState<boolean>(
|
|
||||||
!!localStorage.getItem('accessCode'),
|
|
||||||
);
|
|
||||||
|
|
||||||
return isValidAccessCode ? (
|
|
||||||
<SignUp onDone={onDone} />
|
|
||||||
) : (
|
|
||||||
<AccessCode onCorrectAccessCode={() => setIsValidAccessCode(true)} />
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,287 +0,0 @@
|
|||||||
import { Button } from 'components/shared/Button';
|
|
||||||
import {
|
|
||||||
ArrowRightCircleFilledIcon,
|
|
||||||
GithubIcon,
|
|
||||||
LoaderIcon,
|
|
||||||
} from 'components/shared/CustomIcon';
|
|
||||||
import { GoogleIcon } from 'components/shared/CustomIcon/GoogleIcon';
|
|
||||||
import { DotBorder } from 'components/shared/DotBorder';
|
|
||||||
import { WavyBorder } from 'components/shared/WavyBorder';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useSnowball } from 'utils/use-snowball';
|
|
||||||
import { Input } from 'components/shared/Input';
|
|
||||||
import { AppleIcon } from 'components/shared/CustomIcon/AppleIcon';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { useToast } from 'components/shared/Toast';
|
|
||||||
import { PKPEthersWallet } from '@lit-protocol/pkp-ethers';
|
|
||||||
import { signInWithEthereum } from 'utils/siwe';
|
|
||||||
import { logError } from 'utils/log-error';
|
|
||||||
import {
|
|
||||||
subOrganizationIdForEmail,
|
|
||||||
turnkeySignin,
|
|
||||||
turnkeySignup,
|
|
||||||
} from 'utils/turnkey-frontend';
|
|
||||||
import { verifyAccessCode } from 'utils/accessCode';
|
|
||||||
|
|
||||||
type Provider = 'google' | 'github' | 'apple' | 'email';
|
|
||||||
|
|
||||||
type Err = { type: 'email' | 'provider'; message: string };
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
onDone: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SignUp = ({ onDone }: Props) => {
|
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [error, setError] = useState<Err | null>();
|
|
||||||
const [provider, setProvider] = useState<Provider | false>(false);
|
|
||||||
|
|
||||||
const { toast } = useToast();
|
|
||||||
const snowball = useSnowball();
|
|
||||||
|
|
||||||
async function handleSignupRedirect() {
|
|
||||||
let wallet: PKPEthersWallet | undefined;
|
|
||||||
const { google } = snowball.auth;
|
|
||||||
if (google.canHandleOAuthRedirectBack()) {
|
|
||||||
setProvider('google');
|
|
||||||
try {
|
|
||||||
await google.handleOAuthRedirectBack();
|
|
||||||
// @ts-ignore
|
|
||||||
wallet = await google.getEthersWallet();
|
|
||||||
// @ts-ignore
|
|
||||||
const result = await signInWithEthereum(1, 'signup', wallet);
|
|
||||||
if (result.error) {
|
|
||||||
setError({ type: 'provider', message: result.error });
|
|
||||||
setProvider(false);
|
|
||||||
wallet = undefined;
|
|
||||||
logError(new Error(result.error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setError({ type: 'provider', message: err.message });
|
|
||||||
setProvider(false);
|
|
||||||
logError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if (apple.canHandleOAuthRedirectBack()) {
|
|
||||||
// setProvider('apple');
|
|
||||||
// try {
|
|
||||||
// await apple.handleOAuthRedirectBack();
|
|
||||||
// wallet = await apple.getEthersWallet();
|
|
||||||
// const result = await signInWithEthereum(1, 'signup', wallet);
|
|
||||||
// if (result.error) {
|
|
||||||
// setError({ type: 'provider', message: result.error });
|
|
||||||
// setProvider(false);
|
|
||||||
// wallet = undefined;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// } catch (err: any) {
|
|
||||||
// setError({ type: 'provider', message: err.message });
|
|
||||||
// setProvider(false);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (wallet) {
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function authEmail() {
|
|
||||||
setProvider('email');
|
|
||||||
try {
|
|
||||||
const orgId = await subOrganizationIdForEmail(email);
|
|
||||||
console.log('orgId', orgId);
|
|
||||||
if (orgId) {
|
|
||||||
await turnkeySignin(orgId);
|
|
||||||
window.location.href = '/dashboard';
|
|
||||||
} else {
|
|
||||||
await turnkeySignup(email);
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
setError({ type: 'email', message: err.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleSignupRedirect();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const loading = provider;
|
|
||||||
const emailValid = /.@./.test(email);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const validateAccessCode = async () => {
|
|
||||||
const accessCode = localStorage.getItem('accessCode');
|
|
||||||
if (!accessCode) {
|
|
||||||
redirectToSignup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await verifyAccessCode(accessCode);
|
|
||||||
} catch (err: any) {
|
|
||||||
redirectToSignup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const redirectToSignup = () => {
|
|
||||||
localStorage.removeItem('accessCode');
|
|
||||||
window.location.href = '/signup';
|
|
||||||
};
|
|
||||||
|
|
||||||
validateAccessCode();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="self-stretch p-3 xs:p-6 flex-col justify-center items-center gap-5 flex">
|
|
||||||
<div className="self-stretch text-center text-sky-950 text-2xl font-medium font-display leading-tight">
|
|
||||||
Sign up to Snowball
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<WavyBorder className="self-stretch" variant="stroke" />
|
|
||||||
<div className="self-stretch p-4 xs:p-6 flex-col justify-center items-center gap-8 flex">
|
|
||||||
<div className="self-stretch flex-col justify-center items-center gap-3 flex">
|
|
||||||
<Button
|
|
||||||
leftIcon={loading && loading === 'google' ? null : <GoogleIcon />}
|
|
||||||
rightIcon={
|
|
||||||
loading && loading === 'google' ? (
|
|
||||||
<LoaderIcon className="animate-spin" />
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
setProvider('google');
|
|
||||||
snowball.auth.google.startOAuthRedirect();
|
|
||||||
}}
|
|
||||||
className="flex-1 self-stretch"
|
|
||||||
variant={'tertiary'}
|
|
||||||
disabled={!!loading}
|
|
||||||
>
|
|
||||||
Continue with Google
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
leftIcon={<GithubIcon />}
|
|
||||||
rightIcon={
|
|
||||||
loading && loading === 'github' ? (
|
|
||||||
<LoaderIcon className="animate-spin" />
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
setProvider('github');
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
||||||
setProvider(false);
|
|
||||||
toast({
|
|
||||||
id: 'coming-soon',
|
|
||||||
title: 'Sign-in with GitHub is coming soon!',
|
|
||||||
variant: 'info',
|
|
||||||
onDismiss() {},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className="flex-1 self-stretch"
|
|
||||||
variant={'tertiary'}
|
|
||||||
disabled={!!loading}
|
|
||||||
>
|
|
||||||
Continue with GitHub
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
leftIcon={<AppleIcon />}
|
|
||||||
rightIcon={
|
|
||||||
loading && loading === 'apple' ? (
|
|
||||||
<LoaderIcon className="animate-spin text-white" />
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
onClick={async () => {
|
|
||||||
setProvider('apple');
|
|
||||||
// snowball.auth.apple.startOAuthRedirect();
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
||||||
setProvider(false);
|
|
||||||
toast({
|
|
||||||
id: 'coming-soon',
|
|
||||||
title: 'Sign-in with Apple is coming soon!',
|
|
||||||
variant: 'info',
|
|
||||||
onDismiss() {},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className={`flex-1 self-stretch border-black enabled:bg-black text-white ${
|
|
||||||
loading && loading === 'apple' ? 'disabled:bg-black' : ''
|
|
||||||
}`}
|
|
||||||
variant={'tertiary'}
|
|
||||||
disabled={!!loading}
|
|
||||||
>
|
|
||||||
Continue with Apple
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{error && error.type === 'provider' && (
|
|
||||||
<div className="-mt-3 justify-center items-center inline-flex">
|
|
||||||
<div className="text-red-500 text-sm">Error: {error.message}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="self-stretch justify-start items-center gap-8 inline-flex">
|
|
||||||
<DotBorder className="flex-1" />
|
|
||||||
<div className="text-center text-slate-400 text-xs font-normal font-['JetBrains Mono'] leading-none">
|
|
||||||
OR
|
|
||||||
</div>
|
|
||||||
<DotBorder className="flex-1" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="self-stretch flex-col gap-8 flex">
|
|
||||||
<div className="flex-col justify-start items-start gap-2 inline-flex">
|
|
||||||
<div className="text-sky-950 text-sm font-normal font-['Inter'] leading-tight">
|
|
||||||
Email
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
disabled={!!loading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
rightIcon={
|
|
||||||
loading && loading === 'email' ? (
|
|
||||||
<LoaderIcon className="animate-spin" />
|
|
||||||
) : (
|
|
||||||
<ArrowRightCircleFilledIcon height="16" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onClick={() => {
|
|
||||||
authEmail();
|
|
||||||
}}
|
|
||||||
variant={'secondary'}
|
|
||||||
disabled={!email || !emailValid || !!loading}
|
|
||||||
>
|
|
||||||
Continue with Email
|
|
||||||
</Button>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
{error && error.type === 'email' && (
|
|
||||||
<div className="justify-center items-center gap-2 inline-flex">
|
|
||||||
<div className="text-red-500 text-sm">
|
|
||||||
Error: {error.message}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="justify-center items-center gap-2 inline-flex">
|
|
||||||
<div className="text-center text-slate-600 text-sm font-normal font-['Inter'] leading-tight">
|
|
||||||
Already an user?
|
|
||||||
</div>
|
|
||||||
<div className="justify-center items-center gap-1.5 flex">
|
|
||||||
<Link
|
|
||||||
to="/login"
|
|
||||||
className="text-sky-950 text-sm font-normal font-['Inter'] underline leading-tight"
|
|
||||||
>
|
|
||||||
Sign in now
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,49 +0,0 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { snowball } from 'utils/use-snowball';
|
|
||||||
import { Login } from './Login';
|
|
||||||
import { Done } from './Done';
|
|
||||||
import { AccessSignUp } from './AccessSignUp';
|
|
||||||
|
|
||||||
type Screen = 'login' | 'signup' | 'success';
|
|
||||||
|
|
||||||
const DASHBOARD_URL = '/';
|
|
||||||
|
|
||||||
export const SnowballAuth: React.FC = () => {
|
|
||||||
const path = window.location.pathname;
|
|
||||||
const [screen, setScreen] = useState<Screen>(
|
|
||||||
path === '/login' ? 'login' : 'signup',
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (snowball.session) {
|
|
||||||
window.location.href = DASHBOARD_URL;
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (path === '/login') {
|
|
||||||
setScreen('login');
|
|
||||||
} else if (path === '/signup') {
|
|
||||||
setScreen('signup');
|
|
||||||
}
|
|
||||||
}, [path]);
|
|
||||||
|
|
||||||
if (screen === 'signup') {
|
|
||||||
return (
|
|
||||||
<AccessSignUp
|
|
||||||
onDone={() => {
|
|
||||||
setScreen('success');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (screen === 'login') {
|
|
||||||
return (
|
|
||||||
<Login />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (screen === 'success') {
|
|
||||||
return <Done continueTo={DASHBOARD_URL} />;
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user