This document outlines the current development status of the EUDIW Demo authentication functionalities, the current technical choices, limitations and future development. Readers are advised to note, that project is under heavy development and technical decisions may change during implementation.
Standard | Acronym(s) | Version | Reference |
---|---|---|---|
OpenID for Verifiable Presentations | OpenID4VP | draft 20 | https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html |
Presentation Exchange | 2.0.0 | https://identity.foundation/presentation-exchange/spec/v2.0.0/ | |
Did:web method | https://w3c-ccg.github.io/did-method-web/ | ||
JWT Secured Authorization Response Mode for OAuth 2.0 | JARM | https://openid.net/specs/oauth-v2-jarm-final.html | |
Selective Disclosure for JWTs | SD-JWT | draft 05 | https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-05.html |
Self-Issued OpenID Provider v2 | SIOPv2 | Not supported | https://openid.net/specs/openid-connect-self-issued-v2-1_0-12.html |
This section provides an overview of the EUDIW Demo implementation profile, detailing the technical choices that have been selected and incorporated into the implementation and addresses the optional requirements specified in the OpenID4VP specification
authorization_endpoint
is openid4vp://
client_id_scheme
MUST be used in Wallet Invocation URL and Authorization Request (JAR)client_id_scheme
value MUST be did
client_id
value MUST be a valid did:web identifierrequest_uri
parameter as defined in JWT-Secured Authorization Request (JAR)request
parameter is not supportedkid
parameterclient_metadata
MUST be defined in JARclient_metadata_uri
parameter is not supportedclient_metadata.jwks
MUST be definedjwks_uri
is not supportedclient_metadata.vp_formats
MUST be compatible with credential formats that EUDIW Demo supportsresponse_type
MUST be vp_token
id_token
is not supportedpresentation_definition
parameter MUST be used to send the Presentation Definition presentation_definition_uri
parameter is not supportedscope
parameter is not supportedresponse_mode
value MUST be direct_post.jwt
response_mode
values are not supportedresponse_uri
parameter MUST be usedredirect_uri
parameter MUST NOT be used in Authorization Request with response_mode
direct_post.jwt
redirect_uri
from Response Endpoint (response_uri
)Trust model is incomplete and currently not aligned with ARF.
In order to authenticate the requests made by the Verifier, EUDIW Demo requires the use of did client_id_scheme, with the additional requirement that the client_id MUST be a did:web identifier.
In order to have a did:web identifier, the Verifier should
In summary, when the client_id is resolved using read operation, it should produce a valid DID document.
https://example.com => did:web:example.com https://example.com/auth => did:web:example.com:auth |
did:web:example.com => https://example.com/.well-known/did.json did:web:example.com:auth => https://example.com/auth/did.json |
{ "id": "<value of client_id parameter, example did:web:example.com>", "verificationMethod": [ { "id": "<DID URI that identifies this verificationMethod, the fragment is a JWK Thumbprint of the key in publicKeyJwk, example did:web:example.com#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A>", "type": "JsonWebKey2020", "controller": "<DID identifier of the DID document, example did:web:example.com>", "publicKeyJwk": { "kid": "<JWK Thumbprint of this key, example _Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A>", "kty": "EC", "crv": "P-384", "x": "<base64url-encoded public key x-coordinate>", "y": "<base64url-encoded public key y-coordinate>", "x5c": ["<RP's X.509 certificate>"] } } ], "authentication": [ "id": "<DID fragment that identifies a verificationMethod, example did:web:example.com#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A>" ] } |
Verifier should have a HTTPS endpoint that provides JWT-Secured Authorization Requests (JAR) using HTTP GET method.
See JWT-SecuredAuthorizationRequest(JAR).
Verifier should have a HTTPS endpoint that receives & processes Authorization Response (or Error Response) in JWT-Secured Authorization Response Mode using HTTP POST.
Authorization Request data model consists of two parts:
Verifier invokes the EUDIW Demo app by passing client_id (to fetch DID document) and request_uri (to fetch the JAR) using custom URL scheme.
Wallet Invocation is presented in URI-form containing following information:
openid4vp://?client_id=did%3Aweb%3Aexample.com&client_id_scheme=did&request_uri=https%3A%2F%2Fexample.com%2Fauth%2Frequest%2F123 |
Verifier can invoke the app by
Wallet resolves JWT-Secured Authorization Request (JAR) by performing a HTTP GET to request_uri.
JAR is a JWT containing the actual Authorization Request data in the payload.
EUDIW Demo application interprets the client_id in Wallet Invocation as a did:web identifier and then performs a read operation to resolve Verifier's DID document. If DID document resolution fails, Wallet Invocation is rejected.
Once the DID document has been resolved, EUDIW Demo application fetches JAR from the URI presented by request_uri of the Wallet Invocation. JAR must be cryptographically signed with private key that corresponds the public key in DID document (publicKeyJwk) and this signature must be verified successfully by the EUDIW Demo application. The key identifier (kid) in JAR header must be set to a key identifier that is found in the did.json for authentication verification relationship.
If publicKeyJwk cannot be located via authentication verification relationship by using kid, or signature cannot be verified, EUDIW Demo rejects the JAR and authentication flow is interrupted.
{ // JWS Protected Header "alg": "<algorithm that is listed in ASM.request_object_signing_alg_values_supported array and which matches with the key referenced in JAR.header.kid>", "kid": "<key identifier that maps to DID document's authentication[] → verificationMethod[].id relation, example did:web:example.com#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A>" } . { // JWS Payload "aud": "https://self-issued.me/v2", "nbf": "<timestamp now>", "exp": "<timestamp now + 5 minutes>", "client_id": "<did:web identifier, must match client_id in Wallet Invocation, example did:web:example.com>", "client_id_scheme": "did", "client_metadata": { "jwks": { "keys": [ { // Ephemeral public EC key for encryption of JARM response. "kty": "EC", "use": "keyAgreement", "kid": "<UUIDv4>", "crv": "<P-521 or P-384>", "x": "<base64url-encoded public key x-coordinate>", "y": "<base64url-encoded public key y-coordinate>" } ] }, "vp_formats": { "sd_jwt": { "alg": ["ES384"] }, "kb_jwt": { "alg": ["ES256"] } } }, "presentation_definition": <Presentation Definition object as defined in Presentation Exchange specification>, "response_type": "vp_token", "response_mode": "direct_post.jwt", "response_uri": "<HTTPS URL, example https://example.com/auth/response>", "nonce": "<single use cryptographically secure random value>", "state": "<rp-defined value for optional state management>" } . <RP's signature (JWS Signature)> |
EUDIW Demo solution extends the vp_formats in client_metadata with it's own claim format designation for sd_jwt & kb_jwt:
See WalletMetadata(AuthorizationServerMetadata) for static wallet metadata describing all supported formats & parameter values.
JAR.payload.presentation_definition contains Presentation Definition. Presentation Definition is specified by Presentation Exchange.
Although OpenID4VP describes multiple mechanisms for delivering the Presentation Definition, EUDIW Demo requires the Presentation Definition to be passed by value in the JAR.
EUDIW Demo solution extends the vp_formats with it's own claim format designation for sd_jwt.
{ "id": "<rp-defined value>", "format": { "sd_jwt": { "alg": ["ES384"] } }, "input_descriptors": [ { "id": "<rp-defined value>", "constraints": { "fields": [ { "path": [ "$.credentialSubject.<attribute_name>" ], "filter": <optional JSON Schema object> } ] } } ] } |
For successful selective disclosure, each requested Test PID attribute should be specified within it's own Input Descriptor Object.
Currently, response_mode direct_post.jwt (https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.3.1) is the only supported response_mode.
EUDIW Demo implements JARM (see below) in encrypted but not signed mode (https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.3-3).
Thus, when EUDIW Demo delivers Authorization Response or Error Response, it's sent as JWE, as specified in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.3.1.
POST /post HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded response=<JARM> state=<value of JAR.payload.state, if present in JAR> |
EUDIW Demo uses JARM and sends Authorization Responses using "encrypted but not signed" method described in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.3-3.
Holder Key-Binding can be verified through SD-JWT in vp_token, so additional signatures in JARM are not used.
Authorization Response is encrypted as JWE using
Verifier encryption keys are obtained from JAR.payload.client_metadata.jwks.
EUDIW Demo uses ephemeral key pair & it's public key is included in the JWE header's epk field. Verifier's public key that was used in encryption is identified by the key identifier (kid) in the JWE header.
{ // JWE Protected Header "alg": "ECDH-ES", "enc": "A256CBC-HS512", "epk": { // Ephemeral public key in JWK format as defined in RFC 7518 JWA section 4.6.1.1. "kty": "EC", "crv": "<EC curve value of the key that JARM.header.kid points to>", "x": "<base64url-encoded public key x-coordinate>", "y": "<base64url-encoded public key y-coordinate>" }, "kid": "<kid of the ephemeral public EC key that was selected from JAR.payload.client_metadata.jwks.keys>" } . // Normally this section would be the JWE Encrypted Key. However, because `"alg": "ECDH-ES"` the section is left as an empty string (so-called empty octet sequence). . <JWE Initialization Vector> . <encrypted payload (JWE Ciphertext)> . <JWE Authentication Tag> |
Once the JWE is decrypted using the private key corresponding to the kid header, the decrypted data contains the Authorization Response parameters as in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#name-response-parameters.
If the user approved the release of credentials, JWE payload contains Authorization Response parameters as specified in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.3 for encrypted but not signed Authorization Response:
{ "vp_token": <vp token>, "presentation_submission": <presentation submission>, "state": <value of JAR.payload.state, if present in JAR>, } |
If there was an error processing the request, or user cancelled, JWE payload contains Error Response parameters, as in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.4:
{ "error": <error code>, "error_description": <description provided by EUDIW Demo app>, "state": <value of JAR.payload.state, if present in JAR> } |
As seen in above examples, EUDIW Demo also adds the state parameter, if it was provided by the Verifier in JAR.
vp_token in response object contains the presented credentials in SD-JWT presentation format.
SD-JWT is specified by Selective Disclosure for JWTs (SD-JWT).
<base64url-encoded JWS Protected Header of JWT> . <base64url-encoded JWS Payload of JWT> . <base64url-encoded JWS Signature of JWT> ~ <base64url-encoded disclosure 1> ~ <base64url-encoded disclosure 2> ~ <base64url-encoded disclosure 3> // The number of disclosures varies from one to many. ~ <base64url-encoded JWS Protected Header of KB-JWT> . <base64url-encoded JWS Payload of KB-JWT> . <base64url-encoded JWS Signature of KB-JWT> |
Verification and processing of SD-JWT must be done as speciefied in: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-05.html#name-verification-and-processing. Signing certificate is received in x5c header and must be used to verify SD-JWT signature. Signing certificate itself must be verified using CA certificate found from the JWKS endpoint.
presentation_submission contains a Presentation Submission. Presentation Submission is specified by Presentation Exchange.
{ "definition_id": "<value of JAR.payload.presentation_definition.id>", "id": "<wallet-defined value>", "descriptor_map": [ { "id": "<value of JAR.payload.presentation_definition.id>", "format": "sd_jwt", "path": "$" } ] } |
In order to mitigate Session Fixation attacks, the Verifier is strongly recommended to implement a Session Fixation mitigation and returning a redirect_uri from the Response Endpoint (response_uri), as specified in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.2-21.
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 { "redirect_uri": "https://example.com/redirect#response_code=<Verifier defined response_code, used by Verier's front-end>" } |
Upon receiving a redirect_uri from response_uri, EUDIW Demo will prompt the user to continue interaction with the Verifier by letting the user to confirm navigation to redirect_uri. EUDIW Demo uses operating system's APIs to open the redirect_uri in a browser & does not interpret any parameters in the response_uri. It's the responsibility of the Verifier to implement the Session Fixation mitigation according to the best practices. See https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-12.2 for further details about Session Fixation attack & mitigation.
Using redirect_uri Authorization Request parameter is not allowed, when response_mode is direct_post.jwt. Upon receiving such request, EUDIW Demo will respond with invalid_request Authorization Error, as described in https://openid.net/specs/openid-4-verifiable-presentations-1_0-20.html#section-6.2-7.2.
Sequence diagram below describes the cross-device flow between a browser and EUDIW Demo app on another device.
Same sequence should work when Browser performs URI redirection to EUDIW Demo app on same device.
# | Step | Example | |
---|---|---|---|
1 | User initiates authentication sequence from the Verifier web site. | ||
2 | Verifier starts the authentication and creates:
| Example Wallet Invocation URL | |
3 | Verifier forwards the authentication request for EUDIW Demo application by
| ||
4 | EUDIW Demo application receives the Wallet Invocation
| ||
5 | EUDIW Demo application receives the Wallet Invocation and reads it's parameters | ||
6 | EUDIW Demo application performs did:web read -operation for the client_id -parameter and receives the DID document from the Verifier. | client_id: did:web:example.com => GET https://example.com/.well-known/did.json | |
7 | EUDIW Demo application validates the DID document. The id of the DID document must correspond with the client_id of the Wallet Invocation. | ||
8 | EUDIW Demo application sends HTTP GET -request to URI addressed by the request_uri to fetch a JWT-Secured Authorization Request (JAR). Verifier responds with JWT-Secured Authorization Request (JAR). | Example JWT-Secured Authorization Request (JAR) | |
9 | EUDIW Demo application verifies JAR signature. | ||
10 | EUDIW Demo application presents authentication request with details and Verifier info to user. User accepts the authentication request. | ||
11 | EUDIW Demo application evaluates presentation_definition and creates a presentation of user's credentials to form a vp_token. If presentation_definition is unsatisfiable, authentication flow ends in an error indicating that the credentials are insufficient. | ||
12 | EUDIW Demo application presents requested credentials to user. User accepts sending the credentials. | ||
13 | EUDIW Demo application creates
EUDIW Demo application creates Authorization Response (JARM payload):
| Example Authorization Response | |
14 | EUDIW Demo application creates the encrypted Authorization Response (JWE). | Example JWE | |
15 | EUDIW Demo application sends authentication response to Verifier as defined in JAR.payload.response_mode. Currently, only direct_post.jwt is supported, so response is sent as HTTPS POST to response_uri. | ||
16 | Verifier validates the received response. | ||
17 | Verifier responds with redirect_uri containing a response_code generated by the verifier. | ||
18 | EUDIW Demo application opens the received redirect_uri in a new browser session. | ||
19 | Redirect_uri opened in the browser contains a confirmation request. User is requested to verify the session to mitigate possible session fixation attacks. | ||
20 | After receiving user confirmation, the response_code is posted from the browser to the verifier. | ||
21 | Verifier validates the received response_code. | ||
22 | Verifier performs custom authentication functionalities as needed. |
Verifier can determine the supported formats & other configuration using the static metadata provided below.
Configuration values (and their definitions) are as follows:
authorization_endpoint
: "openid4vp://" (Custom URL Scheme)client_id_schemes_supported
: an array containing supported client identifier schemesrequest_object_signing_alg_values_supported
: an array containing supported algorithms for signing JARresponse_types_supported
: an array containing supported values for JAR.payload.response_typeresponse_modes_supported
: an array containing supported values for JAR.payload.response_modevp_formats_supported
: an object containing supported fields and values for JAR.payload.client_metadata.vp_formatsvp_formats_supported.sd_jwt.alg_values_supported
: an array containing supported algorithms for signing SD-JWTvp_formats_supported.kb_jwt.alg_values_supported
: an array containing supported algorithms for signing KB-JWTauthorization_encryption_alg_values_supported
: an array containing the method to generate key for decrypting JARM contained by the authentication responseauthorization_encryption_enc_values_supported
: an array containing the algorithm used to encrypt JARM contained by the authentication response (corresponds to the value provided in the enc-header)Static configuration in ASM format (OAuth 2.0 Authorization Server Metadata) with extensions based on OpenID4VP standard (8.1. Additional Wallet Metadata parameters) and JARM standard (4. Authorization Server Metadata).
{ "authorization_endpoint": "openid4vp://", "client_id_schemes_supported": [ "did" ], "request_object_signing_alg_values_supported": [ "ES384", "ES256", "PS512", "PS384", "PS256" ], "response_types_supported": [ "vp_token", ], "response_modes_supported": [ "direct_post.jwt" ], "scopes_supported": [ "openid" // unused, reserved for SIOPv2 ], "presentation_definition_uri_supported": false, "vp_formats_supported": { "sd_jwt": { "alg_values_supported": [ "ES384" ] }, "kb_jwt": { "alg_values_supported": [ "ES256" ] } }, "authorization_signing_alg_values_supported": [], // JARM is not signed. It is only encrypted. "authorization_encryption_alg_values_supported": [ "ECDH-ES" ], "authorization_encryption_enc_values_supported": [ "A256CBC-HS512" ] } |
Version | Date | Changes |
---|---|---|
1 | 2023-10-18 | First published version |
2 | 2024-04-05 | Document updated to follow OpenID4VP draft 20 implementation. Notable changes in implementation:
|