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| Parameter | Required | Description |
|---|---|---|
response_type | Yes | Must be code |
client_id | Yes | Wallet client identifier |
code_challenge | Yes | PKCE code challenge (S256) |
code_challenge_method | Yes | Must be S256 |
scope | No | Requested credential scope |
issuer_state | No | Links to a pre-existing offer |
redirect_uri | No | Where to redirect after authorization |
client_assertion_type | No | For wallet attestation |
client_assertion | No | Wallet 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:abc123The 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_wW1gFWFOEjXkThe 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:
- Wallet generates a random
code_verifier(43-128 characters) - Computes
code_challenge = BASE64URL(SHA-256(code_verifier)) - Sends
code_challengein PAR request - Sends
code_verifierin token request - Server verifies the hash matches