Skip to main content
Developer

Token Request#

View as Markdown
On this page

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#

ParameterRequiredTypeDescription
client_idYesstringYour OAuth client identifier
client_assertion_typeYesstringMust be urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_assertionYesstringSigned JWT for client authentication
grant_typeYesstringMust be authorization_code
codeYesstringAuthorization code from callback
code_verifierYesstringPKCE code verifier (43-128 characters)
redirect_uriYesstringMust 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#

HeaderValue
Content-Typeapplication/json
Cache-Controlno-store
Pragmano-cache

Response Body#

{
  "id_token": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZGlwLWVuYy1rZXkifQ...",
  "access_token": "not_applicable",
  "token_type": "Bearer",
  "expires_in": 900
}
FieldTypeDescription
id_tokenstringEncrypted JWT (JWE) containing the signed ID token
access_tokenstringPlaceholder value. DIP is an identity verification service where the ID token is the primary artifact.
token_typestringOAuth 2.0 token type. Always Bearer.
expires_innumberToken lifetime in seconds

Error Response#

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

Error Codes#

ErrorHTTP StatusDescription
invalid_request400Missing or invalid parameters, expired session, redirect URI mismatch, or invalid session state
invalid_client401Client authentication failed (unknown client or invalid credentials)
not_found404Session not found for the given authorization code
access_denied404Unauthorized session access or PKCE verification failure
server_error500Internal server error

ID Token Format#

The id_token is a JWE (JSON Web Encryption) containing a signed JWT.

Encryption Details#

PropertyValue
AlgorithmRSA-OAEP-256
EncryptionA256GCM
ContentSigned JWT (nested)

Decryption Process#

  1. 1

    Parse the JWE

  2. 2

    Decrypt using your private key

  3. 3

    Extract the signed JWT from the payload

  4. 4

    Verify the JWT signature using DIP's public key (from /jwks)

  5. 5

    Validate claims

Signed JWT Details#

PropertyValue
AlgorithmES256
Typejwt

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:

ClaimValidation
issMust match DIP issuer URL
audMust match your client_id
expMust not be expired (current time < exp)
iatMust not be in the future
nonceMust match the nonce from your PAR request
acrShould be urn:bankid:idcheck
SignatureMust be valid using DIP's public key

Next Steps#

Was this page helpful?