Security best practices

Check your OneiD integration against a list of practices for flows, secrets, tokens, sessions and APIs before you go live.

View as Markdown

Use this page as a checklist before an application or API goes live with OneiD. Each item says what to do and why. Most OpenID Connect libraries do the protocol checks for you when you configure them correctly.

Sign-in requests

  • Use the authorization code flow with PKCE and S256 in every application, including server-side applications. Public clients must use PKCE; confidential clients have it required by default. Keep it on.
  • Send state and check it on the callback. It binds the response to the browser that started the sign-in and stops cross-site request forgery. Use a fresh, unguessable value for each request.
  • Send nonce and check it in the ID token. It stops a stolen ID token from being replayed into your application.
  • Check iss in the authorisation response. OneiD includes iss in every authorisation response (RFC 9207). It must equal the issuer from discovery.
  • Register exact redirect URIs. OneiD compares redirect URIs exactly and allows no wildcards. Register one URI per environment instead of a pattern.
  • Use https everywhere. OneiD accepts http redirect URIs only for loopback addresses (localhost, 127.0.0.1, [::1]), for local development and desktop applications.
  • Do not reload the callback page. An authorisation code works once. A second attempt fails with invalid_grant.

Client secrets

  • Never put a client secret in a browser, mobile or desktop application. Anything shipped to a user’s device can be read. These applications are public clients and use PKCE without a secret.
  • Keep the secret in a secret store, such as your platform’s secret manager or environment variables injected at run time. Do not commit it to source control or put it in a configuration file in a repository.
  • Plan secret rotation. A client has one secret at a time. When an administrator replaces it, the old secret stops working immediately. Agree a moment with your OneiD administrator, update your application at the same time, and check that sign-in works afterwards.
  • Know when the secret expires. An expired secret gives invalid_client with “The client secret has expired.” Ask your administrator for the expiry date and put the replacement in your calendar.
  • Send the secret with client_secret_basic (HTTP Basic authentication). client_secret_post also works.

Tokens in your application

  • Validate ID tokens before you trust them: signature (RS256), iss with the trailing slash, aud equals your client ID, exp, and nonce. See Tokens.
  • Store tokens where only your application can read them.
    • Server-side web applications: in the server-side session, not in a cookie the browser can read.
    • Browser applications: in memory. Avoid localStorage, which any script on the page can read.
    • Mobile and desktop applications: in the operating system’s secure storage.
  • Handle refresh-token rotation. Every refresh returns a new refresh token. Store the new one and discard the old one. Do not run two refreshes with the same token at the same time: a refresh token used again after a short grace period is refused, and OneiD then revokes the whole chain, including newer tokens. See Refresh tokens.
  • Treat invalid_grant on refresh as “sign in again”. It also happens when the user was locked, deleted or must change the password.
  • Never log tokens, codes or secrets. Do not put them in URLs you build yourself, error messages or analytics. Log the sub and the token’s expiry instead if you need a trace.
  • Request the fewest scopes you need. Ask for offline_access only when the application really needs to work without the user present.

Sessions and sensitive actions

  • Keep application sessions short. OneiD does not notify other applications when a user signs out of one application. Each application keeps its own session until it ends. Short sessions, prompt=none checks and failed refreshes are how you notice. See Sessions, prompt and max_age.
  • Sign out properly. Clear your session, then send the user to OneiD’s end-session endpoint with id_token_hint. See Sign-out.
  • Check amr or acr before sensitive actions. OneiD does not enforce acr_values; asking for urn:oltin:ac:mfa does not force MFA. If an action needs MFA, check that acr is urn:oltin:ac:mfa or urn:oltin:ac:external as your policy allows, or that amr contains mfa. If MFA must always happen, ask your administrator to make MFA mandatory for the users.
  • Ask for a fresh sign-in when it matters. Use max_age or prompt=login and then check auth_time in the new ID token.

APIs

  • Validate every access token: RS256 signature, iss with the trailing slash, exp, and the required scope. Do not require aud; OneiD access tokens have none.
  • Use distinct scopes per API, so a token for one API cannot be used at another. See Protect an API.
  • Cache the JWKS and re-fetch it when a token has an unknown kid. Do not fetch it on every request.
  • Allow a small clock skew, no more than a minute or so, when you check exp and nbf.

Behaving well

  • Handle HTTP 429. When OneiD answers 429, wait for the number of seconds in the Retry-After header before you try again. Do not retry in a tight loop. See Rate limits.
  • Cache client credentials tokens until shortly before they expire, instead of requesting a new token per call.
  • Read the discovery document instead of hard-coding endpoint addresses.

Reporting a security problem

If you find a security problem in OneiD, report it privately through the contact page (topic: Security report). The contact details are also published at https://oltinid.com/.well-known/security.txt. Please do not share details publicly before we have had a chance to respond.

Learn more