Add Privy authentication to website example#1
Conversation
Gate the terminal behind Privy login so users must authenticate before interacting. The access token is sent as a Bearer token with every /api/agent request, and the API route returns 401 if the header is missing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR integrates Privy authentication into the example site: adds a Providers component wrapping the app, updates the page to gate UI and supply getAccessToken, threads getAccessToken through Terminal to agent command, and requires/validates Bearer Authorization headers on the agent API endpoint. Changes
Sequence DiagramsequenceDiagram
actor User
participant Page as Page Component
participant Privy as PrivyProvider
participant Terminal as Terminal Component
participant AgentCmd as Agent Command
participant API as /api/agent
User->>Page: Access app
Page->>Privy: Check ready & authenticated
alt Not authenticated
Page->>User: Show login prompt
User->>Privy: Click login
Privy->>Privy: Authenticate user
end
Privy-->>Page: Authenticated (getAccessToken available)
Page->>Terminal: Render with getAccessToken
User->>Terminal: Submit command
Terminal->>AgentCmd: Execute with getAccessToken
AgentCmd->>Privy: Call getAccessToken()
Privy-->>AgentCmd: Return token
AgentCmd->>API: POST /api/agent with Authorization: Bearer token
alt Valid token
API->>API: Validate header, process request
API-->>AgentCmd: Response
else Invalid/missing token
API-->>AgentCmd: 401 Unauthorized
AgentCmd->>Terminal: Revert message, show error
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
examples/website/app/components/Terminal.tsx (1)
40-117: 🛠️ Refactor suggestion | 🟠 Major
useEffectdepending ongetAccessTokenrisks tearing down the entire terminal on re-renders.The effect creates a
LiteTerminal,Bashinstance, wires up commands, shows the welcome message, and sets up input handling — all heavy one-time initialization. IfgetAccessToken's reference changes (e.g., on a Privy state transition or parent re-render), everything is destroyed and recreated, causing visible terminal flicker and loss of user state.Consider storing
getAccessTokenin a ref so the effect can remain mount-only ([]deps) while always calling the latest function:Proposed fix using a ref
+import { useEffect, useRef, useCallback } from "react"; + export default function TerminalComponent({ getAccessToken, }: { getAccessToken: () => Promise<string | null>; }) { const terminalRef = useRef<HTMLDivElement>(null); + const getAccessTokenRef = useRef(getAccessToken); + getAccessTokenRef.current = getAccessToken; useEffect(() => { // ... existing setup code ... - const agentCmd = createAgentCommand(term, getAccessToken); + const agentCmd = createAgentCommand(term, () => getAccessTokenRef.current()); // ... rest of setup ... - }, [getAccessToken]); + }, []);
🤖 Fix all issues with AI agents
In `@examples/website/app/api/agent/route.ts`:
- Around line 69-75: The current auth gate only checks authHeader startsWith
"Bearer " but never validates the token; update the route handler to import and
call verifyAuthToken (from `@privy-io/server-auth`), extract the raw token by
stripping the "Bearer " prefix from authHeader, await verifyAuthToken(token),
and on any verification error or falsy result return Response.json({ error:
"Unauthorized" }, { status: 401 }); otherwise continue request handling and use
the returned claims as needed; ensure the handler is async if it isn’t already
and handle exceptions from verifyAuthToken to return 401.
In `@examples/website/app/providers.tsx`:
- Around line 4-9: Providers currently passes
process.env.NEXT_PUBLIC_PRIVY_APP_ID! into PrivyProvider which uses a TypeScript
non-null assertion but can still be undefined at runtime; update Providers to
read process.env.NEXT_PUBLIC_PRIVY_APP_ID into a local const, validate it (if
falsy) and throw a clear Error (or return an error UI) before rendering
PrivyProvider so PrivyProvider never receives undefined, and remove the non-null
assertion usage; reference Providers, PrivyProvider, and
process.env.NEXT_PUBLIC_PRIVY_APP_ID when making the change.
| const authHeader = req.headers.get("Authorization"); | ||
| if (!authHeader?.startsWith("Bearer ")) { | ||
| return Response.json( | ||
| { error: "Unauthorized" }, | ||
| { status: 401 }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd examples/website && find . -name "route.ts" -type f | head -5Repository: Recoupable-com/Recoup-Bash
Length of output: 146
🏁 Script executed:
cat -n examples/website/app/api/agent/route.ts | head -100Repository: Recoupable-com/Recoup-Bash
Length of output: 4183
🌐 Web query:
Privy @privy-io/server-auth verifyAuthToken API
💡 Result:
In @privy-io/server-auth, you verify a Privy access token (JWT) with:
const verifiedClaims = await privy.utils().auth().verifyAuthToken(authToken);- Input:
authToken: string(the user’s access token, typically fromAuthorization: Bearer <token>). [1] - Returns (on success): an object with:
appId,userId(user’s Privy DID),issuer(alwaysprivy.io),issuedAt,expiration,sessionId. [1]
- Throws (on failure): if the token is expired/invalid or for a different app. [1]
- By default it fetches your app’s verification key from Privy; you can avoid that call by initializing the client with
jwtVerificationKeycopied from the Privy Dashboard. [1]
Notes:
@privy-io/server-authis deprecated; Privy recommends@privy-io/nodefor new work, but the token-verification flow is the same conceptually. [2]
Sources: Privy token verification guide (includes verifyAuthToken example + return fields) [1]; Privy @privy-io/server-auth deprecation note [2].
Bearer token is never verified server-side — any arbitrary string bypasses this check.
The route only checks that an Authorization header starting with "Bearer " exists, but never validates the token itself. Any caller can send Authorization: Bearer fake and access the endpoint. Privy's @privy-io/server-auth provides verifyAuthToken(token) to validate the token server-side and extract claims; without it, this auth gate provides no real protection.
🤖 Prompt for AI Agents
In `@examples/website/app/api/agent/route.ts` around lines 69 - 75, The current
auth gate only checks authHeader startsWith "Bearer " but never validates the
token; update the route handler to import and call verifyAuthToken (from
`@privy-io/server-auth`), extract the raw token by stripping the "Bearer " prefix
from authHeader, await verifyAuthToken(token), and on any verification error or
falsy result return Response.json({ error: "Unauthorized" }, { status: 401 });
otherwise continue request handling and use the returned claims as needed;
ensure the handler is async if it isn’t already and handle exceptions from
verifyAuthToken to return 401.
| export default function Providers({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}> | ||
| {children} | ||
| </PrivyProvider> | ||
| ); |
There was a problem hiding this comment.
Missing env var will silently pass undefined as appId.
The non-null assertion (!) only silences TypeScript; if NEXT_PUBLIC_PRIVY_APP_ID is unset at runtime, PrivyProvider receives undefined. Consider adding a guard or throwing a descriptive error early.
Proposed fix
+const PRIVY_APP_ID = process.env.NEXT_PUBLIC_PRIVY_APP_ID;
+if (!PRIVY_APP_ID) {
+ throw new Error("NEXT_PUBLIC_PRIVY_APP_ID environment variable is required");
+}
+
export default function Providers({ children }: { children: React.ReactNode }) {
return (
- <PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}>
+ <PrivyProvider appId={PRIVY_APP_ID}>
{children}
</PrivyProvider>
);
}🤖 Prompt for AI Agents
In `@examples/website/app/providers.tsx` around lines 4 - 9, Providers currently
passes process.env.NEXT_PUBLIC_PRIVY_APP_ID! into PrivyProvider which uses a
TypeScript non-null assertion but can still be undefined at runtime; update
Providers to read process.env.NEXT_PUBLIC_PRIVY_APP_ID into a local const,
validate it (if falsy) and throw a clear Error (or return an error UI) before
rendering PrivyProvider so PrivyProvider never receives undefined, and remove
the non-null assertion usage; reference Providers, PrivyProvider, and
process.env.NEXT_PUBLIC_PRIVY_APP_ID when making the change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gate the terminal behind Privy login so users must authenticate before interacting. The access token is sent as a Bearer token with every /api/agent request, and the API route returns 401 if the header is missing.
Summary by CodeRabbit