Documentation/Developers
Back
  • Getting started
    • Frontends and app types
    • Environments and endpoints
  • OAuth clients
    • Client ID and secret
    • Redirect URIs and start URL
    • Secret rotation
  • Login with appsolut
    • Authorization code flow
    • Tokens and claims
    • Frontend role
    • Sign-out
    • Revocation and issued tokens
    • Token endpoint errors
  • Frontend keys
    • Issue and store a key
    • Send a key
    • Rotate and revoke
  • Members API
    • Eligibility from a backend
    • The signed-in member
    • Results, rate limits and errors
    • Is an applicant an eligible member?
    • The signed-in frontend user's membership
  • appsolut register API
    • Implement the API
    • Connect and test
    • Verify a member with their challenge code
    • List every member
    • The status of several members
    • The status of one member
    • The person data of several members
    • The person data of one member
    • The claims of several members
    • The claims of one member
    • Health check

Authorization code flow

Start, sign-in, callback and refresh: the flow step by step, with its rules.

1. Start

Your backend creates a random state and a PKCE code_verifier (43 to 128 characters, RFC 7636), stores both on the server for this browser, and redirects the browser to:

<auth>/oauth/authorize
  ?response_type=code
  &client_id=<your client ID>
  &redirect_uri=<one of your redirect URIs, exactly>
  &scope=openid
  &state=<state>
  &code_challenge=<BASE64URL(SHA256(code_verifier))>
  &code_challenge_method=S256

nonce is optional and comes back in the ID token. prompt and max_age are not supported.

2. Sign-in

<auth> sends the browser on to the sign-in surface, in the organisation's sign-in branding with your frontend's name. The person enters their email first, then their password, or signs up with a verification code sent to that email. Where the frontend asks for it, they enter their member code and facts to become a member. Then the sign-in surface runs the membership check and approves the request.

A user still signed in on the sign-in surface from another frontend of the organisation, in the same browser, passes without a password (up to 7 days without use, 30 days at most), but the membership check runs again.

3. Callback

The browser comes back to your redirect URI with code and state. Check that state is the one you stored for this browser, then exchange the code from your backend:

POST <auth>/oauth/token
Authorization: Basic base64(<client ID>:<client secret>)
apikey: <publishable key>
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=<code>&redirect_uri=<the same redirect URI>&code_verifier=<code_verifier>

The answer holds access_token, token_type (bearer), expires_in (3600), refresh_token and id_token.

4. Refresh

Refresh before the access token expires, from your backend:

POST <auth>/oauth/token
Authorization: Basic base64(<client ID>:<client secret>)
apikey: <publishable key>
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=<refresh token>

Every refresh returns a new refresh token. Store it before you use the new access token, and run one refresh per session at a time: a refresh token already used is accepted again for 10 seconds only, after which using it counts as theft and ends the whole session.

The rules, and the traps behind them

  • PKCE S256 only. Discovery also lists plain; never use it.
  • Scope exactly openid. The sign-in surface refuses any other scope with an error page, and the token is refused too. A request without a scope is not openid: the Auth server records it as email, and it is refused. Do not add offline_access: you get a refresh token without it.
  • state is required, although the protocol leaves it optional. Besides protecting your callback, it lets the sign-in surface restart an expired request: an authorization lives 10 minutes, a sign-up with its email code can take longer, and the surface then starts the same request again, so you still get your code with your own state for your own code_verifier. Keep state and verifier for at least 30 minutes.
  • HTTP Basic for the client (client_secret_basic). The secret in the form body is refused.
  • Redirect URIs are exact. A request the sign-in surface cannot serve (an unknown or disconnected frontend, an unregistered redirect URI, a wrong scope) ends on a neutral error page there; nothing comes back to your redirect URI.

Lifetimes

WhatLifetime
Authorization and code10 minutes
Access token1 hour (expires_in 3600)
Refresh tokenRotates on every refresh; a used one is accepted again for 10 seconds
SessionNo fixed end; it lasts until it is revoked. Set your own session lifetime if you need one