OAuth Flow

OAuth authorization code flow (PKCE) #

This is the flow for applications that access Microbooks on behalf of other users. Your app never sees the user’s password: it redirects them to Microbooks, the user signs in and approves the requested scopes, and your app receives tokens scoped to exactly what was granted.

Microbooks implements OAuth 2.1: the authorization code grant with PKCE required, rotating refresh tokens, and no implicit or plain-code flows.

Before you start you need a client — see Client registration.

Server metadata #

Endpoints and capabilities are published at the standard discovery location:

curl https://auth.microbooks.io/.well-known/oauth-authorization-server

The endpoints referenced below:

EndpointURL
Authorizationhttps://auth.microbooks.io/oauth/authorize
Tokenhttps://auth.microbooks.io/oauth/token
Revocationhttps://auth.microbooks.io/oauth/revoke
JWKShttps://auth.microbooks.io/.well-known/jwks.json

1. Create a code verifier and challenge #

Generate a random code_verifier (43–128 characters, URL-safe), then derive the challenge:

code_challenge = base64url( sha256( code_verifier ) )
import base64, hashlib, secrets

verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode()
challenge = base64.urlsafe_b64encode(
    hashlib.sha256(verifier.encode()).digest()
).rstrip(b"=").decode()

2. Redirect the user to authorize #

https://auth.microbooks.io/oauth/authorize
    ?client_id=<your client id>
    &redirect_uri=<one of your registered redirect URIs>
    &response_type=code
    &scope=books:read%20books:write
    &state=<random anti-CSRF value>
    &code_challenge=<challenge>
    &code_challenge_method=S256

The user signs in (if needed) and reviews a consent screen listing the scopes you asked for. On approval Microbooks redirects back to your redirect_uri with ?code=...&state=....

Always verify state. Reject the callback if it does not exactly match the value you sent — this is your CSRF protection for the flow.

3. Exchange the code for tokens #

curl --request POST 'https://auth.microbooks.io/oauth/token' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=<your client id>' \
--data-urlencode 'redirect_uri=<the same redirect URI>' \
--data-urlencode 'code=<code from the callback>' \
--data-urlencode 'code_verifier=<the verifier from step 1>'

Response #

{
    "token_type": "Bearer",
    "expires_in": 900,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
    "refresh_token": "def50200a1b2c3..."
}

Confidential clients (server-side apps holding a secret) also send client_secret; public clients (mobile, SPA, CLI) must not have one — PKCE is their proof.

4. Call the API #

curl 'https://api.microbooks.io/books/v1/entity' \
--header 'Authorization: Bearer <access_token>'

5. Refresh #

Access tokens live 15 minutes. Redeem the refresh token for a new pair:

curl --request POST 'https://auth.microbooks.io/oauth/token' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=<your client id>' \
--data-urlencode 'refresh_token=<refresh_token>'
Refresh tokens rotate. Each refresh response contains a new refresh token and the old one is revoked. Persist the new one atomically — reusing a rotated-away token yields invalid_grant and the user has to authorize again.

Verifying tokens yourself #

Access tokens are RS256-signed JWTs. If you run your own resource server, verify signatures against the JWKS; the sub claim is the account id and scopes carries the granted scopes.