Docs
Issuance

Authorization Code Flow

HAIP-compliant credential issuance with PAR and PKCE

The authorization code flow is the HAIP 1.0 recommended approach for credential issuance. It uses Pushed Authorization Requests (PAR, RFC 9126) and Proof Key for Code Exchange (PKCE, RFC 7636).

Flow overview

Step 1: Pushed Authorization Request

The wallet sends its authorization parameters to the PAR endpoint instead of the authorization endpoint directly.

POST /oid4vci/par
Content-Type: application/x-www-form-urlencoded

response_type=code
&client_id=wallet-client-id
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&scope=eu.europa.ec.eudi.pid.1
&issuer_state=offer_abc123
ParameterRequiredDescription
response_typeYesMust be code
client_idYesWallet client identifier
code_challengeYesPKCE code challenge (S256)
code_challenge_methodYesMust be S256
scopeNoRequested credential scope
issuer_stateNoLinks to a pre-existing offer
redirect_uriNoWhere to redirect after authorization
client_assertion_typeNoFor wallet attestation
client_assertionNoWallet attestation JWT

Response (201 Created)

{
  "request_uri": "urn:ietf:params:oauth:request_uri:abc123",
  "expires_in": 60
}

Step 2: Authorization

The wallet navigates to the authorization endpoint with the PAR request URI:

GET /oid4vci/authorize?request_uri=urn:ietf:params:oauth:request_uri:abc123

The server verifies the session and returns an authorization code:

{
  "code": "auth_code_xyz"
}

Or redirects to redirect_uri with ?code=auth_code_xyz&state=... if a redirect URI was provided.

Step 3: Token exchange

The wallet exchanges the authorization code for an access token, proving possession of the PKCE code verifier:

POST /oid4vci/token
Content-Type: application/x-www-form-urlencoded
DPoP: <dpop-proof-jwt>

grant_type=authorization_code
&code=auth_code_xyz
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

The server verifies SHA-256(code_verifier) matches the original code_challenge.

Response

{
  "access_token": "eyJ...",
  "token_type": "DPoP",
  "expires_in": 3600
}

token_type is DPoP when the request carries a DPoP proof header (as above), otherwise Bearer.

The token response does not contain a nonce (OID4VCI 1.0). The wallet fetches the proof nonce from POST /oid4vci/nonce, which returns { "c_nonce": "..." }.

Step 4: Credential request

Same as the pre-authorized flow: the wallet uses the access token and a c_nonce fetched from POST /oid4vci/nonce to request the credential.

PKCE details

PKCE prevents authorization code interception attacks:

  1. Wallet generates a random code_verifier (43-128 characters)
  2. Computes code_challenge = BASE64URL(SHA-256(code_verifier))
  3. Sends code_challenge in PAR request
  4. Sends code_verifier in token request
  5. Server verifies the hash matches