Overview
Brain OAuth implements the RFC 6749 authorization code flow. Your app sends the user to sign in to Brain and approve access, exchanges the returned code for an access_token on your server, then reads the user profile.
Has a backend that can keep client_secret safe. Authenticates with client_secret; PKCE is optional.
SPAs and mobile apps that cannot keep a secret. No client_secret; PKCE is required.
Authorization code flow
- 1Create an appCreate an app in the developer console and set redirect_uri (HTTPS, except localhost).
- 2Redirect to authorizeSend the user to /oauth/authorize with client_id, redirect_uri and state.
- 3User approvesAfter sign-in and consent the browser returns to redirect_uri with ?code=…&state=….
- 4Exchange the codeYour server calls /oauth/token with the code to get access_token and refresh_token.
- 5Read the userCall /oauth/userinfo with the Bearer access_token.
Endpoints
| Method | Path | Caller | Notes |
|---|---|---|---|
| GET | /oauth/authorize | Browser | Shows the consent page. Guests sign in first and come back here. |
| POST | /oauth/token | Server | Exchange a code or refresh_token for tokens. Basic auth or client_secret in the form. |
| POST | /oauth/revoke | Server | Revoke an access_token or refresh_token. |
| GET | /oauth/userinfo | Server | Requires Authorization: Bearer. Returns sub, email, name and more. |
Token exchange
Call this from your server. Never expose client_secret in the browser.
POST /oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=AUTH_CODE &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback &client_id=YOUR_CLIENT_ID &client_secret=YOUR_CLIENT_SECRET # confidential clients &code_verifier=VERIFIER # when using PKCE
Example response
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "def50200...",
"scope": "openid profile"
}PKCE
Public clients must use PKCE: generate a random code_verifier, send its S256 digest as code_challenge in the authorization request, and submit the original code_verifier when exchanging the code.
const base64url = (bytes) =>
btoa(String.fromCharCode(...new Uint8Array(bytes)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = base64url(
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))
);User info
Returns the user id and profile. An invalid or expired token returns 401 with error=invalid_token.
GET /oauth/userinfo
Authorization: Bearer ACCESS_TOKEN
{
"sub": "u_8f3k2p",
"email": "renjie@brain.im",
"name": "Renjie",
"roles": ["user"]
}Errors
Authorization errors come back to redirect_uri as error and error_description. Token and userinfo errors are returned as JSON.
| Error | Where | Notes |
|---|---|---|
| invalid_request | All | A required parameter is missing or malformed. |
| unauthorized_client | Authorize | The app does not exist, is disabled, or cannot use this grant. |
| access_denied | Authorize | The user denied access. |
| invalid_scope | Authorize | The requested scope is invalid or not allowed for this app. |
| invalid_client | Token | Client authentication failed. Check client_id and client_secret. |
| invalid_grant | Token | The code is invalid, expired or used, or code_verifier does not match. |
| invalid_token | User info | The access_token is invalid or expired. |