Sign-in and Access · 10 min read

Web clients and API clients in Access

Register an app that signs users in, a backend that calls the platform's service APIs, and a pipeline that configures Access itself. Each needs a different kind of client.

Pick the right kind of client

Access has two kinds of clients, and they are not interchangeable. Pick by what the caller does.

You want toCreateWhereIt authenticates with
Sign users in to a web, single-page or mobile appA web client with the Authorization code grantAccess admin → Web clientsAuthorization code + PKCE, then the token endpoint
Call service APIs (Identity Store, Password Store, FIDO, Consent …) from a backend, with no userA web client with the Client credentials grantAccess admin → Web clientsPOST https://{tenant-host}/oauth/v1/token, then a bearer token
Configure Access: web clients, API clients, scopes, IdPs, SAML service providers, web hooks, assurance levelsAn API clientAccess admin → API clientsIts own credentials on every call. No token request.

A web client sent to Access's configuration API gets 401 invalid_credentials. An API client's credentials don't get you a token for the service APIs. If a call fails with 401 and the secret looks right, check that you used the right kind of client.

Create a web client for an app

  1. 1

    Open Access admin → Web clients and add a client

    Give it a Name (only shown to admins) and a Client ID. Let the console generate the ID unless the app needs a fixed one.

  2. 2

    Choose the authentication method

    Client secret or Private key JWT for apps with a backend that can keep a secret. Public for single-page and mobile apps, which can't. See the table below.

  3. 3

    Set the grant type to Authorization code

    Leave Implicit and Resource owner password credentials off. Add Device code only for devices without a browser or keyboard.

  4. 4

    Choose the access token format

    JWT if your APIs validate tokens themselves. Opaque if they call Access to introspect them.

  5. 5

    Add the redirect URIs

    Redirect URL is required for the Authorization code grant. Add other environments under Additional redirect URLs. Access compares them exactly, so include the path.

  6. 6

    Set the lifetimes

    Access grant validity (how long the code lives, e.g. 30 seconds) and Access token validity (e.g. 900 seconds) are both required. Turn on Issue refresh token only if the app needs to stay signed in without a browser round trip.

  7. 7

    Assign scopes

    Put openid and the scopes the app always needs under Default scopes. Put scopes the app sometimes asks for under Additional scopes. See Scopes and claims.

  8. 8

    Choose the identity provider

    Select the Identity provider that signs the users in: an Identity Orchestration journey, the Identity Broker, or another provider set up in Access. This decides which sign-in the user sees.

  9. 9

    Complete the OpenID Connect settings

    Because openid is among the scopes, Access asks for the relying party settings: ID token validity, Post Logout Redirect URL, Front-Channel Logout URL, and Delete access/refresh tokens on logout (turn it on).

  10. 10

    Save and hand over the values

    Give the app team the client ID, the secret or how to register its key, and the discovery URL https://{tenant-host}/oauth/.well-known/openid-configuration.

Skip Consent page hides the consent prompt. Use it for your own first-party apps. Leave consent on for third-party apps that get user data.

Grant types and client authentication

Grant typeUse it for
Authorization codeEvery app that signs users in. The only grant a public client may use, always with PKCE.
Client credentialsMachine-to-machine calls with no user. See the next section.
Device codeTVs, consoles and other devices with no browser or limited input. Set the Device Verification URL too.
ImplicitDon't. Tokens travel in the browser URL. Use Authorization code with PKCE.
Resource owner password credentialsDon't. The app sees the user's password and MFA can't work. Only for migrating legacy apps.
Authentication methodHow the client proves who it isUse when
Client secretClient ID and secret in an HTTP Basic header (preferred) or in the form body.A server-side app. Rotate the secret like a password.
Private key JWTThe client signs a short JWT with its private key (iss and sub = client ID, aud, exp, jti). Access holds only the public key, from a JWKS URI or as a static key. Keys: EC P-256 or stronger, or RSA 2048–4096.You want no shared secret. Prefer a JWKS URI so keys can rotate.
PublicNo client authentication. PKCE protects the code instead.Single-page and mobile apps, which can't keep a secret.

Use PKCE with the S256 method for every authorization code client, public or not. It costs nothing and stops stolen codes from being redeemed.

Token lifetimes

SettingWhat it controlsStarting point
Access grant validitySeconds the app has to exchange an authorization code. Required with Authorization code.30
Access token validitySeconds an access token is valid. Always required, for user and machine clients.300–900 for user apps; up to 3600 for machine clients
Refresh token validitySeconds from the first access token until the refresh token stops working. Empty means it never expires.Match your app's "stay signed in" promise; never leave it empty for public clients
ID token validityThe ID token's exp. Your app checks it once, at sign-in.Same as the access token
  • Short access tokens limit the damage of a leaked token. Refresh tokens keep the user signed in meanwhile.
  • Keep Revoke all active refresh tokens when an old refresh token is used on. A replayed refresh token then revokes the whole chain.
  • The Access single sign-on session has its own lifetime, separate from the tokens. See How sign-in works.

Create a machine-to-machine web client

A backend, a pipeline or an Identity Orchestration flow that calls the service APIs uses a web client with the Client credentials grant. Two settings decide what its token can do:

SettingDecidesWhen it's wrong
Default audiencesWhich services accept the token (its aud claim). Add one audience per service, e.g. identity-store.The service answers 401.
Default scopesWhat the client may do in those services (its scope claim), e.g. read identities.The service answers 403 although the audience is there.
  1. 1

    Add a web client

    Access admin → Web clients. Authentication method Client secret or Private key JWT. Grant type Client credentials only.

  2. 2

    Set Access token validity

    It is required even though there is no user.

  3. 3

    Add the audiences

    Add each service the client calls to Default audiences. Each API page lists the audience it expects.

  4. 4

    Add the scopes

    Add the scopes the client always needs to Default scopes. Use the scope builder to pick the scopes for the operations you call. Grant the least you need.

  5. 5

    Check the length

    Join the Default scopes with spaces and count: keep the string within 1024 characters.

  6. 6

    Request a token and test

    Call the token endpoint, then one read operation of each service.

curl -s https://{tenant-host}/oauth/v1/token \
  -d grant_type=client_credentials \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET

# Service APIs live under the tenant host
curl -s https://{tenant-host}/identity-store/api/v1/... \
  -H "Authorization: Bearer $ACCESS_TOKEN"

If the Default scopes, joined with spaces, pass about 1024 characters, the token endpoint answers HTTP 500 to that client. Access still accepts the client change, so the 500 looks like an outage. Move rarely used scopes to Additional scopes, or split the work over two clients.

Additional scopes are issued only when the token request names them with scope= (space-separated). A request that names scopes gets only those, so one client can hold more scopes than fit in one token.

ReplyMeansFix
Token endpoint: 401 invalid_clientUnknown client ID or wrong secret.Check the client; set the secret again.
Token endpoint: 400 unauthorized_clientThe client doesn't have the Client credentials grant.Add the grant type.
Token endpoint: 400 invalid_request with audience=The client doesn't have that audience.Add it to Default audiences. Sending audience=<name> is a quick test.
Token endpoint: 500Default scopes too long.Trim to 1024 characters.
Service: 401The token lacks the service's audience.Add it to Default audiences.
Service: 403A scope is missing.Add it to Default scopes, or request it with scope=.

To see what a client really gets, decode a fresh token's payload and read aud and scope. Never paste a live token into a website.

Create an API client for Access's configuration API

Access's own configuration API, at https://{tenant-host}/oauth/api/v1/configuration, manages web clients, API clients, scopes, identity providers, SAML service providers, web hooks and assurance levels. Use it to keep Access configuration in code. It takes an API client, not a web client.

  1. 1

    Add an API client

    Access admin → API clients. Give it a name and a client ID.

  2. 2

    Choose how it authenticates

    Client secret (HTTP Basic) or Private key JWT. With Private key JWT, register the public key as a PEM (-----BEGIN PUBLIC KEY-----), not as a JWK.

  3. 3

    Allow only the APIs it needs

    Each Access API is allowed separately, as an onegini_api_* scope. The configuration API is onegini_api_config. Events and end-user APIs are separate permissions.

  4. 4

    Call the API directly

    There is no token request. Send the client's credentials on every call: Basic, or a freshly signed JWT as a bearer token.

curl -s https://{tenant-host}/oauth/api/v1/configuration/web-clients \
  -u "$API_CLIENT_ID:$API_CLIENT_SECRET"
ReplyMeans
401 invalid_credentialsNot an API client (often a web client), wrong secret, or a Private key JWT with the wrong key, client ID or aud.
403 insufficient_permissionsThe API client exists but isn't allowed to use this API. Allow it on the API client.
  • Creating a web client through the API needs access_token_expires_in, and with the Authorization code grant also access_grant_expires_in, although the spec doesn't mark them required.
  • A scope PATCH replaces the whole scope: send every field. A web client PATCH keeps the fields you leave out.
  • A hashed client secret must be in PHC form ($bcrypt$...), not the usual $2b$ form.
  • Reads by ID can show the old version for about two minutes after a change. Tokens reflect the change at once.

All operations and more pitfalls: Access configuration API.

Related