Skip to content

Commit

Permalink
Authentication Rework
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans committed Aug 3, 2023
1 parent 31c2fc2 commit 698ff7b
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 617 deletions.
734 changes: 289 additions & 445 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions web/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"color-hash": "^2.0.2",
"connectkit": "^1.2.3",
"crypto-browserify": "^3.12.0",
"date-fns": "^2.28.0",
"dotenv": "^16.0.1",
Expand Down Expand Up @@ -67,9 +66,10 @@
"swr": "^2.1.5",
"tailwindcss": "^3.3.2",
"use-debounce": "^8.0.1",
"viem": "^1.5.2",
"vite": "^3.0.8",
"vite-tsconfig-paths": "^3.5.0",
"wagmi": "^0.12.0",
"wagmi": "^1.3.9",
"yup": "^0.32.11",
"zod": "^3.21.4",
"zustand": "^4.0.0-rc.1"
Expand Down
135 changes: 16 additions & 119 deletions web/app/src/_document.tsx
Original file line number Diff line number Diff line change
@@ -1,121 +1,22 @@
import { LoginFacade } from '@components/LoginFacade';
import { environment } from '@utils/environment';
import { localStorageProvider } from '@utils/swrStorage';
import {
ConnectKitProvider,
getDefaultClient,
SIWEConfig,
SIWEProvider,
SIWESession,
} from 'connectkit';
import { BrowserRouter } from 'react-router-dom';
import { SiweMessage } from 'siwe';
import { SWRConfig } from 'swr';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import { arbitrum, mainnet, optimism, polygon } from 'wagmi/chains';
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
import { publicProvider } from 'wagmi/providers/public';
import { createPublicClient, http } from 'viem';
import { createConfig, mainnet, WagmiConfig } from 'wagmi';

import { App } from './App';
import { useAuthState } from './hooks/useAuth';

const { chains, provider } = configureChains(
[mainnet, polygon, optimism, arbitrum],
[
jsonRpcProvider({
rpc: (chain) => {
if (chain.id == 1) return { http: 'https://rpc.ankr.com/eth' };

// eslint-disable-next-line unicorn/no-null
return null;
},
}),
jsonRpcProvider({
rpc: (chain) => {
if (chain.id == 1)
return { http: 'https://ethereum.publicnode.com' };

// eslint-disable-next-line unicorn/no-null
return null;
},
}),

publicProvider(),
]
);

const defaultClient = getDefaultClient({
appName: 'Edgeserver',
provider,
chains,
});

const wagmiClient = createClient({
...defaultClient,
const config = createConfig({
autoConnect: true,
publicClient: createPublicClient({
chain: mainnet,
transport: http(),
}),
});

const siweConfig: SIWEConfig = {
createMessage: ({ address, chainId, nonce }) => {
localStorage.setItem('address', address);
localStorage.setItem('chain_id', chainId.toString());

return new SiweMessage({
version: '1',
domain: window.location.host,
uri: window.location.origin,
address,
chainId: 0,
nonce,
statement: 'Sign in with Ethereum.',
}).prepareMessage();
},
// TODO: make fancy
getNonce: async () => {
return Date.now().toString();
},
verifyMessage: async ({ message, signature }) => {
const result = await fetch(environment.API_URL + '/siwe/verify', {
method: 'post',
body: JSON.stringify({ message, signature }),
});

const { token } = (await result.json()) as { token: string };

useAuthState.setState({ auth_token: token });

return result.status == 200;
},
getSession: async () => {
const token = useAuthState.getState().auth_token;

// eslint-disable-next-line unicorn/no-null
if (!token) return null;

const headers = new Headers();

headers.append('authorization', 'Bearer ' + token);

const result = await fetch(environment.API_URL + '/siwe/session', {
method: 'get',
headers,
});

if (result.status == 200)
return {
address: localStorage.getItem('address'),
chainId: 0,
} as SIWESession;

// eslint-disable-next-line unicorn/no-null
return null;
},
signOut: async () => {
useAuthState.setState({ auth_token: '' });

return true;
},
};

export const Document = () => {
return (
<SWRConfig
Expand All @@ -139,18 +40,14 @@ export const Document = () => {
},
}}
>
<WagmiConfig client={wagmiClient}>
<SIWEProvider {...siweConfig}>
<ConnectKitProvider>
<BrowserRouter>
<div className="text-black-800 min-h-screen w-full dark:text-white">
<LoginFacade>
<App />
</LoginFacade>
</div>
</BrowserRouter>
</ConnectKitProvider>
</SIWEProvider>
<WagmiConfig config={config}>
<BrowserRouter>
<div className="text-black-800 min-h-screen w-full dark:text-white">
<LoginFacade>
<App />
</LoginFacade>
</div>
</BrowserRouter>
</WagmiConfig>
</SWRConfig>
);
Expand Down
44 changes: 15 additions & 29 deletions web/app/src/hooks/useAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { SIWESession, useSIWE } from 'connectkit';
import { StatusState } from 'connectkit/build/siwe/SIWEContext';
import { useAccount } from 'wagmi';
import create from 'zustand';
import { persist } from 'zustand/middleware';
Expand Down Expand Up @@ -35,38 +33,26 @@ export type useAuthResult = {
signOut?: () => Promise<boolean>;
};

type POLYFILL_HOOK_PROP = {
isSignedIn: boolean;
data?: SIWESession;
status: StatusState;
error?: Error | any;
isRejected: boolean;
isError: boolean;
isLoading: boolean;
isSuccess: boolean;
isReady: boolean;
reset: () => void;
signIn: () => Promise<boolean>;
signOut: () => Promise<boolean>;
};

export const useAuth = (): useAuthResult => {
const { auth_token, setAuthToken } = useAuthState();
const { address } = useAccount();
const { isSignedIn, data, status, signOut, signIn } =
useSIWE() as POLYFILL_HOOK_PROP;
// const { isSignedIn, data, status, signOut, signIn } =
// useSIWE() as POLYFILL_HOOK_PROP;

if (!auth_token || !address || !isSignedIn) {
return {
state: AuthState.LoggedOut,
user: address,
signIn,
};
}
// if (!auth_token || !address || !isSignedIn) {
// return {
// state: AuthState.LoggedOut,
// user: address,
// signIn,
// };
// }

// return {
// state: AuthState.LoggedIn,
// user: address,
// signOut,
// };
return {
state: AuthState.LoggedIn,
user: address,
signOut,
state: AuthState.LoggedOut,
};
};
27 changes: 7 additions & 20 deletions web/app/src/pages/login/components/LoginButton.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
/* eslint-disable jsx-a11y/alt-text */
import { ConnectKitButton } from 'connectkit';
import { useAuth } from 'src/hooks/useAuth';

export const LoginButton = () => {
const { user } = useAuth();

return (
<ConnectKitButton.Custom>
{({
isConnected,
isConnecting,
show,
hide,
address,
ensName,
chain,
}) => {
return (
<button
className="group flex items-center justify-center rounded-lg bg-gray-100 p-2 font-bold transition hover:brightness-95"
onClick={show}
>
Connect App
</button>
);
<button
className="group flex items-center justify-center rounded-lg bg-gray-100 p-2 font-bold transition hover:brightness-95"
onClick={() => {
console.log('login');
}}
</ConnectKitButton.Custom>
>
Connect App
</button>
);
};
26 changes: 26 additions & 0 deletions web/app/src/pages/login/components/SelectableAccount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-disable react/display-name */
import { toComponent } from '@utils/toComponent';
import { FC } from 'react';
import { FiArrowRight } from 'react-icons/fi';

export const SelectableAccount: FC<{
data: { name: string; avatar: string };
}> = ({ data }) => {
return (
<li key={'account-' + data.name}>
<button className="group flex w-full items-center gap-x-2 rounded-xl p-2 font-bold outline-neutral-400 transition hover:bg-gray-100 focus:bg-gray-100">
<div className="h-8 w-8 overflow-hidden rounded-full bg-gray-200">
<img
src={data.avatar}
className="h-8 w-8 rounded-full"
alt=""
/>
</div>
<span className="grow text-left">{data.name}</span>
<FiArrowRight className="-translate-x-3 opacity-0 transition-all group-hover:translate-x-0 group-hover:opacity-100 group-focus:translate-x-0 group-focus:opacity-100" />
</button>
</li>
);
};

export const toSelectableAccount = toComponent(SelectableAccount);
20 changes: 19 additions & 1 deletion web/app/src/pages/login/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import { LoginButton } from './components/LoginButton';
import { toSelectableAccount } from './components/SelectableAccount';

export const LoginPage = () => {
const lastAcconts = [
{
name: 'luc.eth',
avatar: 'https://metadata.ens.domains/mainnet/avatar/luc.eth',
},
{
name: 'bckup.eth',
avatar: 'https://metadata.ens.domains/mainnet/avatar/bckup.eth',
},
];

return (
<div className="flex h-screen w-screen items-center justify-center bg-neutral-100/10 text-black">
<div className="flex w-full max-w-xs flex-col justify-center gap-y-2 rounded-xl border bg-white p-4 shadow-sm">
<div className="p-2">Edgeserver</div>

{lastAcconts && (
<ul>{lastAcconts?.map(toSelectableAccount)}</ul>
)}

<div className="px-3 pb-2 text-sm">
<p>
You're about to sign in using passkeys. If you need help{' '}
You&apos;re about to sign in using passkeys. If you need
help{' '}
<a
href="https://og.ax"
className="text-blue-500 hover:underline"
Expand Down
12 changes: 12 additions & 0 deletions web/app/src/utils/toComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable react/display-name */
import { FC } from 'react';

export type DataFromComponent<T> = T extends FC<{ data: infer O }> ? O : never;

export const toComponent =
<C, O = DataFromComponent<C>>(Component: C) =>
(object: O, index: number) =>
(
// @ts-ignore
<Component data={object} key={index} />
);
6 changes: 5 additions & 1 deletion web/app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import GlobalPolyFill from '@esbuild-plugins/node-globals-polyfill';
import inject from '@rollup/plugin-inject';
import react from '@vitejs/plugin-react';
import nodePolyfills from 'rollup-plugin-polyfill-node';
import { defineConfig } from 'vite';
Expand All @@ -23,15 +22,20 @@ export default defineConfig({
commonjsOptions: {
transformMixedEsModules: true,
},
target: ['esnext'],
},
optimizeDeps: {
esbuildOptions: {
target: 'esnext',
// Node.js global to browser globalThis
define: {
global: 'globalThis',
},
// Enable esbuild polyfill plugins
plugins: [GlobalPolyFill({ process: true, buffer: true })],
supported: {
bigint: true,
},
},
},
resolve: {
Expand Down

0 comments on commit 698ff7b

Please sign in to comment.