Proof of Possession schema (openid4vci-proof+jwt)
The proof of possession is the JWT a wallet sends in a
Credential Request to prove it controls the key a
credential will be bound to. Media type openid4vci-proof+jwt, defined in
Appendix F.1, jwt Proof Type
and Section 8.2
of OpenID4VCI v1.0. It carries the Key Attestation in
its header.
JOSE header
| Field | Status | Meaning |
|---|---|---|
typ | REQUIRED | MUST be openid4vci-proof+jwt. |
alg | REQUIRED | Asymmetric algorithm; none MUST NOT be used. |
kid | OPTIONAL | Key ID of the proof key. MUST NOT be present if jwk or x5c is present. |
jwk / x5c / trust_chain | OPTIONAL | Alternative ways to carry the proof public key. |
key_attestation | OPTIONAL | The Key Attestation (key-attestation+jwt) attesting the proof key. |
Payload claims
| Claim | Status | Meaning |
|---|---|---|
iss | OPTIONAL | The Client identifier, when the wallet is a registered client. |
aud | REQUIRED | The Credential Issuer identifier. |
iat | REQUIRED | Issued-at, used for freshness. |
nonce | conditional | The c_nonce provided by the Issuer, when one was issued. |
OpenID4VCI vs TS3
The proof shape is the same openid4vci-proof+jwt, but the two profiles bind it
differently. Switch tabs to compare.
- OpenID4VCI v1.0
- TS3 v1.5
A single proof carrying the KA, signed by one of the attested keys (here k0) with a kid. Header:
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"kid": "k0",
"key_attestation": "eyJ0eXAiOiJrZXktYXR0ZXN0YXRpb24rand0Ii... (the KA)"
}
Payload:
{
"iss": "https://wallet.igrant.io",
"aud": "https://issuer.igrant.io",
"iat": 1780737559,
"nonce": "K3o1c2pZ... (c_nonce)"
}
A single proof, with no kid, signed by attested_keys[0]. Header:
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"key_attestation": "eyJ0eXAiOiJrZXlhdHRlc3RhdGlvbitqd3Qi... (the TS3 KA)"
}
Payload:
{
"iss": "https://wallet.igrant.io",
"aud": "https://issuer.igrant.io",
"iat": 1780737559,
"nonce": "K3o1c2pZ... (c_nonce)"
}