OAuth integration guide

Register a client, send users to consent, exchange the code for tokens and read the user profile

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.

Confidential client

Has a backend that can keep client_secret safe. Authenticates with client_secret; PKCE is optional.

Public client

SPAs and mobile apps that cannot keep a secret. No client_secret; PKCE is required.

Authorization code flow

  1. 1Create an appCreate an app in the developer console and set redirect_uri (HTTPS, except localhost).
  2. 2Redirect to authorizeSend the user to /oauth/authorize with client_id, redirect_uri and state.
  3. 3User approvesAfter sign-in and consent the browser returns to redirect_uri with ?code=…&state=….
  4. 4Exchange the codeYour server calls /oauth/token with the code to get access_token and refresh_token.
  5. 5Read the userCall /oauth/userinfo with the Bearer access_token.

Endpoints

MethodPathCallerNotes
GET/oauth/authorizeBrowserShows the consent page. Guests sign in first and come back here.
POST/oauth/tokenServerExchange a code or refresh_token for tokens. Basic auth or client_secret in the form.
POST/oauth/revokeServerRevoke an access_token or refresh_token.
GET/oauth/userinfoServerRequires Authorization: Bearer. Returns sub, email, name and more.

Authorization request

Redirect the user's browser to the authorization URL. redirect_uri must exactly match one registered on the app.

ParameterRequiredNotes
response_typeRequiredAlways code.
client_idRequiredThe Client ID from the developer console.
redirect_uriRequiredWhere to return after consent. Must be on the app's allow list.
scopeOptionalSpace separated, e.g. openid profile.
stateOptionalA random string returned as is, to prevent CSRF.
code_challengePublic clientsThe S256 digest of code_verifier, sent with code_challenge_method=S256.
HTTP
GET /oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  &scope=openid%20profile
  &state=RANDOM_STATE
  &code_challenge=CHALLENGE
  &code_challenge_method=S256

Token exchange

Call this from your server. Never expose client_secret in the browser.

HTTP
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

JSON
{
  "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.

JavaScript
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))
);
Confidential clients can use PKCE too for extra protection.

User info

Returns the user id and profile. An invalid or expired token returns 401 with error=invalid_token.

HTTP
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.

ErrorWhereNotes
invalid_requestAllA required parameter is missing or malformed.
unauthorized_clientAuthorizeThe app does not exist, is disabled, or cannot use this grant.
access_deniedAuthorizeThe user denied access.
invalid_scopeAuthorizeThe requested scope is invalid or not allowed for this app.
invalid_clientTokenClient authentication failed. Check client_id and client_secret.
invalid_grantTokenThe code is invalid, expired or used, or code_verifier does not match.
invalid_tokenUser infoThe access_token is invalid or expired.