OpenID4VCI-udstedelse af legitimation forklaret: fra tilbud til accepteret legitimation
OpenID4VCI, OpenID for Verifiable Credential Issuance, definerer, hvordan en wallet anmoder om og modtager en legitimation fra en issuer. En enkelt udstedelse gennemløber flere adskilte trin, før wallet reelt besidder en brugbar legitimation, og hvert trin har sin egen fejltype. Denne side gennemgår hele det forløb i detaljer, med originale gennemarbejdede eksempler.
To måder at starte på: forhåndsautoriseret kode og autorisationskode
Udstedelse starter ofte fra et Credential Offer sendt af issueren, og den grant, det angiver, afgør, hvordan wallet bliver autoriseret. En wallet kan også selv starte udstedelsen uden noget tilbud ved brug af autorisationskodeflowet. Begge flows ender samme sted: wallet besidder et access token, den kan bruge til at anmode om legitimationen.
Flow med forhåndsautoriseret kode
1. Issuer
Kender allerede indehaveren og udleverer et Credential Offer med en pre-authorized_code
2. Wallet
Indløser koden hos token endpoint, eventuelt sammen med en transaktionskode
3. Wallet
Anmoder om legitimationen med et bevis på besiddelse af sin nøgle
Flow med autorisationskode
1. Wallet
Scanner et Credential Offer, der angiver et authorization_code-grant, eller starter selv flowet uden et tilbud
2. Authorization Server
Fører indehaveren gennem login og samtykke og udsteder derefter en kode
3. Wallet
Bytter koden til et token og anmoder derefter om legitimationen
Hele livscyklussen for et legitimationstilbud
Specifikationen definerer ikke navngivne tilstande, men et issuer-initieret flow, hvor legitimationen udstedes med det samme, er nemmest at følge, som i sekvensen nedenfor. Hvert trin kan fejle på sin egen måde, og en wallet-implementering skal håndtere disse veje, ikke kun den ideelle.
Gennemarbejdet eksempel: et synscertifikat for en vognmandsflåde
En køretøjssynsvirksomhed udsteder et synscertifikat til et vognmandsfirmas business wallet efter et rutinesyn. Synsinspektøren har allerede autentificeret flådelederen på synsstedet, så issueren bruger flowet med forhåndsautoriseret kode. Trinnene nedenfor følger denne ene udstedelse fra tilbud til accepteret legitimation.
Eksemplet viser grundlæggende OpenID4VCI. Løsninger med høj sikringsgrad, som EUDI Wallet, følger HAIP-profilen oven på dette, som tilføjer DPoP-bundne access tokens, wallet attestation hos token endpoint og key attestation for legitimationens nøgler. Det er udeladt her for at holde hvert trin let at følge.
1. Legitimationstilbud
Synsvirksomhedens terminal viser en QR-kode. Den indeholder en URI, der starter med openid-credential-offer://, og som bærer tilbuddet nedenfor, URL-kodet i en credential_offer-parameter, eller en credential_offer_uri, som wallet henter det fra. Wallet scanner den og læser, hvilken legitimation der tilbydes, og hvordan den hentes.
Issuer
Viser en QR-kode med et Credential Offer
Angiver legitimationskonfigurationen 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. Opdagelse af issuerens metadata
Før wallet anmoder om noget som helst, henter den issuerens metadata for at finde ud af, hvad roadworthiness_certificate indeholder, og hvilke endpoints der skal kaldes. Metadataen lister ingen separate authorization servers, så issueren er sin egen authorization server, og wallet læser token endpoint fra den servers metadata.
Wallet
GET /.well-known/openid-credential-issuer
Lærer legitimationsformatet, claims og accepterede prooftyper, samt endpoints for nonce, credential, deferred og notification
Metadata for credential issuer (uddrag)
{
"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 (uddrag), 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-anmodning
Wallet indløser den forhåndsautoriserede kode hos token endpoint sammen med transaktionskoden, som synsvirksomheden sendte til flådelederens telefon. At sende koden over en anden kanal betyder, at en person, der fotograferer QR-koden over skulderen, stadig ikke kan indløse den.
Wallet
POST /token
Sender pre-authorized_code og tx_code, modtager et access token begrænset til dette tilbud
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øglebesiddelse
Fordi metadataen lister en nonce_endpoint, henter wallet først en frisk c_nonce der. Den beviser derefter, at den besidder den private nøgle, som legitimationen bliver bundet til, ved at signere en proof-JWT over issuer-identifikatoren og den c_nonce.
Wallet
POST /nonce, signerer derefter en proof-JWT med den nøgle, legitimationen bliver bundet til
Binder legitimationen til den nøgle, ikke blot til den, der besidder access token
POST /nonce HTTP/1.1 Host: issuer.fleetinspect.example
Svar
{
"c_nonce": "fi-nonce-77aa"
}Wallet bygger nu proof-JWT'en ud fra to JSON-objekter, en header og en payload, og signerer dem med den private nøgle, legitimationen bliver bundet til.
Header: hvad denne JWT er, og hvilken nøgle der signerede den
{
"typ": "openid4vci-proof+jwt",
"alg": "ES256",
"jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
}typ: markerer dette som en OpenID4VCI key proof, så den ikke kan forveksles med nogen anden slags JWTalg: signeringsalgoritmen, en af dem issueren angav i proof_signing_alg_values_supportedjwk: den offentlige nøgle, legitimationen bliver bundet til; issueren tjekker signaturen mod den
Payload: hvem beviset gælder for, og hvornår det blev lavet
{
"aud": "https://issuer.fleetinspect.example",
"iat": 1789376400,
"nonce": "fi-nonce-77aa"
}aud: issuer-identifikatoren, så beviset ikke kan genbruges hos en anden issueriat: tidspunktet, hvor beviset blev oprettet, i sekunder siden 1970nonce: c_nonce fra nonce endpoint, som viser, at beviset er frisk
Signeret resultat
Header og payload bliver hver base64url-kodet og sat sammen med et punktum. Wallet signerer den streng med sin private nøgle og tilføjer den base64url-kodede signatur efter et andet punktum. Den resulterende streng er den proof-JWT, wallet sender i legitimationsanmodningen i trin 5.
base64url(header) . base64url(payload) . base64url(signature) eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIs... .eyJhdWQiOiJodHRwczovL2lzc3Vlci5mbGVldGluc3BlY3QuZXhhbXBsZSIs... .<ES256 signature>
5. Legitimationsanmodning
Wallet kalder credential endpoint med access token og beviset, og issueren udsteder og returnerer den signerede legitimation.
Wallet
POST /credential
Sender access token, konfigurations-id'et og proof-JWT'en, modtager den signerede legitimation og et 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 forløbet på ét overblik
Dette sekvensdiagram samler de fem trin i det gennemarbejdede eksempel, fra tilbuddet til notifikationen. Massive pile er anmodninger, stiplede pile er svar, og den prikkede pil er transaktionskoden, der rejser uden for protokollen via sms.
Wallet
Vognmandsfirmaets business wallet
Authorization Server
Drives af issueren selv i dette eksempel
Credential Issuer
Køretøjssynsvirksomhed
- Credential Issuer til Wallet: Credential Offer, vist som QR-kode
- Credential Issuer til Wallet: tx_code, sendt til flådelederens telefon via 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, beviser
- Credential Issuer til Wallet: credentials, notification_id
- Wallet: validerer og gemmer
- Wallet til Credential Issuer: POST /notify: credential_accepted
- Credential Issuer til Wallet: 204 No Content
Når legitimationen ikke er klar endnu: udskudt udstedelse
Eksemplet ovenfor forudsætter, at synsresultatet allerede er endeligt. Hvis synsvirksomheden i stedet skal eskalere et tvivlsomt resultat til en seniorinspektør, kan credential endpoint ikke returnere legitimationen med det samme, så den udskyder udstedelsen.
Øjeblikkelig udstedelse
Credential endpoint returnerer den signerede legitimation i samme svar som anmodningen.
Udskudt udstedelse
Credential endpoint svarer i stedet med HTTP 202, et transaction_id og et interval. Wallet spørger deferred credential endpoint med dette id og venter mindst intervallets antal sekunder mellem hver anmodning, indtil legitimationen er klar.
Udskudt svar fra /credential
HTTP/1.1 202 Accepted
Content-Type: application/json
{
"transaction_id": "fi-tx-55c2",
"interval": 900
}Spørger /deferred, indtil 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.At lukke løkken: notification endpoint
Efter udstedelsen kan wallet fortælle issueren, hvad der skete med legitimationen, ved hjælp af notification_id fra legitimationssvaret. Wallets er ikke forpligtet til at sende disse notifikationer, og levering er ikke garanteret, så en issuer kan ikke tolke en manglende notifikation som noget som helst.
Wallet
Sender en hændelse til issuerens notification_endpoint for det modtagne notification_id, som dækker alle legitimationer i det svar
Gemt i wallet
Udstedelsen mislykkedes af en anden grund, for eksempel at legitimationen ikke bestod validering
Udstedelsen mislykkedes på grund af indehaveren, for eksempel fordi denne afslog at gemme 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"
}Relaterede begreber
Ofte stillede spørgsmål
Hvordan ved en wallet, hvilken grant-type der skal bruges?
Credential Offer angiver grant-typen i sit grants-objekt. authorization_code er til stede, når issueren vil have, at indehaveren logger ind som en del af flowet. pre-authorized_code er til stede, når indehaveren allerede var autentificeret på den kanal, hvor tilbuddet blev oprettet, for eksempel af synsinspektøren på synsstedet i det gennemarbejdede eksempel nedenfor. Et tilbud kan angive begge dele, og wallet vælger så en af dem. Hvis tilbuddet slet ikke har et grants-objekt, slår wallet op, hvilke grant-typer authorization server understøtter, i dens metadata.
Hvorfor henter wallet issuerens metadata, før den anmoder om noget som helst?
Credential Offer angiver kun credential_configuration_ids, issuerens URL og grants. Issuerens metadata, som leveres fra en well-known-sti, beskriver hver konfiguration: dens format, dens claims og de prooftyper den accepterer, samt endpoints for nonce, credential, deferred og notification. Den angiver også, hvilken authorization server der skal bruges, og den servers egne metadata giver token endpoint. Uden begge dele ville wallet ikke vide, hvordan den skal bygge gyldige anmodninger, eller hvad den skal vise indehaveren før samtykke.
Hvad beviser bevis på nøglebesiddelse egentlig?
Det beviser, at den wallet, der anmoder om legitimationen, besidder den private nøgle, som legitimationen bliver bundet til, ikke blot at den har et gyldigt access token. Wallet signerer en proof-JWT over issuer-identifikatoren og en frisk c_nonce fra issuerens nonce endpoint med den nøgle. Issueren indlejrer den tilsvarende offentlige nøgle i legitimationen. En verifier, der kræver nøglebinding, beder indehaveren om at signere med samme nøgle igen ved fremvisning, så en kopieret legitimation uden nøglen ikke består den kontrol.
Hvorfor skulle en issuer udskyde udstedelsen i stedet for at returnere legitimationen med det samme?
Nogle kontroller, som issueren udfører, før en legitimation udstedes, kan ikke gennemføres inden for en enkelt HTTP-anmodning, for eksempel en manuel gennemgang eller et opkald til et langsomt eksternt register. Udskudt udstedelse lader credential endpoint svare med det samme med et transaction_id i stedet for at blokere forbindelsen, og wallet spørger det udskudte endpoint med det id, indtil kontrollen er færdig, og legitimationen er klar til afhentning.
Er notification endpoint obligatorisk for en issuer at implementere?
Nej. Det er valgfrit for issuere, og wallets er heller ikke forpligtet til at bruge det. Når begge understøtter det, får issueren at vide, hvad der skete efter udstedelsen: legitimationerne blev gemt (credential_accepted), indehaveren stoppede udstedelsen, for eksempel ved at afslå at gemme dem (credential_deleted), eller den mislykkedes af en anden grund (credential_failure). Levering er ikke garanteret, så en issuer bør behandle en notifikation som nyttig information, aldrig som et pålideligt bevis, og kan ikke tolke noget ud fra en manglende notifikation.
Kilder
Denne side er udelukkende til orientering og udgør ikke juridisk rådgivning. Kontakt OpenID Foundation og Europa-Kommissionen direkte for autoritativ vejledning.