OAuth device login for ChatGPT accounts

Let users bring their own ChatGPT to your app.

Server-owned login, encrypted sessions, and Vercel AI SDK streaming on the user's plan — no API key from you, no tokens in the browser.

terminal
bun add @opencoredev/loginwithchatgpt-server \
  @opencoredev/loginwithchatgpt-react @opencoredev/loginwithchatgpt-ai
server.tsthe whole backend
const auth = createChatGPTHandler({
  secret: process.env.LWC_SECRET,
});

Bun.serve({
  routes: {
    "/api/chatgpt/*": (req) => auth.handler(req),
  },
});

One handler owns the whole flow

The device-code flow needs no redirect URL, so it works the same in serverless, containers, and local dev.

  1. 01

    Browser starts login

    The widget shows a consent step, then requests a device code.

    POST /api/chatgpt/login
  2. 02

    User authorizes on OpenAI

    They enter a short code on auth.openai.com — no redirect URL, no localhost listener.

    auth.openai.com/codex/device
  3. 03

    Server stores the tokens

    Polling advances one step per request; tokens are encrypted into your session store.

    GET /api/chatgpt/status
  4. 04

    AI SDK streams through you

    streamText() hits your proxy, which injects credentials and streams back.

    POST /api/chatgpt/responses

The browser never holds a token

What the browser sees

HttpOnly session cookie
an opaque signed id — no tokens, ever
The user code
shown once during device authorization
Public profile
email, name, and plan from the id token
Streamed text
responses relayed by your proxy route

What your server keeps

Access & refresh tokens
AES-GCM encrypted in your session store
Token refresh
automatic, deduplicated, 60s early margin
Rate limits & allowlists
30 req/min per session by default
Origin checks
cross-site requests can't ride the cookie

Signed-in apps can spend the user's plan, so consent is mandatory and guardrails are on by default. Read the security model.

Login with ChatGPT sign-in screen with device code
The default widget: consent, device code, connected state
Streaming playground after sign-in
Streaming through the proxy on the signed-in account