Tokens and lifetimes
Code, id_token, access_token, client_credentials token and webhook signing key: what each is for, how long it lives, how to protect it.
Several “tokens” are involved in an integration. They do not look alike and do not serve the same purpose: mixing them up is the first source of mistakes.
Overview
| Authorization code | id_token |
access_token (user flow) |
client_credentials token |
Webhook signing key | |
|---|---|---|---|---|---|
| Who authenticates to whom | A3BC to your site (through the user) | A3BC proves identity to your server | Your server to A3BC, on behalf of the user | Your server to A3BC, no user | A3BC to your server |
| Where to get it | code parameter of the callback |
Token endpoint response | Token endpoint response | Token endpoint, grant_type=client_credentials |
Delivered by A3BC when the webhook is configured |
| Form | Opaque string | Signed JWT (3 parts) | Opaque string | Opaque string | 32-byte key in base64 (44 characters) |
| What it is for | Being exchanged for the tokens | Reading the claims, after verification | Calling userinfo and the document API |
Calling the delayed sharing API | Checking that the webhook really comes from A3BC |
| Lifetime | Very short, single use | Given by exp (10 minutes in the guide’s example) |
Given by expires_in; access to information is guaranteed for 30 minutes |
About 10 minutes (expires_in: 599 in the guide) |
Until rotation (versioned key: v1, v2…) |
| Where to keep it | Nowhere: exchange it immediately | Keep only the useful claims, not the token | In memory, for the duration of the calls | In memory; request a new one each time | Secrets manager, server-side |
The authorization code
It arrives in the callback URL (?code=…&state=…). It is only worth something for a few moments: your server exchanges it right away at the token endpoint, with its credentials. It is single-use: a second attempt is refused. It goes through the browser, which is why it gives nothing without your client_secret.
The id_token: the proof of identity
It is a JWT: three base64url-encoded blocks separated by dots (header, payload, signature). The payload contains:
- the claims the user shared (
given_name,birthdate…); - information about the token itself:
iss(who issued it),aud(for whom: yourclient_id),sub(the user),iat/exp(issued at / expires at),nonce(yours), and technical claims (at_hash,auth_time,jti,sid,rat).
Never read the content without verifying the signature: anyone can forge a JWT that looks valid. Verification uses the public keys of the jwks_uri; see Verify the tokens. Once verified, extract and store the claims you need: the token itself expires quickly.
The access_token: the key to call the user’s APIs
It represents the consent given by this user for this transaction. You present it in the Authorization: Bearer <access_token> header to:
- the
userinfoendpoint (which returns the same claims as theid_token); - the URL of a document claim (
private:doc.*).
In the guide’s examples it is not a JWT: treat it as an opaque string, do not try to decode it. Its validity is given by expires_in in the token endpoint response. In all cases, information can only be retrieved for 30 minutes after consent: call the APIs right away.
Security rules: never in a URL (it would be logged), never in a browser or mobile app, never in logs, never sent anywhere other than A3BC endpoints.
refresh_token grant was enabled for your RP (optional and reserved for delayed sharing), you also receive a refresh_token. Only use it if A3BC told you to.The client_credentials token: access without a user
For delayed sharing, the user is no longer there when you query the API (that is the point). Your server therefore authenticates itself, with its credentials:
curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=client_credentials -d scope=private:delayed_sharing \
https://oidc-sandbox.a3bc.io/oauth2/token
It is only valid for the private:delayed_sharing scope and lives about 10 minutes: request a new one for each call, or cache it until expires_in minus a margin.
The webhook signing key: authentication the other way round
For all the other keys, you prove your identity to A3BC. Here, A3BC proves its identity to your server: each notification carries a signature computed with a key that only you and A3BC share. It is not an access token and is never sent anywhere. See Webhooks.
The RP credentials
| Credential | Role | Confidential? |
|---|---|---|
client_id |
Identifies your service (also in the id_token aud). |
No |
client_secret |
Authenticates you at the token endpoint (client_secret_basic). |
Yes |
client_secret_basic means you send Authorization: Basic base64(client_id:client_secret). HTTP libraries do it with an auth=(client_id, client_secret) parameter (Python requests) or -u client_id:client_secret (curl).