Skip to content

Token Request

The token endpoint exchanges an authorization code for an ID token containing verified identity claims.

Endpoint

POST /token

Request Format

Headers

Content-Type: application/x-www-form-urlencoded

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):

{
  "alg": "ES256",
  "kid": "my-signing-key",
  "typ": "JWT"
}

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, and nbf are 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": "invalid_grant",
  "error_description": "Authorization code has expired"
}

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

  1. Parse the JWE
  2. Decrypt using your private key
  3. Extract the signed JWT from the payload
  4. Verify the JWT signature using DIP's public key (from /jwks)
  5. 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