---
**Navigation:** [Introduction](#loginwithchatgpt) | [Quickstart](#quickstart-nextjs-app-router) | [API](#react-api) | [Playground](#official-examples) | [GitHub](https://github.com/weknowyourgame/loginwithchatgpt) | [npm](https://www.npmjs.com/package/loginwithchatgpt)
---
A drop-in "Login with ChatGPT" button that lets your app's users power its AI features with their own ChatGPT subscription. The user signs in with their ChatGPT account, and your AI calls bill against their Plus/Pro plan instead of an API key you pay for.
It uses OpenAI's Codex OAuth flow (PKCE against auth.openai.com) - the same mechanism the
Codex CLI uses. One package, three entry points: a headless engine, a React component, and
Next.js route handlers.
This is strictly "let your users bring their own ChatGPT subscription into your app." Each user logs in with their own account, on their own machine, and spends their own subscription. It is built for local-first contexts desktop apps, Electron, CLIs, and local-first Next.js dev tools where the loopback OAuth redirect lands on the user's own machine.
It is not a way to pool or resell subscriptions through a central server. That is account-sharing and will get the access revoked.
bun add loginwithchatgptreact is an optional peer dependency - you only need it if you use the React entry point.
Two files. First, mount the route handlers under a catch-all route:
// app/api/chatgpt/[...lwc]/route.ts
import { createHandlers } from "loginwithchatgpt/next";
export const runtime = "nodejs";
export const { GET, POST } = createHandlers();Then drop the button anywhere in your UI:
"use client";
import { LoginWithChatGPT } from "loginwithchatgpt/react";
export default function Page() {
return <LoginWithChatGPT onConnected={(s) => console.log(s.account.email)} />;
}Once a user is connected, make AI calls that bill their subscription from any server route:
// app/api/chat/route.ts
import { createClient } from "loginwithchatgpt";
export const runtime = "nodejs";
export async function POST(req: Request) {
const { prompt } = await req.json();
const text = await createClient().respond(prompt);
return Response.json({ text });
}import { LoginWithChatGPT, useChatGPTAuth } from "loginwithchatgpt/react";<LoginWithChatGPT />
| Prop | Type | Description |
|---|---|---|
onConnected |
(session) => void |
Called once when the user connects. |
basePath |
string |
Route handler base path. Defaults to /api/chatgpt. |
className |
string |
Applied to the button. |
useChatGPTAuth(options?) returns { status, account, plan, error, login, logout }, where
status is one of idle, connecting, connected, or error. Use it directly if you want
to build your own button.
import { login, logout, getSession, refresh, createClient } from "loginwithchatgpt";| Function | Description |
|---|---|
login(options?) |
Runs the loopback OAuth flow and stores tokens. Returns the session. |
startLogin(options?) |
Headless variant for SSH/containers/CI. Returns { url, complete }. |
startDeviceLogin(options?) |
Device-code flow for web/headless. Returns { userCode, verificationUrl, wait }. |
logout(store?) |
Clears stored tokens. |
getSession(store?) |
Returns the current session, or null. |
refresh(store?) |
Forces a token refresh. |
createClient(store?) |
Returns a client with respond(prompt) and stream(prompt). |
The client auto-refreshes tokens before expiry and retries once on a 401, so callers never handle tokens directly.
<LoginWithChatGPT /> -> /api/chatgpt/* -> createHandlers (next) -> core engine
browser (UI) HTTP server (Node) PKCE + loopback
- The button calls the route handler, which runs
login()on the server. login()generates a PKCE pair, opensauth.openai.com, and starts a one-shot loopback server on127.0.0.1:1455to catch the redirect.- The returned code is exchanged for tokens, which are stored encrypted.
createClient()attaches the access token to requests and refreshes it as needed.
Tokens are stored encrypted at rest (AES-256-GCM). The encryption key is kept in the macOS
Keychain where available, with a 0600 key-file fallback on other platforms. Storage is
pluggable through the TokenStore interface, so you can supply your own backend:
import { createClient, type TokenStore } from "loginwithchatgpt";
const myStore: TokenStore = { load, save, clear };
const client = createClient(myStore);The default is encryptedFileStore; a plaintext fileStore is also exported for debugging.
| Entry | Runtime | Contents |
|---|---|---|
loginwithchatgpt |
Node | engine: login, getSession, createClient, stores |
loginwithchatgpt/react |
Browser | LoginWithChatGPT, useChatGPTAuth |
loginwithchatgpt/next |
Node | createHandlers for App Router |
A runnable Next.js demo lives in examples/web:
cd examples/web
bun install
bun run devThere are three ways to authenticate, matching different environments:
| Flow | Function | When to use |
|---|---|---|
| Loopback | login() |
Desktop apps, Electron, local CLIs — auto-opens the browser and catches the redirect on localhost:1455. Nothing to copy-paste. |
| Device code | startDeviceLogin() |
Web servers, Next.js, Docker, remote machines — shows a short code the user enters at an OpenAI page. Requires enabling ChatGPT → Settings → Security & Login → Allow device code login. |
| Headless paste | startLogin() |
SSH sessions, CI pipelines — prints a URL for the user to open in any browser, then waits for them to paste the redirect URL back. Pure fallback. |
The repo includes a small CLI that exercises the engine directly:
bun run login # loopback flow — auto-opens browser (use this on your Mac)
bun run login --device # device-code flow — shows a short code, works anywhere
bun run login --headless # headless paste — for SSH/containers/CI with no browser
bun run whoami # show the connected account and plan
bun run call # one AI call billed to the subscription
bun run stream # the same call, streamed
bun run refresh # force a token refresh
bun run logout # clear stored tokensThese are the models supported by the ChatGPT subscription backend (chatgpt.com/backend-api/codex/responses). These are different from the direct OpenAI API models — they only work with a ChatGPT subscription (Plus/Pro), not an API key.
| Model | Description | Plan |
|---|---|---|
gpt-5.5 |
Newest frontier model. Best for complex coding, computer use, and research. (default) | Plus / Pro |
gpt-5.4 |
Flagship model with strong coding, reasoning, and tool use. | Plus / Pro |
gpt-5.4-mini |
Faster, lighter option for responsive tasks and subagents. | Plus / Pro |
gpt-5.3-codex-spark |
Near-instant real-time coding iteration. Research preview. | Pro only |
Pass a model option to override the default:
// Core / Next.js
const text = await createClient().respond("Refactor this", { model: "gpt-5.4-mini" });
// Streaming
for await (const delta of createClient().stream("Write tests", { model: "gpt-5.4" })) {
process.stdout.write(delta);
}The RespondOptions type exported from the package accepts model, instructions, and signal.
Note:
codex-mini-latestand othero4-mini-based names only work with a direct OpenAI API key — they are not valid for the ChatGPT subscription backend.
The auth flow (login, token storage, refresh) is implemented and working. The AI call path
targets the Codex /responses endpoint and requires an account with Codex access
(ChatGPT Plus or Pro); free accounts can authenticate but cannot make billed calls.
The client config (client id, endpoints) tracks OpenAI's public Codex client and may need updating if OpenAI rotates it; it is isolated in a single file.
Multiple working examples for different environments and frameworks:
- examples/web - Next.js App Router example with interactive playground (official)
github.com/weknowyourgame/loginwithchatgpt
loginwithchatgpt/
src/ # SDK source code
dist/ # Compiled output
examples/
web/ # Original Next.js example
qa/ # 9 QA testing examples
docs/
qa/ # QA testing documentation
website/ # Documentation site
[other files]
MIT