OpenID4VCI-utstedelse av legitimasjon forklart: fra tilbud til akseptert legitimasjon
OpenID4VCI, OpenID for Verifiable Credential Issuance, definerer hvordan en wallet ber om og mottar en legitimasjon fra en issuer. En enkelt utstedelse går gjennom flere distinkte trinn før wallet faktisk har en brukbar legitimasjon, og hvert trinn har sin egen feilmodus. Denne siden går gjennom hele livssyklusen, med originale gjennomarbeidede eksempler.
To måter å starte på: forhåndsautorisert kode og autorisasjonskode
Utstedelse starter ofte med en Credential Offer sendt av issueren, og grant-en den angir avgjør hvordan wallet blir autorisert. En wallet kan også starte utstedelsen selv, uten noe tilbud, ved å bruke autorisasjonskode-flyten. Begge flytene ender samme sted: wallet innehar et access token den kan bruke til å be om legitimasjonen.
Flyt med forhåndsautorisert kode
1. Issuer
Kjenner allerede innehaveren og utsteder en Credential Offer med en pre-authorized_code
2. Wallet
Løser inn koden hos token endpoint, eventuelt sammen med en transaksjonskode
3. Wallet
Ber om legitimasjonen med et bevis på at den innehar nøkkelen
Flyt med autorisasjonskode
1. Wallet
Skanner en Credential Offer som angir et authorization_code-grant, eller starter flyten selv uten et tilbud
2. Authorization Server
Fører innehaveren gjennom innlogging og samtykke, og utsteder deretter en kode
3. Wallet
Veksler koden inn i et token, og ber deretter om legitimasjonen
Hele livssyklusen til et legitimasjonstilbud
Spesifikasjonen definerer ikke navngitte tilstander, men en issuer-initiert flyt der legitimasjonen utstedes med en gang er enklest å følge, som i sekvensen under. Hvert trinn kan feile på sin egen måte, og en wallet-implementasjon må håndtere disse stiene, ikke bare den vellykkede.
Gjennomarbeidet eksempel: et kjøretøykontrollsertifikat for en transportflåte
Et kjøretøykontrollorgan utsteder et kjøretøykontrollsertifikat til et transportselskaps business wallet etter en rutinekontroll. Kontrolløren har allerede autentisert flåtesjefen på kontrollstedet, så issueren bruker flyten med forhåndsautorisert kode. Trinnene under følger denne ene utstedelsen fra tilbud til akseptert legitimasjon.
Eksempelet viser grunnleggende OpenID4VCI. Løsninger med høy tillitsgrad, som EUDI Wallet, følger HAIP-profilen på toppen av dette, som legger til DPoP-bundne access tokens, wallet attestation hos token endpoint og key attestation for legitimasjonsnøklene. Dette er utelatt her for at hvert trinn skal være lett å følge.
1. Legitimasjonstilbud
Kontrollorganets terminal viser en QR-kode. Den inneholder en URI som starter med openid-credential-offer:// og som bærer tilbudet under, URL-kodet i en credential_offer-parameter, eller en credential_offer_uri som wallet henter det fra. Wallet skanner den og leser hvilken legitimasjon som tilbys og hvordan den skal hentes.
Issuer
Viser en QR-kode med en Credential Offer
Angir legitimasjonskonfigurasjonen og et pre-authorized_code-grant
{
"credential_issuer": "https://issuer.fleetinspect.example",
"credential_configuration_ids": ["roadworthiness_certificate"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "fi-8f2c0b3a",
"tx_code": {
"length": 6,
"input_mode": "numeric",
"description": "Enter the code sent to your phone by text message"
}
}
}
}2. Oppdagelse av issuerens metadata
Før wallet ber om noe som helst, henter den issuerens metadata for å finne ut hva roadworthiness_certificate inneholder og hvilke endepunkter som skal kalles. Metadataene lister ingen egen authorization server, så issueren er sin egen authorization server, og wallet leser token endpoint fra den serverens metadata.
Wallet
GET /.well-known/openid-credential-issuer
Lærer legitimasjonsformatet, claims og godtatte bevistyper, i tillegg til nonce-, credential-, deferred- og notification-endepunktene
Metadata for credential issuer (utdrag)
{
"credential_issuer": "https://issuer.fleetinspect.example",
"nonce_endpoint": "https://issuer.fleetinspect.example/nonce",
"credential_endpoint": "https://issuer.fleetinspect.example/credential",
"deferred_credential_endpoint": "https://issuer.fleetinspect.example/deferred",
"notification_endpoint": "https://issuer.fleetinspect.example/notify",
"credential_configurations_supported": {
"roadworthiness_certificate": {
"format": "dc+sd-jwt",
"vct": "https://fleetinspect.example/vct/roadworthiness",
"cryptographic_binding_methods_supported": ["jwk"],
"credential_signing_alg_values_supported": ["ES256"],
"proof_types_supported": {
"jwt": { "proof_signing_alg_values_supported": ["ES256"] }
},
"credential_metadata": {
"claims": [
{ "path": ["vehicle_registration"] },
{ "path": ["inspection_result"] },
{ "path": ["valid_until"] }
]
}
}
}
}Metadata for authorization server (utdrag), fra /.well-known/oauth-authorization-server
{
"issuer": "https://issuer.fleetinspect.example",
"token_endpoint": "https://issuer.fleetinspect.example/token",
"pre-authorized_grant_anonymous_access_supported": true
}3. Token-forespørsel
Wallet løser inn den forhåndsautoriserte koden hos token endpoint, sammen med transaksjonskoden kontrollorganet sendte til flåtesjefens telefon. Å sende koden over en annen kanal gjør at noen som fotograferer QR-koden over skulderen, likevel ikke kan løse den inn.
Wallet
POST /token
Sender pre-authorized_code og tx_code, mottar et access token begrenset til dette tilbudet
POST /token HTTP/1.1 Host: issuer.fleetinspect.example Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code &pre-authorized_code=fi-8f2c0b3a &tx_code=482913
Svar
{
"access_token": "fi-at-3d91e0",
"token_type": "Bearer",
"expires_in": 86400
}4. Bevis på nøkkelbesittelse
Fordi metadataene lister et nonce_endpoint, henter wallet først en fersk c_nonce der. Deretter beviser den at den innehar den private nøkkelen legitimasjonen skal bindes til, ved å signere en proof-JWT over issuer-identifikatoren og denne c_nonce-en.
Wallet
POST /nonce, deretter signeres en proof-JWT med nøkkelen legitimasjonen skal bindes til
Binder legitimasjonen til den nøkkelen, ikke bare til den som innehar access token
POST /nonce HTTP/1.1 Host: issuer.fleetinspect.example
Svar
{
"c_nonce": "fi-nonce-77aa"
}Wallet bygger nå proof-JWT-en fra to JSON-objekter, en header og en payload, og signerer dem med den private nøkkelen legitimasjonen skal bindes til.
Header: hva denne JWT-en er og hvilken nøkkel som signerte den
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
}typ: markerer dette som en OpenID4VCI key proof, slik at den ikke kan forveksles med noen annen type JWTalg: signeringsalgoritmen, en av dem issueren listet i proof_signing_alg_values_supportedjwk: den offentlige nøkkelen legitimasjonen skal bindes til; issueren kontrollerer signaturen mot denne
Payload: hvem beviset gjelder og når det ble laget
{
"aud": "https://issuer.fleetinspect.example",
"iat": 1789376400,
"nonce": "fi-nonce-77aa"
}aud: issuer-identifikatoren, slik at beviset ikke kan gjenbrukes hos en annen issueriat: tidspunktet beviset ble opprettet, i sekunder siden 1970nonce: c_nonce fra nonce endpoint, som viser at beviset er ferskt
Signert resultat
Header og payload er hver base64url-kodet og satt sammen med et punktum. Wallet signerer denne strengen med sin private nøkkel og legger til den base64url-kodede signaturen etter et andre punktum. Den resulterende strengen er proof-JWT-en wallet sender i legitimasjonsforespørselen i trinn 5.
base64url(header) . base64url(payload) . base64url(signature) eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIs... .eyJhdWQiOiJodHRwczovL2lzc3Vlci5mbGVldGluc3BlY3QuZXhhbXBsZSIs... .<ES256 signature>
5. Legitimasjonsforespørsel
Wallet kaller credential endpoint med access token og beviset, og issueren utsteder og returnerer den signerte legitimasjonen.
Wallet
POST /credential
Sender access token, konfigurasjons-id-en og proof-JWT-en, mottar den signerte legitimasjonen og en notification_id
POST /credential HTTP/1.1
Host: issuer.fleetinspect.example
Content-Type: application/json
Authorization: Bearer fi-at-3d91e0
{
"credential_configuration_id": "roadworthiness_certificate",
"proofs": {
"jwt": ["<proof JWT from step 4>"]
}
}Svar
{
"credentials": [
{ "credential": "<issuer-signed SD-JWT VC>" }
],
"notification_id": "fi-notif-9012"
}Hele flyten i ett blikk
Dette sekvensdiagrammet setter sammen de fem trinnene i det gjennomarbeidede eksempelet, fra tilbudet til varselet. Heltrukne piler er forespørsler, stiplede piler er svar, og den prikkede pilen er transaksjonskoden som beveger seg utenfor protokollen via SMS.
Wallet
Transportselskapets business wallet
Authorization Server
Drives av issueren selv i dette eksempelet
Credential Issuer
Kjøretøykontrollorgan
- Credential Issuer til Wallet: Credential Offer, vist som QR-kode
- Credential Issuer til Wallet: tx_code, sendt til flåtesjefens telefon per SMS
- Wallet til Credential Issuer: GET /.well-known/openid-credential-issuer
- Credential Issuer til Wallet: metadata for credential issuer
- Wallet til Authorization Server: GET /.well-known/oauth-authorization-server
- Authorization Server til Wallet: metadata for authorization server
- Wallet til Authorization Server: POST /token: pre-authorized_code, tx_code
- Authorization Server til Wallet: access_token
- Wallet til Credential Issuer: POST /nonce
- Credential Issuer til Wallet: c_nonce
- Wallet: signerer proof-JWT
- Wallet til Credential Issuer: POST /credential: access token, bevis
- Credential Issuer til Wallet: credentials, notification_id
- Wallet: validerer og lagrer
- Wallet til Credential Issuer: POST /notify: credential_accepted
- Credential Issuer til Wallet: 204 No Content
Når legitimasjonen ikke er klar ennå: utsatt utstedelse
Eksempelet over forutsetter at kontrollresultatet allerede er endelig. Hvis kontrollorganet i stedet må eskalere et tvilsomt resultat til en seniorkontrollør, kan credential endpoint ikke returnere legitimasjonen med en gang, så den utsetter utstedelsen.
Umiddelbar utstedelse
Credential endpoint returnerer den signerte legitimasjonen i samme respons som forespørselen.
Utsatt utstedelse
Credential endpoint svarer med HTTP 202, en transaction_id og et intervall i stedet. Wallet spør deferred credential endpoint med denne id-en, og venter minst intervallet i sekunder mellom hver forespørsel, helt til legitimasjonen er klar.
Utsatt svar fra /credential
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"transaction_id": "fi-tx-55c2",
"interval": 900
}Spørring mot /deferred til den er klar
POST /deferred HTTP/1.1
Host: issuer.fleetinspect.example
Content-Type: application/json
Authorization: Bearer fi-at-3d91e0
{ "transaction_id": "fi-tx-55c2" }
// While the review is open: 202 with the same transaction_id and interval.
// Once approved: 200 with the credentials, optionally with a notification_id.
// If the review rejects the result: a credential_request_denied error.Lukke sløyfen: notification endpoint
Etter utstedelse kan wallet fortelle issueren hva som skjedde med legitimasjonen, ved å bruke notification_id fra legitimasjonssvaret. Wallet-er er ikke pålagt å sende disse varslene, og levering er ikke garantert, så en issuer kan ikke tolke et manglende varsel som noe som helst.
Wallet
Sender en hendelse til issuerens notification_endpoint for den mottatte notification_id-en, som dekker alle legitimasjoner i det svaret
Lagret i wallet
Utstedelsen feilet av en annen grunn, for eksempel at legitimasjonen ikke besto validering
Utstedelsen feilet på grunn av innehaveren, for eksempel at de avslo å lagre den
POST /notify HTTP/1.1
Host: issuer.fleetinspect.example
Content-Type: application/json
Authorization: Bearer fi-at-3d91e0
{
"notification_id": "fi-notif-9012",
"event": "credential_accepted"
}Relaterte begreper
Ofte stilte spørsmål
Hvordan vet en wallet hvilken grant-type som skal brukes?
Credential Offer angir grant-en i sitt grants-objekt. authorization_code er til stede når issueren vil at innehaveren skal logge inn som del av flyten. pre-authorized_code er til stede når innehaveren allerede var autentisert på kanalen der tilbudet ble opprettet, for eksempel av kontrolløren på kontrollstedet i eksempelet under. Et tilbud kan liste opp begge, og wallet velger da en av dem. Hvis tilbudet ikke har noe grants-objekt i det hele tatt, slår wallet opp hvilke grant-typer authorization server støtter i sine metadata.
Hvorfor henter wallet issuerens metadata før den ber om noe som helst?
Credential Offer angir kun credential_configuration_ids, issuerens URL og grants. Issuerens metadata, som serveres fra en well-known-sti, beskriver hver konfigurasjon: dens format, dens claims og hvilke bevistyper den godtar, i tillegg til nonce-, credential-, deferred- og notification-endepunktene. Den angir også hvilken authorization server som skal brukes, og denne serverens egne metadata gir token endpoint. Uten begge deler ville ikke wallet vite hvordan den skal bygge gyldige forespørsler eller hva den skal vise innehaveren før samtykke.
Hva beviser bevis på nøkkelbesittelse egentlig?
Det beviser at walleten som ber om legitimasjonen, innehar den private nøkkelen legitimasjonen skal bindes til, ikke bare at den har et gyldig access token. Wallet signerer en proof-JWT over issuer-identifikatoren og en fersk c_nonce fra issuerens nonce endpoint, med denne nøkkelen. Issueren legger den tilhørende offentlige nøkkelen inn i legitimasjonen. En verifier som krever nøkkelbinding ber innehaveren signere med samme nøkkel på nytt ved fremvisning, slik at en kopiert legitimasjon uten nøkkelen ikke består den kontrollen.
Hvorfor skulle en issuer utsette utstedelsen i stedet for å returnere legitimasjonen med en gang?
Enkelte kontroller issueren utfører før en legitimasjon utstedes, kan ikke fullføres innenfor en enkelt HTTP-forespørsel, for eksempel en manuell gjennomgang eller et kall til et tregt eksternt register. Utsatt utstedelse lar credential endpoint svare umiddelbart med en transaction_id i stedet for å blokkere forbindelsen, og wallet spør deferred endpoint med denne id-en helt til kontrollen er ferdig og legitimasjonen er klar til henting.
Er notification endpoint obligatorisk for en issuer å implementere?
Nei. Det er valgfritt for issuere, og wallet-er er heller ikke pålagt å bruke det. Når begge støtter det, får issueren vite hva som skjedde etter utstedelsen: legitimasjonene ble lagret (credential_accepted), innehaveren stoppet utstedelsen, for eksempel ved å avslå å lagre dem (credential_deleted), eller den feilet av en annen grunn (credential_failure). Levering er ikke garantert, så en issuer bør behandle et varsel som nyttig informasjon, aldri som en pålitelig oversikt, og kan ikke lese noe inn i et manglende varsel.
Kilder
Denne siden er kun til informasjon og utgjør ikke juridisk rådgivning. For autoritativ veiledning, kontakt OpenID Foundation og Europakommisjonen direkte.