Skip to content

feat(backend): add Poppy browser session helpers - #10702

Draft
thiskevinwang wants to merge 1 commit into
mainfrom
kevin/poppy-v04-browser-session
Draft

thiskevinwang wants to merge 1 commit into
mainfrom
kevin/poppy-v04-browser-session

Conversation

@thiskevinwang

@thiskevinwang thiskevinwang commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Description

Implement support for Poppy v0.4 browser session endpoints. See https://personalagentprotocol.org/docs/0.4/guides#company-browsing

We'll support #3 — Signed-in browsing with the account's full access

  • poppy:browser-sign-in scope allows everything the account can do.

The rationale: given the browser session endpoint will resolve to a clerk user session, we don't want to conflate that end state with OAuth scopes. This decision is 100% fine by the spec 1,

The high-level flow:

stateDiagram-v2
    state "1. Discovery" as Discovery
    state "2. OAuth" as OAuth
    state "3. Poppy browser Session endpoint" as BrowserJoin
    state "4. Clerk ticket issued" as Ticket
    state "5. Redeemed for Clerk session" as ClerkSession

    Discovery --> OAuth
    OAuth --> BrowserJoin
    BrowserJoin --> Ticket
    Ticket --> ClerkSession
Loading

Proposed SDK usage guide — Next.js

Important

Proposed SDK usage...

1. Add the Clerk-defined __experimental_poppy option to proxy.ts, alongside normal route protection. Replace https://example.com with the configured application origin.

import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher(['/account(.*)']);

export default clerkMiddleware(
  async (auth, request) => {
    if (isProtectedRoute(request)) await auth.protect();
  },
  {
    // Clerk preview configuration: serves Poppy discovery and browser session endpoint,
    // and checks current account access on joined-browser requests.
    __experimental_poppy: {
      // The poppy browser session endpoint the application hosts.
      // Poppy-defined  /{J}/authorize & Clerk-defined /{J}/callback path are derived from this URL.
      // where {J} is short for {browser_session_endpoint}
      browserSessionEndpoint: 'https://example.com/poppy/browser-session',
      // Clerk-defined destination after the join (after activation when signed in).
      // Same-origin path or URL; defaults to '/'. 
      redirectTo: '/account',
    },
  },
);

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
};

2. Add the Clerk-defined callback at app/poppy/browser-session/callback/page.tsx.

export { PoppyCallbackPage as default } from '@clerk/nextjs/server';

What is this component? ..So, the flow is

  1. Agent browser visits GET /{J} ({J} = browser session endpoint)
    • html form, code, cookie returned
  2. Agent sends POST /{J}/authorize, application/json
  3. Agent submits form POST /{J}, application/x-www-form-urlencoded
  4. On success, Clerk will set a ticket in cookies, and 303 redirect to /{J}/callback which is where the PoppyCallbackPage is mounted.
    • The PoppyCallbackPage will read the ticket through normal Clerk sign-in, redeem it, and convert to a signed in session.

3. Read normal Clerk authentication in a Server Component or Route Handler.

At this point, agent auth is readable by the application. ✅

import { auth } from '@clerk/nextjs/server';

const { userId, agent } = await auth();

// Clerk-defined experimental field for the verified Poppy-defined Session ID.
// Different from Clerk's sessionId. Undefined without an authenticated Poppy agent.
const protocolSessionId = agent?.__experimental_poppySessionId;
  • userId identifies the Clerk account.
  • Clerk-defined agent is present only for an authenticated, agent-attributed Clerk Session. Only present when actually sign-in.
  • Other Clerk fields:
    • agent.__experimental_poppyClientId ~ The agents CIMD url
    • agent.__experimental_poppyUserId ~ The agent's pseudonymous user id (not a clerk user id)
    • agent.__experimental_poppySessionId ~ The agent's poppy session id

Checklist

  • Backend SDK Node tests and focused edge-runtime tests pass.
  • Backend SDK build and declarations pass.
  • (If applicable) JSDoc comments have been added or updated for package exports.
  • (If applicable) Documentation has been updated.

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Footnotes

  1. Personal agents work faster and more reliably through a company agent or APIs than through a website, so start there for the tasks users ask for most. Then choose how much of your website personal agents can use, from least to most ↩

@vercel

vercel Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
clerk-js-sandbox Ready Ready Preview Nov 10, 2026 12:18am UTC
swingset Ready Ready Preview Nov 10, 2026 12:18am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@changeset-bot

changeset-bot Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 528132a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@clerk/backend Minor
@clerk/nextjs Minor
@clerk/astro Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/hono Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@10702

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@10702

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@10702

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@10702

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@10702

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@10702

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@10702

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@10702

@clerk/expo-biometrics

npm i https://pkg.pr.new/@clerk/expo-biometrics@10702

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@10702

@clerk/expo-native-components

npm i https://pkg.pr.new/@clerk/expo-native-components@10702

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@10702

@clerk/express

npm i https://pkg.pr.new/@clerk/express@10702

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@10702

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@10702

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@10702

@clerk/mosaic

npm i https://pkg.pr.new/@clerk/mosaic@10702

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@10702

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@10702

@clerk/react

npm i https://pkg.pr.new/@clerk/react@10702

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@10702

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@10702

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@10702

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@10702

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@10702

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@10702

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@10702

commit: 528132a

@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 6e672b1 to 0b8dc07 Compare November 6, 2026 21:58
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 0b8dc07 to ac99b0b Compare November 7, 2026 00:32
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from ac99b0b to 12887af Compare November 7, 2026 01:08
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 12887af to 67b7431 Compare November 7, 2026 01:23
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 67b7431 to ebb9b68 Compare November 7, 2026 05:13
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from ebb9b68 to d214318 Compare November 9, 2026 01:41
@github-actions github-actions Bot added the nextjs label Nov 9, 2026
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from d214318 to 170e4c6 Compare November 9, 2026 01:55
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 170e4c6 to 7747bb8 Compare November 9, 2026 03:20
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 7747bb8 to f09fa21 Compare November 9, 2026 03:44
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from f09fa21 to 443d736 Compare November 9, 2026 05:32
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 443d736 to 69e28bb Compare November 9, 2026 13:17
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 69e28bb to 7a75150 Compare November 9, 2026 18:50
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 7a75150 to dcb0243 Compare November 9, 2026 18:51
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from dcb0243 to 126d064 Compare November 9, 2026 19:28
@thiskevinwang
thiskevinwang force-pushed the kevin/poppy-v04-browser-session branch from 126d064 to 0994619 Compare November 9, 2026 21:06
import { PoppyCallback } from '../app-router/client/PoppyCallback';
import { decryptClerkRequestData } from './utils';

export async function PoppyCallbackPage() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

💭 If this is meant to be mounted at an already clerk-derived path, eg. {J}/callback 1, then it feels more appropriate to handle entirely in middleware/proxy.

  • [benefit] 1 less component to mount; 1 less path to fat-finger-wrong.

Footnotes

  1. {J} = {browser_session_endpoint} ↩

@thiskevinwang thiskevinwang Nov 10, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Update: FAPI interaction, primarily ...

signIn.create({ strategy: 'ticket', ticket })

...needs to happen client side.

This branch was successfully deployed

2 active deployments
Preview – swingset — 528132a9 Deployed Nov 10, 2026 by vercel[bot]
Preview – clerk-js-sandbox — 528132a9 Deployed Nov 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant