Skip to content

Commit a88e212

Browse files
Update Privy section of Base Account (#424)
* setup privy project with base account * update docs.json * updates * update sub accounts * update link * remove commit * update * add * update setup instructions * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/authentication.mdx Co-authored-by: Stephan Cilliers <[email protected]> * update auth * Update docs/base-account/framework-integrations/privy/wallet-actions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/sub-accounts.mdx Co-authored-by: Stephan Cilliers <[email protected]> * Update docs/base-account/framework-integrations/privy/spend-permissions.mdx Co-authored-by: Stephan Cilliers <[email protected]> * update following comments * remove spend permissions --------- Co-authored-by: Stephan Cilliers <[email protected]>
1 parent ab453af commit a88e212

File tree

8 files changed

+1173
-588
lines changed

8 files changed

+1173
-588
lines changed
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
---
2+
title: "Auth (Sign In With Base)"
3+
description: "Manage user authentication with Privy and Base Account"
4+
---
5+
6+
import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx";
7+
8+
Learn how to handle authentication flows with Privy and Base Account, including both Privy-managed authentication and custom backend verification.
9+
10+
## Overview
11+
12+
Privy handles the initial authentication flow, managing user sessions and wallet connections. You can also implement additional authentication layers for enhanced security or custom requirements.
13+
14+
The code snippets in this guide are based on the following example project:
15+
<GithubRepoCard
16+
title="Base Account Privy Template"
17+
githubUrl="https://github.com/base/base-account-privy"
18+
/>
19+
20+
## Authentication Flow
21+
22+
Privy manages the primary authentication before users enter your application:
23+
24+
<div style={{ display: 'flex', justifyContent: 'center'}}>
25+
<img src="/images/base-account/privy-base-auth.gif" alt="Privy Base Auth" style={{ width: '600px', height: 'auto' }} />
26+
</div>
27+
28+
## Custom Authentication
29+
30+
For additional security or custom authentication requirements, you can implement backend verification using Sign-In with Ethereum (SIWE)
31+
with the Base Account SDK.
32+
33+
### Setup
34+
35+
Follow the [Setup](/base-account/framework-integrations/privy/setup) guide to set up Privy with Base Account.
36+
37+
### Frontend Component (Sign In With Base)
38+
39+
We use the [`SignInWithBaseButton`](/base-account/reference/ui-elements/sign-in-with-base-button) component from the `@base-org/account-ui/react` package to make sure
40+
we are following the brand guidelines.
41+
42+
<CodeGroup>
43+
```tsx Authentication Component (components/sections/authentication.tsx) expandable
44+
"use client";
45+
46+
import { useState } from "react";
47+
import { useBaseAccountSdk } from "@privy-io/react-auth";
48+
import { SignInWithBaseButton } from "@base-org/account-ui/react";
49+
50+
export const Authentication = () => {
51+
const { baseAccountSdk } = useBaseAccountSdk();
52+
const [loading, setLoading] = useState(false);
53+
const [verificationResult, setVerificationResult] = useState<any>(null);
54+
55+
const provider = baseAccountSdk?.getProvider();
56+
57+
const handleSignInWithBase = async () => {
58+
if (!provider) return;
59+
60+
try {
61+
setLoading(true);
62+
63+
// Get a fresh nonce from backend
64+
const nonceResponse = await fetch("/api/auth/nonce");
65+
const { nonce } = await nonceResponse.json();
66+
67+
// Switch to Base Chain
68+
await provider.request({
69+
method: "wallet_switchEthereumChain",
70+
params: [{ chainId: "0x2105" }],
71+
});
72+
73+
// Connect and authenticate with SIWE
74+
const response = (await provider.request({
75+
method: "wallet_connect",
76+
params: [{
77+
version: "1",
78+
capabilities: {
79+
signInWithEthereum: {
80+
nonce,
81+
chainId: "0x2105",
82+
},
83+
},
84+
}],
85+
})) as {
86+
accounts: {
87+
address: string;
88+
capabilities: {
89+
signInWithEthereum: { signature: string; message: string };
90+
};
91+
}[];
92+
};
93+
94+
const { address } = response.accounts[0];
95+
const { message, signature } = response.accounts[0].capabilities.signInWithEthereum;
96+
97+
// Verify with backend
98+
const verifyResponse = await fetch("/api/auth/verify", {
99+
method: "POST",
100+
headers: { "Content-Type": "application/json" },
101+
body: JSON.stringify({ address, message, signature }),
102+
});
103+
104+
const result = await verifyResponse.json();
105+
setVerificationResult(result);
106+
} catch (error) {
107+
console.error("Sign in error:", error);
108+
} finally {
109+
setLoading(false);
110+
}
111+
};
112+
113+
return (
114+
<div>
115+
<SignInWithBaseButton onClick={handleSignInWithBase} />
116+
{verificationResult && (
117+
<div>✅ Backend Verified! Address: {verificationResult.address}</div>
118+
)}
119+
</div>
120+
);
121+
};
122+
123+
export default Authentication;
124+
```
125+
</CodeGroup>
126+
127+
### Using the Authentication Component
128+
129+
Add the Authentication component to your page to enable Sign In with Base functionality:
130+
131+
<CodeGroup>
132+
```tsx Page Implementation (app/page.tsx)
133+
import Authentication from "@/components/sections/authentication";
134+
135+
export default function Home() {
136+
return (
137+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
138+
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm">
139+
<h1 className="text-4xl font-bold text-center mb-8">
140+
Base Account with Privy
141+
</h1>
142+
143+
<div className="flex flex-col items-center space-y-4">
144+
<Authentication />
145+
</div>
146+
</div>
147+
</main>
148+
);
149+
}
150+
```
151+
152+
```tsx Alternative: Protected Page (app/dashboard/page.tsx)
153+
"use client";
154+
155+
import { usePrivy } from "@privy-io/react-auth";
156+
import Authentication from "@/components/sections/authentication";
157+
158+
export default function Dashboard() {
159+
const { authenticated } = usePrivy();
160+
161+
if (!authenticated) {
162+
return (
163+
<div className="flex min-h-screen items-center justify-center">
164+
<div className="text-center">
165+
<h1 className="text-2xl font-bold mb-4">Access Required</h1>
166+
<p className="mb-6">Please authenticate to access the dashboard.</p>
167+
<Authentication />
168+
</div>
169+
</div>
170+
);
171+
}
172+
173+
return (
174+
<div className="min-h-screen p-8">
175+
<h1 className="text-3xl font-bold mb-6">Dashboard</h1>
176+
<p>Welcome to your authenticated dashboard!</p>
177+
{/* Your protected content here */}
178+
</div>
179+
);
180+
}
181+
```
182+
</CodeGroup>
183+
184+
### Backend Implementation
185+
186+
<Warning>
187+
**Development Only**: This backend implementation is not production-ready. The nonce management system needs proper persistence and security enhancements for production use.
188+
</Warning>
189+
190+
<CodeGroup>
191+
```ts Nonce Generation (app/api/auth/nonce/route.ts)
192+
import { NextResponse } from 'next/server';
193+
import crypto from 'crypto';
194+
import { nonceStore } from '@/lib/nonce-store';
195+
196+
export async function GET() {
197+
try {
198+
const nonce = crypto.randomBytes(16).toString('hex');
199+
nonceStore.add(nonce);
200+
201+
return NextResponse.json({ nonce });
202+
} catch (error) {
203+
return NextResponse.json(
204+
{ error: 'Failed to generate nonce' },
205+
{ status: 500 }
206+
);
207+
}
208+
}
209+
```
210+
211+
```ts Signature Verification (app/api/auth/verify/route.ts) expandable
212+
import { NextRequest, NextResponse } from 'next/server';
213+
import { createPublicClient, http } from 'viem';
214+
import { base } from 'viem/chains';
215+
import { nonceStore } from '@/lib/nonce-store';
216+
217+
const client = createPublicClient({
218+
chain: base,
219+
transport: http()
220+
});
221+
222+
export async function POST(request: NextRequest) {
223+
try {
224+
const { address, message, signature } = await request.json();
225+
226+
// Extract nonce from SIWE message
227+
const nonce = message.match(/Nonce: (\w+)/)?.[1];
228+
229+
if (!nonce || !nonceStore.consume(nonce)) {
230+
return NextResponse.json(
231+
{ error: 'Invalid or reused nonce' },
232+
{ status: 400 }
233+
);
234+
}
235+
236+
// Verify signature using viem
237+
const valid = await client.verifyMessage({
238+
address: address as `0x${string}`,
239+
message,
240+
signature: signature as `0x${string}`
241+
});
242+
243+
if (!valid) {
244+
return NextResponse.json(
245+
{ error: 'Invalid signature' },
246+
{ status: 401 }
247+
);
248+
}
249+
250+
return NextResponse.json({
251+
success: true,
252+
address,
253+
timestamp: new Date().toISOString()
254+
});
255+
256+
} catch (error) {
257+
return NextResponse.json(
258+
{ error: 'Internal server error' },
259+
{ status: 500 }
260+
);
261+
}
262+
}
263+
```
264+
265+
```ts Nonce Store (lib/nonce-store.ts) expandable
266+
// Simple in-memory nonce store
267+
// In production, use Redis or a database
268+
class NonceStore {
269+
private nonces = new Set<string>();
270+
271+
add(nonce: string): void {
272+
this.nonces.add(nonce);
273+
}
274+
275+
consume(nonce: string): boolean {
276+
return this.nonces.delete(nonce);
277+
}
278+
}
279+
280+
export const nonceStore = new NonceStore();
281+
```
282+
</CodeGroup>
283+
284+
### Production Considerations
285+
286+
For production deployments, enhance the backend implementation with:
287+
288+
- **Persistent storage**: Use Redis or a database instead of in-memory storage
289+
- **Rate limiting**: Implement request rate limiting for nonce generation
290+
- **Session management**: Create proper JWT tokens or session cookies
291+
- **Nonce expiration**: Add timestamp-based nonce expiration

0 commit comments

Comments
 (0)