Skip to content

Commit f70cd6a

Browse files
authored
Merge pull request #105 from geobrowser/ng/improve-auth-api
restructure authentication
2 parents 136235e + 79ee5c2 commit f70cd6a

File tree

6 files changed

+123
-153
lines changed

6 files changed

+123
-153
lines changed

apps/events/src/Boot.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { RouterProvider, createRouter } from '@tanstack/react-router';
22

3-
import { AuthProvider } from './components/auth';
3+
import { Identity } from '@graphprotocol/hypergraph';
4+
import { PrivyProvider } from '@privy-io/react-auth';
45
import { routeTree } from './routeTree.gen';
56

67
// Create a new router instance
@@ -15,8 +16,25 @@ declare module '@tanstack/react-router' {
1516

1617
export function Boot() {
1718
return (
18-
<AuthProvider>
19-
<RouterProvider router={router} />
20-
</AuthProvider>
19+
<PrivyProvider // note: PrivyProvider is only needed for the login page and the logout button in the navigation
20+
appId="cm4wx6ziv00ngrmfjf9ik36iu"
21+
config={{
22+
// Display email and wallet as login methods
23+
loginMethods: ['email', 'wallet', 'google', 'twitter', 'github'],
24+
// Customize Privy's appearance in your app
25+
appearance: {
26+
theme: 'light',
27+
accentColor: '#676FFF',
28+
},
29+
// Create embedded wallets for users who don't have a wallet
30+
embeddedWallets: {
31+
createOnLogin: 'users-without-wallets',
32+
},
33+
}}
34+
>
35+
<Identity.GraphLogin storage={localStorage}>
36+
<RouterProvider router={router} />
37+
</Identity.GraphLogin>
38+
</PrivyProvider>
2139
);
2240
}

apps/events/src/components/auth.tsx

Lines changed: 0 additions & 91 deletions
This file was deleted.

apps/events/src/components/logout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export function Logout() {
77
const { logout: graphLogout } = Identity.useGraphLogin();
88
const { logout: privyLogout } = usePrivy();
99
const router = useRouter();
10-
const disconnectWallet = () => {
11-
graphLogout();
12-
privyLogout();
10+
const disconnectWallet = async () => {
11+
await privyLogout();
12+
graphLogout(); // needs to be called after privy logout since it triggers a re-render
1313
router.navigate({
1414
to: '/login',
1515
});

apps/events/src/routes/__root.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { TanStackRouterDevtools } from '@tanstack/router-devtools';
66
export const Route = createRootRoute({
77
component: () => {
88
const { authenticated, getIdentity, getSessionToken } = Identity.useGraphLogin();
9-
109
const graphIdentity = getIdentity();
1110
const loggedInSessionToken = getSessionToken();
1211

@@ -28,7 +27,7 @@ export const Route = createRootRoute({
2827
<>
2928
<div className="flex flex-col min-h-screen">
3029
<header className="px-4 lg:px-6 h-14 flex items-center">
31-
<Link to="/" className="flex items-center justify-center">
30+
<Link to={authenticated ? '/' : '/login'} className="flex items-center justify-center">
3231
Home
3332
</Link>
3433
<nav className="ml-auto flex gap-4 sm:gap-6">
Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,110 @@
11
import { Button } from '@/components/ui/button';
22
import { availableAccounts } from '@/lib/availableAccounts';
33
import { Identity } from '@graphprotocol/hypergraph';
4-
import { usePrivy } from '@privy-io/react-auth';
5-
import { Link, createLazyFileRoute } from '@tanstack/react-router';
4+
import { usePrivy, useWallets } from '@privy-io/react-auth';
5+
import { createLazyFileRoute, useRouter } from '@tanstack/react-router';
6+
import { Loader2 } from 'lucide-react';
7+
import { useEffect, useState } from 'react';
8+
import { createWalletClient, custom } from 'viem';
9+
import { mainnet } from 'viem/chains';
610

711
export const Route = createLazyFileRoute('/login')({
812
component: () => <Login />,
913
});
1014

1115
function Login() {
12-
const { ready, authenticated, login } = usePrivy();
13-
// Disable login when Privy is not ready or the user is already authenticated
14-
const disableLogin = !ready || (ready && authenticated);
15-
const { setIdentityAndSessionToken, authenticated: graphAuthenticated } = Identity.useGraphLogin();
16+
const { ready: privyReady, login: privyLogin, signMessage, authenticated: privyAuthenticated } = usePrivy();
17+
const { ready: walletsReady, wallets } = useWallets();
18+
const { setIdentityAndSessionToken, login: hypergraphLogin } = Identity.useGraphLogin();
19+
const { navigate } = useRouter();
20+
const [hypergraphLoginStarted, setHypergraphLoginStarted] = useState(false);
21+
22+
useEffect(() => {
23+
if (
24+
!hypergraphLoginStarted && // avoid re-running the effect to often
25+
privyAuthenticated && // privy must be authenticated to run it
26+
walletsReady && // wallets must be ready to run it
27+
wallets.length > 0 // wallets must have at least one wallet to run it
28+
) {
29+
setHypergraphLoginStarted(true);
30+
(async () => {
31+
try {
32+
const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy') || wallets[0];
33+
const privyProvider = await embeddedWallet.getEthereumProvider();
34+
const walletClient = createWalletClient({
35+
chain: mainnet,
36+
transport: custom(privyProvider),
37+
});
38+
39+
const signer: Identity.Signer = {
40+
getAddress: async () => {
41+
const [address] = await walletClient.getAddresses();
42+
return address;
43+
},
44+
signMessage: async (message: string) => {
45+
if (embeddedWallet.walletClientType === 'privy') {
46+
const { signature } = await signMessage({ message });
47+
return signature;
48+
}
49+
const [address] = await walletClient.getAddresses();
50+
return await walletClient.signMessage({ account: address, message });
51+
},
52+
};
53+
54+
await hypergraphLogin(signer);
55+
navigate({ to: '/' });
56+
} catch (error) {
57+
alert('Failed to login');
58+
console.error(error);
59+
}
60+
})();
61+
}
62+
}, [hypergraphLoginStarted, walletsReady, wallets, signMessage, hypergraphLogin, navigate, privyAuthenticated]);
1663

1764
return (
1865
<div className="flex flex-1 justify-center items-center flex-col gap-4">
19-
{(!ready || !authenticated) && !graphAuthenticated ? (
66+
<div>
67+
<Button
68+
disabled={!privyReady || hypergraphLoginStarted}
69+
onClick={(event) => {
70+
event.preventDefault();
71+
privyLogin({});
72+
}}
73+
>
74+
Log in {hypergraphLoginStarted && <Loader2 className="ml-2 w-4 h-4 animate-spin" />}
75+
</Button>
76+
2077
<div>
21-
<Button disabled={disableLogin} onClick={login}>
22-
Log in
78+
<h1>Choose account</h1>
79+
<Button
80+
onClick={(event) => {
81+
event.preventDefault();
82+
setIdentityAndSessionToken(availableAccounts[0]);
83+
navigate({ to: '/' });
84+
}}
85+
>
86+
{availableAccounts[0].accountId.substring(0, 6)}
87+
</Button>
88+
<Button
89+
onClick={(event) => {
90+
event.preventDefault();
91+
setIdentityAndSessionToken(availableAccounts[1]);
92+
navigate({ to: '/' });
93+
}}
94+
>
95+
{availableAccounts[1].accountId.substring(0, 6)}
96+
</Button>
97+
<Button
98+
onClick={(event) => {
99+
event.preventDefault();
100+
setIdentityAndSessionToken(availableAccounts[2]);
101+
navigate({ to: '/' });
102+
}}
103+
>
104+
{availableAccounts[2].accountId.substring(0, 6)}
23105
</Button>
24-
25-
<div>
26-
<h1>Choose account</h1>
27-
<Button
28-
onClick={(event) => {
29-
event.preventDefault();
30-
setIdentityAndSessionToken(availableAccounts[0]);
31-
}}
32-
>
33-
{availableAccounts[0].accountId.substring(0, 6)}
34-
</Button>
35-
<Button
36-
onClick={(event) => {
37-
event.preventDefault();
38-
setIdentityAndSessionToken(availableAccounts[1]);
39-
}}
40-
>
41-
{availableAccounts[1].accountId.substring(0, 6)}
42-
</Button>
43-
<Button
44-
onClick={(event) => {
45-
event.preventDefault();
46-
setIdentityAndSessionToken(availableAccounts[2]);
47-
}}
48-
>
49-
{availableAccounts[2].accountId.substring(0, 6)}
50-
</Button>
51-
</div>
52106
</div>
53-
) : (
54-
<Link className="text-blue-500 hover:text-blue-600 underline" to="/">
55-
Go to Home
56-
</Link>
57-
)}
107+
</div>
58108
</div>
59109
);
60110
}

packages/hypergraph/src/identity/graph-login.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import type { Identity, Keys, Signer, Storage } from './types.js';
3434
export type LoginProps = {
3535
children: React.ReactNode;
3636
storage: Storage;
37-
signer: Signer | null;
3837
syncServer?: string;
3938
chainId?: number;
4039
};
@@ -51,7 +50,7 @@ const GraphLoginContext = createContext<{
5150
getAccountId: () => string | null;
5251
getIdentity: () => Identity | null;
5352
isAuthenticated: () => boolean;
54-
login: () => void;
53+
login: (signer: Signer) => Promise<void>;
5554
logout: () => void;
5655
authenticated: boolean;
5756
setIdentityAndSessionToken: (account: Identity & { sessionToken: string }) => void;
@@ -60,7 +59,7 @@ const GraphLoginContext = createContext<{
6059
getAccountId: () => null,
6160
getIdentity: () => null,
6261
isAuthenticated: () => false,
63-
login: () => {},
62+
login: () => Promise.resolve(),
6463
logout: () => {},
6564
authenticated: false,
6665
setIdentityAndSessionToken: () => {},
@@ -71,19 +70,14 @@ const GraphLoginContext = createContext<{
7170
// 2) a)Try to get identity from the sync server, or
7271
// b) If identity is not found, create a new identity
7372
// (and store it in the sync server)
74-
export function GraphLogin({
75-
children,
76-
storage,
77-
signer,
78-
syncServer = 'http://localhost:3030',
79-
chainId = 80451,
80-
}: LoginProps) {
73+
export function GraphLogin({ children, storage, syncServer = 'http://localhost:3030', chainId = 80451 }: LoginProps) {
8174
const [state, setState] = useState<GraphLoginState>({
8275
authenticated: false,
8376
accountId: null,
8477
sessionToken: null,
8578
keys: null,
8679
});
80+
8781
const getSessionToken = () => {
8882
return state.sessionToken;
8983
};
@@ -290,7 +284,7 @@ export function GraphLogin({
290284
}
291285
};
292286

293-
const login = async () => {
287+
const login = async (signer: Signer) => {
294288
if (!signer) {
295289
return;
296290
}

0 commit comments

Comments
 (0)