[metadata]
apple-mobile-web-app-title: OpenRouter | Documentation
application-name: OpenRouter | Documentation
description: Connect your users to OpenRouter
generator: Mintlify
msapplication-TileColor: #7624F4
msapplication-config: /docs/_mintlify/favicons/openrouter-d02e98a0/EmhfYwFO2tvnkuV-/_generated/favicon/browserconfig.xml
og:description: Implement secure user authentication with OpenRouter using OAuth PKCE. Complete guide to setting up and managing OAuth authentication flows.
og:image: https://openrouter.ai/dynamic-og?pathname=use-cases/oauth-pkce&title=OAuth%20PKCE&description=Secure%20one-click%20authentication%20for%20your%20OpenRouter%20users
og:image:height: 630
og:image:width: 1200
og:site_name: OpenRouter Documentation
og:title: OAuth PKCE - Secure User Authentication
og:type: website
og:url: https://openrouter.ai/docs/guides/overview/auth/oauth
twitter:card: summary_large_image
twitter:description: Implement secure user authentication with OpenRouter using OAuth PKCE. Complete guide to setting up and managing OAuth authentication flows.
twitter:image: https://openrouter.ai/dynamic-og?pathname=use-cases/oauth-pkce&title=OAuth%20PKCE&description=Secure%20one-click%20authentication%20for%20your%20OpenRouter%20users
twitter:image:height: 630
twitter:image:width: 1200
twitter:title: OAuth PKCE - Secure User Authentication
viewport: width=device-width, initial-scale=1, viewport-fit=cover

[canonical-links]
https://openrouter.ai/docs/guides/overview/auth/oauth

[document-links]
/docs/llms.txt: /docs/llms.txt
API Reference: /docs/api_reference/overview
Agent SDK: /docs/agent-sdk/overview
Anthropic Agent SDK: /docs/guides/community/anthropic-agent-sdk
App Attribution: /docs/app-attribution
Apps: https://openrouter.ai/apps
Arize AX: /docs/guides/community/arize
Auto Exacto: /docs/guides/routing/auto-exacto
Awesome OpenRouter: /docs/guides/community/awesome-openrouter
BYOK: /docs/guides/overview/auth/byok
Batch Beta: /docs/batch-quickstart
Benchmarks: https://openrouter.ai/benchmarks
Chat: https://openrouter.ai/chat
Client SDKs: /docs/client-sdks/overview
Cookbook: /docs/cookbook/get-started/quickstart
Custom Classifiers: /docs/guides/features/classifiers
Data Collection: /docs/guides/privacy/data-collection
Docs: /docs/quickstart
Docs: https://openrouter.ai/docs
Effect AI SDK: /docs/guides/community/effect-ai-sdk
FAQ: /docs/faq
For Providers: /docs/guides/community/for-providers
Frameworks and Integrations Overview: /docs/guides/community/frameworks-and-integrations-overview
Infisical: /docs/guides/community/infisical
Input & Output Logging: /docs/guides/features/input-output-logging
LangChain: /docs/guides/community/langchain
Langfuse: /docs/guides/community/langfuse
Latency and Performance: /docs/guides/best-practices/latency-and-performance
LiveKit: /docs/guides/community/livekit
MCP: /docs/guides/overview/mcp-server
Management API Keys: /docs/guides/overview/auth/management-api-keys
Mastra: /docs/guides/community/mastra
Message Transforms: /docs/guides/features/message-transforms
Model Fallbacks: /docs/guides/routing/model-fallbacks
Models: /docs/guides/overview/models
Models: https://openrouter.ai/models
OAuth: /docs/guides/overview/auth/oauth
Online PKCE Generator: https://tonyxu-io.github.io/pkce-generator/
OpenAI SDK: /docs/guides/community/openai-sdk
OpenRouter | Documentation home page: https://openrouter.ai
Ori Eval: /docs/guides/ori/eval
Ori Harness: /docs/guides/ori/harness
PKCE Tools: https://example-app.com/pkce
Presets: /docs/guides/features/presets
Principles: /docs/guides/overview/principles
Private Models: /docs/guides/routing/private-models
Prompt Caching: /docs/guides/best-practices/prompt-caching
Proof Key for Code Exchange (PKCE): https://oauth.net/2/pkce/
Provider Logging: /docs/guides/privacy/provider-logging
Provider Selection: /docs/guides/routing/provider-selection
PydanticAI: /docs/guides/community/pydantic-ai
Quickstart: /docs/quickstart
Rankings: https://openrouter.ai/rankings
Reasoning Tokens: /docs/guides/best-practices/reasoning-tokens
Render: /docs/guides/community/render
Replit: /docs/guides/community/replit
Report Feedback: /docs/guides/overview/report-feedback
Response Caching: /docs/guides/features/response-caching
Router Metadata: /docs/guides/features/router-metadata
SCIM Group Mappings: /docs/guides/features/scim-mappings
Service Tiers: /docs/guides/features/service-tiers
Single Sign-On (SSO): /docs/guides/features/sso
Sovereign AI: /docs/guides/features/sovereign-ai
Stripe Projects: /docs/guides/overview/stripe-projects
Structured Outputs: /docs/guides/features/structured-outputs
Switching Workspaces: /docs/guides/features/workspaces/switching
TanStack AI: /docs/guides/community/tanstack-ai
Tool Calling: /docs/guides/features/tool-calling
Uptime Optimization: /docs/guides/best-practices/uptime-optimization
Vercel AI SDK: /docs/guides/community/vercel-ai-sdk
Video Understanding: /docs/guides/overview/multimodal/videos
Where Ori writes files: /docs/guides/ori/files
Workspace Budgets: /docs/guides/features/workspaces/workspace-budgets
Workspaces: /docs/guides/features/workspaces
Xcode: /docs/guides/community/xcode
ZDR: /docs/guides/features/zdr
Zapier: /docs/guides/community/zapier
Zero Completion Insurance: /docs/guides/features/zero-completion-insurance
make OpenRouter requests: /docs/api_reference/overview
visit Auth0’s docs: https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-authorization-code-flow-with-pkce#parameters

[structured-data]
{"@context":"https://schema.org","@graph":[{"@id":"https://openrouter.ai/#organization","@type":"Organization","logo":{"@type":"ImageObject","url":"https://mintcdn.com/openrouter-d02e98a0/ksNSeB_K7gD-BUDh/assets/logo-v2-light.svg?fit=max&auto=format&n=ksNSeB_K7gD-BUDh&q=85&s=690e45873843519efe02bcbe45795dac"},"name":"OpenRouter | Documentation","url":"https://openrouter.ai"},{"@id":"https://openrouter.ai/docs#website","@type":"WebSite","name":"OpenRouter | Documentation","publisher":{"@id":"https://openrouter.ai/#organization"},"url":"https://openrouter.ai/docs"},{"@id":"https://openrouter.ai/docs/guides/overview/auth/oauth#webpage","@type":"WebPage","breadcrumb":{"@id":"https://openrouter.ai/docs/guides/overview/auth/oauth#breadcrumb"},"dateModified":"2026-08-08T19:05:56.363Z","description":"Connect your users to OpenRouter","isPartOf":{"@id":"https://openrouter.ai/docs#website"},"name":"OAuth PKCE","url":"https://openrouter.ai/docs/guides/overview/auth/oauth"},{"@id":"https://openrouter.ai/docs/guides/overview/auth/oauth#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","item":"https://openrouter.ai/docs/quickstart","name":"Overview","position":1},{"@type":"ListItem","item":"https://openrouter.ai/docs/guides/overview/auth/oauth","name":"OAuth PKCE","position":2}]},{"@id":"https://openrouter.ai/docs/guides/overview/auth/oauth#article","@type":["Article","TechArticle"],"dateModified":"2026-08-08T19:05:56.363Z","description":"Connect your users to OpenRouter","headline":"OAuth PKCE","image":"https://openrouter.ai/dynamic-og?pathname=use-cases/oauth-pkce&title=OAuth%20PKCE&description=Secure%20one-click%20authentication%20for%20your%20OpenRouter%20users","isPartOf":{"@id":"https://openrouter.ai/docs#website"},"mainEntityOfPage":{"@id":"https://openrouter.ai/docs/guides/overview/auth/oauth#webpage"},"name":"OAuth PKCE","publisher":{"@id":"https://openrouter.ai/#organization"},"url":"https://openrouter.ai/docs/guides/overview/auth/oauth"}]}

[content]
OAuth PKCE - Secure User Authentication
Documentation Index
Fetch the complete documentation index at:
/docs/llms.txt
Use this file to discover all available pages before exploring further.
Skip to main content
OpenRouter | Documentation
home page
Search...
⌘
K
Ask Assistant
Models
Benchmarks
Chat
Rankings
Apps
Docs
Search...
Navigation
Authentication
OAuth PKCE
Docs
API Reference
Client SDKs
Agent SDK
Cookbook
Overview
Quickstart
Batch
Beta
Principles
Models
MCP
Multimodal
Authentication
OAuth
Management API Keys
BYOK
Stripe Projects
FAQ
Report Feedback
Models & Routing
Model Fallbacks
Provider Selection
Auto Exacto
Private Models
Model Variants
Routers
Features
Workspaces
Workspace Budgets
Switching Workspaces
Single Sign-On (SSO)
SCIM Group Mappings
Presets
Custom Classifiers
Response Caching
Tool Calling
Server Tools
Plugins
Structured Outputs
Message Transforms
Zero Completion Insurance
ZDR
App Attribution
Guardrails
Service Tiers
Sovereign AI
Router Metadata
Input & Output Logging
Broadcast
Ori
Ori Eval
Ori Harness
Where Ori writes files
Privacy
Data Collection
Provider Logging
Best Practices
Latency and Performance
Prompt Caching
Uptime Optimization
Reasoning Tokens
Community
For Providers
Frameworks and Integrations Overview
Awesome OpenRouter
Effect AI SDK
Arize AX
LangChain
LiveKit
Langfuse
Mastra
OpenAI SDK
Anthropic Agent SDK
PydanticAI
Render
Replit
TanStack AI
Vercel AI SDK
Xcode
Zapier
Infisical
On this page
PKCE Guide
Step 1: Send your user to OpenRouter
How to Generate a Code Challenge
Localhost Apps
Headless Apps (SSH Servers, Containers)
Step 2: Exchange the code for a user-controlled API key
Deep-link to the user’s key
Step 3: Use the API key
Error Codes
External Tools
Authentication
OAuth PKCE
Copy page
Copy page
Connect your users to OpenRouter
Copy page
Copy page
Users can connect to OpenRouter in one click using
Proof Key for Code Exchange (PKCE)
.
Here’s a step-by-step guide:
​
PKCE Guide
​
Step 1: Send your user to OpenRouter
To start the PKCE flow, send your user to OpenRouter’s
/auth
URL with a
callback_url
parameter pointing back to your site:
With S256 Code Challenge (Recommended)
With Plain Code Challenge
Without Code Challenge
https://openrouter.ai/auth?callback_url=<YOUR_SITE_URL>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256
https://openrouter.ai/auth?callback_url=<YOUR_SITE_URL>&code_challenge=<CODE_CHALLENGE>&code_challenge_method=plain
https://openrouter.ai/auth?callback_url=<YOUR_SITE_URL>
The
code_challenge
parameter is optional but recommended.
Your user will be prompted to log in to OpenRouter and authorize your app. After authorization, they will be redirected back to your site with a
code
parameter in the URL:
Use SHA-256 for Maximum Security
For maximum security, set
code_challenge_method
to
S256
, and set
code_challenge
to the base64 encoding of the sha256 hash of
code_verifier
.
For more info,
visit Auth0’s docs
.
​
How to Generate a Code Challenge
The following example uses the Web Crypto API and the Buffer API to generate a code challenge for the S256 method. You will need a bundler to use the Buffer API in the web browser:
Generate Code Challenge
import
{
Buffer
}
from
'buffer'
;
async
function
createSHA256CodeChallenge
(
input
:
string
) {
const
encoder
=
new
TextEncoder
();
const
data
=
encoder
.
encode
(
input
);
const
hash
=
await
crypto
.
subtle
.
digest
(
'SHA-256'
,
data
);
return
Buffer
.
from
(
hash
).
toString
(
'base64url'
);
}
const
codeVerifier
=
'your-random-string'
;
const
generatedCodeChallenge
=
await
createSHA256CodeChallenge
(
codeVerifier
);
​
Localhost Apps
Localhost callbacks are supported on
any port
. This is useful for CLI tools and local-first apps that bind to an arbitrary free OS port for the OAuth callback (e.g.
http://localhost:51423/callback
).
Localhost apps are assigned a fixed title matching the host and port (e.g.
localhost:3000
) but will not appear in the OpenRouter marketplace or rankings. If you want a custom app name and marketplace presence, use a public URL as the callback instead.
When moving to production, replace the localhost callback URL with a public URL (your project website or a GitHub repo link) to get full app attribution.
​
Headless Apps (SSH Servers, Containers)
If your app runs where a localhost callback can’t be reached (an SSH session, a remote dev box, a container), omit
callback_url
entirely:
Headless (No Callback)
https://openrouter.ai/auth?code_challenge=<CODE_CHALLENGE>&code_challenge_method=S256&key_label=<YOUR_APP_NAME>
After the user authorizes, the page displays the authorization code on screen instead of redirecting. The user copies it and pastes it into your app (e.g. at a terminal prompt), and you exchange it in Step 2 exactly as usual.
A
code_challenge
is
required
in this mode: because the code is displayed on screen, PKCE ensures it is useless to anyone without your app’s
code_verifier
. The code is single-use and expires after 10 minutes.
​
Step 2: Exchange the code for a user-controlled API key
After the user logs in with OpenRouter, they are redirected back to your site with a
code
parameter in the URL:
Extract this code using the browser API:
Extract Code
const
urlParams
=
new
URLSearchParams
(
window
.
location
.
search
);
const
code
=
urlParams
.
get
(
'code'
);
Then use it to make an API call to
https://openrouter.ai/api/v1/auth/keys
to exchange the code for a user-controlled API key:
Exchange Code
const
response
=
await
fetch
(
'https://openrouter.ai/api/v1/auth/keys'
, {
method:
'POST'
,
headers:
{
'Content-Type'
:
'application/json'
,
},
body:
JSON
.
stringify
({
code:
'<CODE_FROM_QUERY_PARAM>'
,
code_verifier:
'<CODE_VERIFIER>'
,
// If code_challenge was used
code_challenge_method:
'<CODE_CHALLENGE_METHOD>'
,
// If code_challenge was used
}),
});
const
{
key
}
=
await
response
.
json
();
​
Deep-link to the user’s key
Once you have the API key, you can create links to the user’s OpenRouter activity and key settings pages by hashing the key with SHA-256. Use the lowercase hexadecimal digest in both URLs:
Create Key Links
async
function
sha256Hex
(
value
:
string
) {
const
data
=
new
TextEncoder
().
encode
(
value
);
const
hash
=
await
crypto
.
subtle
.
digest
(
'SHA-256'
,
data
);
return
Array
.
from
(
new
Uint8Array
(
hash
), (
byte
)
=>
byte
.
toString
(
16
).
padStart
(
2
,
'0'
),
).
join
(
''
);
}
const
keyHash
=
await
sha256Hex
(
key
);
const
logsUrl
=
`https://openrouter.ai/logs?api_key_hash=
${
keyHash
}
`
;
const
settingsUrl
=
`https://openrouter.ai/keys/
${
keyHash
}
`
;
The links only work for the signed-in owner of the API key. If the hash does not resolve for the viewer, the page returns a
404
rather than showing unfiltered data.
And that’s it for the PKCE flow!
​
Step 3: Use the API key
Store the API key securely within the user’s browser or in your own database, and use it to
make OpenRouter requests
.
TypeScript SDK
TypeScript (fetch)
import
{
OpenRouter
}
from
'@openrouter/sdk'
;
const
openRouter
=
new
OpenRouter
({
apiKey:
key
,
// The key from Step 2
});
const
completion
=
await
openRouter
.
chat
.
send
({
model:
'~openai/gpt-latest'
,
messages:
[
{
role:
'user'
,
content:
'Hello!'
,
},
],
stream:
false
,
});
console
.
log
(
completion
.
choices
[
0
].
message
);
fetch
(
'https://openrouter.ai/api/v1/chat/completions'
, {
method:
'POST'
,
headers:
{
Authorization:
`Bearer
${
key
}
`
,
'Content-Type'
:
'application/json'
,
},
body:
JSON
.
stringify
({
model:
'~openai/gpt-latest'
,
messages:
[
{
role:
'user'
,
content:
'Hello!'
,
},
],
}),
});
​
Error Codes
400 Invalid code_challenge_method
: Make sure you’re using the same code challenge method in step 1 as in step 2.
403 Invalid code or code_verifier
: Make sure your user is logged in to OpenRouter, and that
code_verifier
and
code_challenge_method
are correct.
403 Authorization code expired
: Authorization codes expire 10 minutes after issuance. Restart the OAuth flow and exchange the new code promptly.
405 Method Not Allowed
: Make sure you’re using
POST
and
HTTPS
for your request.
​
External Tools
PKCE Tools
Online PKCE Generator
Video Understanding
Management API Keys
⌘
I
Assistant
Responses are generated using AI and may contain mistakes.
