curl --request POST \
--url 'https://connect.hopae.com/v2/token' \
--user '{appId}:{appSecret}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback'
{
"access_token": "<opaque access token>",
"token_type": "Bearer",
"expires_in": 600,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid hopae"
}
OIDC
Exchange Code for Token
Exchange an authorization code for v2 ID and access tokens using your app credentials.
POST
/
token
curl --request POST \
--url 'https://connect.hopae.com/v2/token' \
--user '{appId}:{appSecret}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback'
{
"access_token": "<opaque access token>",
"token_type": "Bearer",
"expires_in": 600,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid hopae"
}
Exchanges an authorization code for an access token and an ID token, following the OIDC standard. Call it from your backend, because it needs the App Secret.
Send parameters as
application/x-www-form-urlencoded and authenticate with your App ID and App Secret: HTTP Basic (client_secret_basic, recommended) or form fields (client_secret_post). Public clients without a secret (none) are not supported.Request Body
string
default:"authorization_code"
required
Must be
authorization_code. Refresh tokens are not issued.string
required
The authorization code received on your redirect URI. Single-use, 5-minute lifetime.
string
required
Must exactly match the redirect URI used in the authorization request.
string
Required only if you sent
code_challenge on the authorization request (PKCE, S256).Response
string
Bearer token for the
/userinfo endpoint. Valid for 10 minutes.string
default:"Bearer"
Always
Bearer.number
Access token lifetime in seconds (
600).string
A signed JWT with authentication context only:
sub, iss, aud, iat, exp, nonce, plus acr, hopae_loa, hopae_loa_label, and hopae_verification (provider_id, connection_id). Personal data is never in the ID token. Read it from /userinfo.string
The granted scopes.
curl --request POST \
--url 'https://connect.hopae.com/v2/token' \
--user '{appId}:{appSecret}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback'
{
"access_token": "<opaque access token>",
"token_type": "Bearer",
"expires_in": 600,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid hopae"
}
ID token claims
Decoded id_token payload
{
"sub": "019bc4f2-8a31-7c5e-9d02-4f7a1b3e60d8",
"iss": "https://connect.hopae.com",
"aud": "xhdh8a13",
"iat": 1788427192,
"exp": 1788428992,
"nonce": "n-0S6_WzA2Mj",
"acr": "urn:hopae:loa:4",
"hopae_loa": 4,
"hopae_loa_label": "high",
"hopae_verification": {
"provider_id": "smart-id",
"connection_id": "smart-id"
}
}
subis the verification id, a new value for every verification, not a stable user identifier.hopae_verification.provider_idandhopae_verification.connection_ididentify the connection that ran (catalog ids). They are nested underhopae_verification, not top-level claims. There is noamrclaim on v2 tokens.issishttps://connect.hopae.com. Validate it, and verifyaudagainst your App ID. Keys are at/jwks.- ID tokens are valid for 30 minutes.
Authorizations
Basic authentication header of the form Basic <encoded-value>, where <encoded-value> is the base64-encoded string username:password.
Body
application/json
Must be 'authorization_code'.
Available options:
authorization_code The authorization code received after a successful verification.
Your application's Client ID.
Your application's Client Secret. Required for confidential clients.
Response
Token exchange successful.
A JWT containing the user's verified claims.

