Skip to content

Add Privy authentication to website example#1

Merged
sweetmantech merged 2 commits intomainfrom
claude/privy
Feb 9, 2026
Merged

Add Privy authentication to website example#1
sweetmantech merged 2 commits intomainfrom
claude/privy

Conversation

@sweetmantech
Copy link
Copy Markdown

@sweetmantech sweetmantech commented Feb 9, 2026

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

  • New Features
    • Added sign-in flow and access-token support for authenticated sessions.
    • Terminal now requests an access token before running commands and prompts unauthenticated users to log in.
    • App layout now wraps content with an authentication provider to enable login and session handling.

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>
@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
just-bash Ready Ready Preview Feb 9, 2026 0:23am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Authentication Setup
examples/website/app/providers.tsx, examples/website/app/layout.tsx, examples/website/package.json
Added a client-side Providers component using PrivyProvider, wrapped root layout with Providers, and added @privy-io/react-auth dependency.
Page Authentication Logic
examples/website/app/page.tsx
Integrated usePrivy (ready, authenticated, login, getAccessToken); added loading/auth guards and a login prompt; passes getAccessToken into TerminalComponent.
Terminal Components
examples/website/app/components/Terminal.tsx, examples/website/app/components/terminal-parts/agent-command.ts
TerminalComponent now accepts getAccessToken prop and forwards it to createAgentCommand. createAgentCommand fetches token, errors and reverts on missing token, and includes Authorization: Bearer <token> header when calling /api/agent.
API Authorization
examples/website/app/api/agent/route.ts
Added early Authorization header validation in POST handler; returns 401 Unauthorized when header is missing or not Bearer before reading the body.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble keys and fetch a token bright,
I hop through Privy, guard the night,
Bearer header snug and neat,
Commands sent safely, none to cheat—
Hooray, the rabbit says: secure delight! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main objective: adding Privy authentication to the website example. This is reflected consistently across all changed files (new providers.tsx with PrivyProvider, page.tsx with usePrivy hooks, authorization checks in route.ts, and token passing through components).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/privy

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

useEffect depending on getAccessToken risks tearing down the entire terminal on re-renders.

The effect creates a LiteTerminal, Bash instance, wires up commands, shows the welcome message, and sets up input handling — all heavy one-time initialization. If getAccessToken'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 getAccessToken in 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.

Comment on lines +69 to +75
const authHeader = req.headers.get("Authorization");
if (!authHeader?.startsWith("Bearer ")) {
return Response.json(
{ error: "Unauthorized" },
{ status: 401 },
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd examples/website && find . -name "route.ts" -type f | head -5

Repository: Recoupable-com/Recoup-Bash

Length of output: 146


🏁 Script executed:

cat -n examples/website/app/api/agent/route.ts | head -100

Repository: 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 from Authorization: Bearer <token>). [1]
  • Returns (on success): an object with:
    • appId, userId (user’s Privy DID), issuer (always privy.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 jwtVerificationKey copied from the Privy Dashboard. [1]

Notes:

  • @privy-io/server-auth is deprecated; Privy recommends @privy-io/node for 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.

Comment on lines +4 to +9
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}>
{children}
</PrivyProvider>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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>
@sweetmantech sweetmantech merged commit 859ead4 into main Feb 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant