Developer Documentation
Everything you need to integrate with this OAuth 2.0 and OpenID Connect (OIDC) authorization server — from registering your application to authenticating users and fetching profile data.
How It Works
The server implements the standard Authorization Code Grant flow with PKCE, separating authentication (verifying identity) from authorization (granting permissions).
- Authorization Request: Your app redirects the user to this server.
- Authentication: The user logs in securely on this domain.
- Consent: The user reviews the permissions (scopes) your app is requesting and approves them.
- Callback: The server redirects the user back to your app with a short-lived authorization
code. - Token Exchange: Your backend exchanges the
codefor an Access Token and ID Token. - Data Access: Your app uses the Access Token to fetch user profile data.
Your App Auth Server User
| | |
|-- GET /o/authorization -->| |
| (client_id, scope, | |
| state, code_challenge) | |
| |<-- login/register ---|
| |--- consent screen -->|
| |<-- user approves ----|
|<-- redirect ?code=... ----| |
| | |
|-- POST /o/token ----------| |
| (code, code_verifier, | |
| client_secret) | |
|<-- access_token, id_token-| |
| | |
|-- GET /o/userinfo ------->| |
| (Bearer access_token) | |
|<-- { sub, name, email } --| |
Authentication Flow
Users interact with this server directly to register and log in. Your app never handles passwords — it only receives tokens after the user authenticates here.
New users
When a user hits /o/authorization without an active session, they are redirected to the sign-up/login page. After creating an account and verifying their identity, they are returned to the consent screen and then back to your app.
Returning users
If the user already has a valid session cookie on this domain, the login step is skipped. If they have previously granted the same scopes to your client, the consent screen is also skipped and they are redirected straight back with a new code.
user_id + client_id pair in the oauth_consents table. Requesting additional scopes later will re-show the consent screen for only the new scopes.Client Registration
Before integrating, register your application in the Developer Portal to obtain credentials.
- Go to the Developer Portal and sign in.
- Click Register a new application.
- Provide your Application Name, Application URL, and Redirect URI — the exact endpoint in your app that will handle the authorization callback.
- You will receive a Client ID and a Client Secret.
Generating PKCE
PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks. You generate a random code_verifier, hash it to produce the code_challenge, and send the challenge with the authorization request. The verifier is sent only during the token exchange.
// Works in any modern browser or Node.js 20+
async function generatePKCE() {
// 1. Random 32-byte verifier, base64url-encoded
const array = crypto.getRandomValues(new Uint8Array(32));
const codeVerifier = btoa(String.fromCharCode(...array))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
// 2. SHA-256 hash of the verifier
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await crypto.subtle.digest('SHA-256', data);
// 3. base64url-encode the hash → code_challenge
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
return { codeVerifier, codeChallenge };
}
// Generate a random state to prevent CSRF
const state = crypto.randomUUID();
Store codeVerifier and state in sessionStorage before redirecting. Verify state matches when your callback receives the redirect.
Integration Guide
Step 1 — Request Authorization
Redirect the user's browser to the authorization endpoint. Use the PKCE values and state generated above. The state parameter is an opaque random string your app generates — the server echoes it back in the callback so you can verify the redirect wasn't forged (CSRF protection).
GET /o/authorization?
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
response_type=code&
scope=openid email profile&
state=YOUR_RANDOM_STATE&
code_challenge=YOUR_PKCE_CHALLENGE&
code_challenge_method=S256
On callback, verify state matches what you stored before proceeding with the code exchange.
Step 2 — Exchange Code for Tokens
After the user approves, they are redirected to your redirect_uri with a code. Exchange it from your backend:
POST /o/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "CODE_FROM_CALLBACK",
"redirect_uri": "https://yourapp.com/callback",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"code_verifier": "YOUR_PKCE_VERIFIER"
}
The response includes an access_token, id_token, and optionally a refresh_token.
{
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"id_token": "eyJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "opaque-refresh-token-string"
}
refresh_token is only present when offline_access was in the requested scope.
Step 3 — Fetch User Data
Use the Access Token to retrieve the authenticated user's profile:
GET /o/userinfo
Authorization: Bearer YOUR_ACCESS_TOKEN
{
"sub": "3f2a1b4c-...",
"name": "Jane Doe",
"email": "jane@example.com",
"picture": "https://example.com/avatar.jpg"
}
Step 4 — Refresh Tokens
Access tokens expire after 1 hour. If you requested the offline_access scope, use the Refresh Token to get a new Access Token without prompting the user again:
POST /o/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Endpoints Reference
| Endpoint | Method | Description |
|---|---|---|
/.well-known/openid-configuration |
GET | OIDC Discovery document — all endpoints and supported configurations. |
/o/certs |
GET | JSON Web Key Set (JWKS) used to verify JWT signatures. |
/o/authorization |
GET | Authorization endpoint — users log in and grant consent here. |
/o/token |
POST | Exchanges authorization codes or refresh tokens for active tokens. |
/o/userinfo |
GET | Returns user profile data. Requires a valid Bearer token. |
Scopes & Tokens
Supported Scopes
openid— Required for OIDC. Verifies the user's identity.email— Access the user's email address.profile— Access the user's name and profile picture.offline_access— Issues a Refresh Token for long-lived access.
Token Types
/o/certs.- Access Token — A stateless JWT valid for 1 hour. Contains
client_id,sub(user ID), and granted scopes. For first-party apps, also set as an HttpOnly secure cookie. - ID Token — An OIDC-compliant JWT with identity claims like email and name.
- Refresh Token — An opaque string with a longer lifetime. Store it securely in your backend only.