Token Request¶
The token endpoint exchanges an authorization code for an ID token containing verified identity claims.
Endpoint¶
Request Format¶
Headers¶
Parameters¶
| Parameter | Required | Type | Description |
|---|---|---|---|
client_id |
Yes | string | Your OAuth client identifier |
client_assertion_type |
Yes | string | Must be urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
client_assertion |
Yes | string | Signed JWT for client authentication |
grant_type |
Yes | string | Must be authorization_code |
code |
Yes | string | Authorization code from callback |
code_verifier |
Yes | string | PKCE code verifier (43-128 characters) |
redirect_uri |
Yes | string | Must match the redirect_uri from PAR |
Example Request¶
POST /token HTTP/1.1
Host: {dip-base-url}
Content-Type: application/x-www-form-urlencoded
client_id=dip_aci_your_client_id
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6Im15LWtleSIsInR5cCI6IkpXVCJ9...
&grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&redirect_uri=https://client.example.org/callback
Client Assertion JWT¶
The client assertion authenticates your client. See Authentication for full details.
Header (3 claims exactly):
Payload (4 required claims):
{
"iss": "dip_aci_your_client_id",
"sub": "dip_aci_your_client_id",
"aud": "https://{dip-base-url}",
"exp": 1759835872
}
Note: Standard JWT claims
iat,jti, andnbfare accepted but not required. Any other claims will be rejected.
Response¶
Success Response (200 OK)¶
{
"id_token": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZGlwLWVuYy1rZXkifQ...",
"expires_in": 3600
}
| Field | Type | Description |
|---|---|---|
id_token |
string | Encrypted JWT (JWE) containing the signed ID token |
expires_in |
number | Token lifetime in seconds |
Error Response¶
Error Codes¶
| Error | HTTP Status | Description |
|---|---|---|
invalid_request |
400 | Missing or invalid parameters |
invalid_client |
401 | Client authentication failed |
invalid_grant |
400 | Invalid or expired authorization code |
unauthorized_client |
403 | Client not authorized |
unsupported_grant_type |
400 | Grant type not supported |
server_error |
500 | Internal server error |
temporarily_unavailable |
503 | Service temporarily unavailable |
ID Token Format¶
The id_token is a JWE (JSON Web Encryption) containing a signed JWT.
Encryption Details¶
| Property | Value |
|---|---|
| Algorithm | RSA-OAEP-256 |
| Encryption | A256GCM |
| Content | Signed JWT (nested) |
Decryption Process¶
- Parse the JWE
- Decrypt using your private key
- Extract the signed JWT from the payload
- Verify the JWT signature using DIP's public key (from
/jwks) - Validate claims
Signed JWT Details¶
| Property | Value |
|---|---|
| Algorithm | ES256 |
| Type | jwt |
Complete ID Token Structure¶
After decryption and signature verification, the ID token contains:
{
"iss": "https://{dip-base-url}",
"sub": "hashed-pairwise-subject-identifier",
"aud": "dip_aci_your_client_id",
"exp": 1759839472,
"iat": 1759835872,
"auth_time": 1759835872,
"nonce": "n-0S6_WzA2Mj",
"acr": "urn:bankid:idcheck",
"amr": ["face", "user"],
"verified_claims": {
"verification": {
"trust_framework": "stoe",
"evidence": [
{
"type": "document",
"document_details": {
"type": "passport",
"document_number": "AB1234567",
"date_of_issuance": "2020-01-15",
"date_of_expiry": "2030-01-15",
"issuer": {
"country_code": "NOR",
"name": "INNLANDET POLITIDISTRIKT"
},
"personal_number": {
"type": "no-fnr",
"value": "12345678901"
},
"active_authentication_result": "passed",
"issuer_check": {
"valid": "VALID"
}
}
}
]
},
"claims": {
"given_name": "AASAMUND SPECIMEN",
"family_name": "OESTENBYEN",
"birthdate": "1990-01-15",
"gender": "male",
"nationalities": ["NOR"]
}
}
}
See ID Token for complete claim documentation.
Validation Checklist¶
After decrypting the ID token, validate the following:
| Claim | Validation |
|---|---|
iss |
Must match DIP issuer URL |
aud |
Must match your client_id |
exp |
Must not be expired (current time < exp) |
iat |
Must not be in the future |
nonce |
Must match the nonce from your PAR request |
acr |
Should be urn:bankid:idcheck |
| Signature | Must be valid using DIP's public key |
Next Steps¶
- ID Token - Complete ID token structure and claim details
- Authentication - JWT requirements
- API Reference - OpenID discovery endpoint