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)

Response Headers

Header Value
Content-Type application/json
Cache-Control no-store
Pragma no-cache

Response Body

{
  "id_token": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZGlwLWVuYy1rZXkifQ...",
  "access_token": "not_applicable",
  "token_type": "Bearer",
  "expires_in": 900
}
Field Type Description
id_token string Encrypted JWT (JWE) containing the signed ID token
access_token string Placeholder value. DIP is an identity verification service where the ID token is the primary artifact.
token_type string OAuth 2.0 token type. Always Bearer.
expires_in number Token lifetime in seconds

Error Response

{
  "error": "invalid_request",
  "error_description": "Session expired"
}

Error Codes

Error HTTP Status Description
invalid_request 400 Missing or invalid parameters, expired session, redirect URI mismatch, or invalid session state
invalid_client 401 Client authentication failed (unknown client or invalid credentials)
not_found 404 Session not found for the given authorization code
access_denied 404 Unauthorized session access or PKCE verification failure
server_error 500 Internal server error

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",
          "check_details": [
            {
              "check_method": "vcrypt",
              "result": "success"
            },
            {
              "check_method": "data",
              "organization": "id.politiet.no",
              "result": "valid"
            }
          ],
          "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": "12345678901"
          }
        }
      ]
    },
    "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