Hopp til hovedinnhold

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.

Tilbud opprettet
QR-kode eller lenke åpner wallet
Tilbud mottatt
grant løst inn
Token mottatt
nonce og bevis på nøkkelbesittelse
Legitimasjon forespurt
legitimasjon utstedt
Legitimasjon utstedt
wallet validerer og lagrer
Legitimasjon akseptert
Hvor det kan feile, i rekkefølgen trinnene kjøres:
Koden utløp før innløsning
Token-forespørsel avvist
Legitimasjonsforespørsel avvist
Legitimasjon ikke lagret

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 JWT
  • alg: signeringsalgoritmen, en av dem issueren listet i proof_signing_alg_values_supported
  • jwk: 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 issuer
  • iat: tidspunktet beviset ble opprettet, i sekunder siden 1970
  • nonce: 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

  1. Credential Issuer til Wallet: Credential Offer, vist som QR-kode
  2. Credential Issuer til Wallet: tx_code, sendt til flåtesjefens telefon per SMS
  3. Wallet til Credential Issuer: GET /.well-known/openid-credential-issuer
  4. Credential Issuer til Wallet: metadata for credential issuer
  5. Wallet til Authorization Server: GET /.well-known/oauth-authorization-server
  6. Authorization Server til Wallet: metadata for authorization server
  7. Wallet til Authorization Server: POST /token: pre-authorized_code, tx_code
  8. Authorization Server til Wallet: access_token
  9. Wallet til Credential Issuer: POST /nonce
  10. Credential Issuer til Wallet: c_nonce
  11. Wallet: signerer proof-JWT
  12. Wallet til Credential Issuer: POST /credential: access token, bevis
  13. Credential Issuer til Wallet: credentials, notification_id
  14. Wallet: validerer og lagrer
  15. Wallet til Credential Issuer: POST /notify: credential_accepted
  16. 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.

transaction_idspør deferred_credential_endpoint

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

credential_accepted

Lagret i wallet

credential_failure

Utstedelsen feilet av en annen grunn, for eksempel at legitimasjonen ikke besto validering

credential_deleted

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

  1. OpenID for Verifiable Credential Issuance 1.0
  2. OpenID4VC High Assurance Interoperability Profile 1.0
  3. EUDI Wallet Architecture and Reference Framework

Denne siden er kun til informasjon og utgjør ikke juridisk rådgivning. For autoritativ veiledning, kontakt OpenID Foundation og Europakommisjonen direkte.

Snakk med oss om integrasjon med EUDI Wallet