OAuth PKCE: let users connect your app
Let an app get a key from a signed-in member.
Your app can get a RouterPlus inference key for a user. The user does not copy a key. The user signs in, selects a workspace and approves. Then your app receives the key.
The flow is OAuth 2.0 with PKCE (RFC 7636). It uses the common request and response shapes for PKCE key exchange. To use an existing PKCE client, change its two URLs.
Steps
- 01Make a code verifier: 43 to 128 random characters from
A-Z a-z 0-9 - . _ ~. Keep it in your app. - 02Make the code challenge:
base64url(sha256(verifier)), with no padding. - 03Send the user's browser to the consent page:
https://demo.routerplus.com/auth?callback_url=https://yourapp.example.com/callback&code_challenge=CHALLENGE&code_challenge_method=S256- 04The user approves. The browser goes to your
callback_url, with?code=CODEadded. - 05From your app, exchange the code for a key:
curl -s https://dev2-api.routerplus.com/api/v1/auth/keys \
-H "Content-Type: application/json" \
-d '{"code":"CODE","code_verifier":"VERIFIER","code_challenge_method":"S256"}'{"key": "tm_vk_...", "user_id": "[email protected]"}- 06Send the key as a Bearer token on inference calls:
Authorization: Bearer tm_vk_....
Consent page parameters
| Parameter | Required | Meaning |
|---|---|---|
callback_url | yes | Where the browser returns with the code. https only, except plain http on localhost and 127.0.0.1. No user name, password or fragment. Its own query parameters are kept. |
code_challenge | recommended | The PKCE challenge. Without it, the code alone is enough to get the key. |
code_challenge_method | no | S256 (recommended) or plain. Default plain. |
key_label | no | The key's name on the user's API keys page, up to 80 characters. Default: your callback host. |
limit | no | A spending limit for the key in USD, for example 25. The user can change it before approval. |
The user sees your callback host, the workspace list and the limit. Owners and admins can approve. Viewers cannot approve. A user who is not signed in signs in first and then returns to the consent page.
Exchange endpoint
POST https://demo-api.routerplus.com/api/v1/auth/keys (also POST /v1/auth/keys)
Request body, JSON:
| Field | Required | Meaning |
|---|---|---|
code | yes | The code from the callback. |
code_verifier | when a challenge was sent | The verifier you made in step 1. |
code_challenge_method | no | If sent, it must equal the method on the consent request. |
Response 200: {"key": "...", "user_id": "<email of the user who approved>"}. RouterPlus returns the key one time. Store it in your app.
Rules
- A code expires 10 minutes after approval.
- A code works one time. A second exchange of the same code fails and makes no key.
- A wrong
code_verifierfails and leaves the code usable until it expires. - The key belongs to the workspace that the user selected. Its spend goes to that organization, up to the limit.
- The user can disable the key at any time in the console, under Access > API keys.
- If the user stops being an owner or admin before the exchange, the exchange fails. The code is then used up.
Errors
Errors have the shape {"error": {"code": <status>, "message": "..."}}.
| Status | Cause |
|---|---|
400 | No code; the code is unknown, expired or used; or code_challenge_method is not S256 or plain, or differs from the consent request. |
403 | The code_verifier does not match the challenge, or the user or workspace can no longer make keys. |
405 | The method is not POST. |
503 | A temporary failure. Retry with the same code. |
For agents: /docs/oauth.md and /llms.txt