Attestation Verification
Attestation Verification as defined in the Architecture Reference Framework (ARF) is based on the OpenID for Verifiable Presentations (OID4VP (opens in a new tab)) protocol. This page outlines the specific flows that need to be supported for Attestation Verification within Company Passport.
OpenID for Verifiable Presentations
Attestations presented in an online scenario MUST be presented using the OID4VP (opens in a new tab) protocol. In addition implementations MUST support the Batch Credential Endpoint. An Issuer MAY support deferred credential issuance, which MUST be supported by the Wallet. Both the Issuer and Wallet MUST support the Notification Endpoint for updates from the Wallet to the Issuer.
The Organizational Wallet can act as both Verifier and Wallet.
Attestation Formats
Both the Verifier and Wallet MUST support the vc+sd-jwt
(for the SD-JWT VC Attestation Format) and mso_mdoc
(for the ISO/IEC 18013-5 mDL Attestation Format) formats as defined in Appendix A of OID4VP (opens in a new tab). Other attestation formats MAY be supported. See Attestation Formats for requirements around supported Attestation Formats.
High Assurance
For additional security, requirements as defined in Section 5. of High Assurance Interoperability Profile (HAIP) (opens in a new tab) MUST be supported in Company Passport solutions. In addition to the client_id_scheme
as defined in HAIP, Company Passport Solutions can also use client_id_scheme
value did
for using a Decentralized Identifier.
Support might be added for the entity_id
as client_id_scheme
value for integration with OpenID Federation as
Trust Management
Cross Device Flow
Cross Device Flow in-depth
- User browses to Relying Party (RP) website
- Browser on the user's device opens the Resource server/website associated with the RP on a device different from its wallet
- The RS/website asks the RP component via its management API to generate a new Authorization Request URI
- The RP generates a new
state
value which for instance can be used as a browser session using a secure HTTP only cookie, referred to as correlation id. - RP generates an ephemeral key pair to be used for key agreement for response encryption (not supported yet)
- RP generates an OpenID4VP Authorization Request, binding it internally to the
state
and stores - RP makes sure the Request is accessible using the unique
request_uri
( e.g.,https://rp.example.com/oid4vp/requests/abcde-abcde-1234
);- The request is bound to the user's browser session
- It is signed using a key bound to the RP's metadata that can be retrieved using the RP's client_id
- It contains the presentation_definition containing the information about the requested (Q)EAAs
- It contains RP's nonce and state
- It contains the purpose given by the RP
- RP returns the Request to the RS/RP website
- Rs/RP website generate a QR code from the Authorization request
- Rs/RP website returns a HTML page to the browser containing a QR-Code to the wallet app (
e.g.,
openid4vp://authorize?client_id=..&request_uri=https://rp.example.com/oidc/request/1234
). It also sets a Cookie with thestate
value. - The user launches his wallet and hits a button to scan QR-Code from the RP's website.
- The wallet scans the QR code
- The Wallet starts the OID4VP flow
- The wallet decodes the Authorization Request from the QR-Code and extracts request_uri and client_id
- The wallet optionally evaluates the trust associated with the client_id of the RP (optional)
- The wallet app retrieves the Authorization Request Object from the RP website (
e.g.,
https://rp.example.com/oid4vp/requests/abcde-abcde-1234
) Example signed JWT whenclient_id_scheme
isdid
. This is a vp_token only, meaning only presentation definition and thus Verifiable Presentation is expected
header:
{
"typ": "oauth-authz-req+jwt",
"alg": "RS256",
"kid": "did:example:123#1"
}
body/payload:
{
"client_id": "did:example:123",
"client_id_scheme": "did",
"response_types": "vp_token",
"redirect_uri": "https://relying-party.acme.org/callback",
"nonce": "n-0S6_WzA2Mj",
"presentation_definition": "..see PE chapter for examples..",
"client_metadata": {
"vp_formats": {
"jwt_vp_json": {
"alg": ["ES256", "ES256K"]
},
"mso_mdoc": {
"proof_type": ["ES256"]
}
}
}
}
See also: Presentation Exchange explanation
- The RP website return the signed Authorization Request Object JWT
- The wallet validates the Authorization Request signature (JWT) using the RP's public key
- Was the signature valid and the key bound to the RP's metadata? This ensures that the Authorization Request was not tampered with;
- The wallet validates the Presentation Exchange's Presentation Definition to be valid (presentation_definition)
- The wallet optionally filters out any Verifiable Credentials that cannot satisfy the presentation_definition
- The wallet notifies the user in case credentials are missing. Or to be more precise, in case presentation_definition input descriptors cannot be satisfied.
- In the future a trust framework could would list all issuers and their credential types, allowing for a nicer UX, by redirecting the user to (Q)EAA Providers in case credentials are missing.
- Consent: The Wallet displays information about the identity of the Relying Party and the purpose, the user gives consent to present the (Q)EAA.
- JSON-LD W3C (Q)EAA The wallet generates a W3C Verifiable Presentation containing one or more proofs using wallet/device private key(s)
- (SD-JWT (Q)EAA) The Wallet generates a KB-JWT by signing over nonce and audience with device private keyv
- (SD-JWT (Q)EAA) The Wallet creates the presentation according to presentation_definition by cutting out the unnecessary Disclosures and appending the KB-JWT
- (Mdoc (Q)EAA) The Wallet generates an Mdoc deviceAuth by signing the SessionTranscript with device private key
- (Mdoc (Q)EAA) The Wallet creates the presentation according to presentation_definition by cutting out the unnecessary Releases assembling the issuerAuth with deviceAuth
- JWT W3C VP The wallet generates a W3C VCDM 1.1 JWT encoded Verifiable Presentation containing one or more proofs using wallet/device private key(s)
- The wallet asks the user to authenticate, for instance using pin, face id or fingerprint
- The wallet creates a VP token and a presentation submission, describing the paths from the definition to the VC(s) contained in the VP.
- The wallet sends the VP token and presentation submission to the RP (encrypted to the RP's ephemeral public key, not yet supported).
- The RP optionally decrypts the Authorization Response with its ephemeral public key created during the Authorization Request.
- The RP matches the browser session with the state parameter.
- The RP verifies the content of the vp_token. verifies its signatures, including these of credentials contained in presentation(s)
- The RP verifies the submission_data and vp_token against its definition
- The RP responds with HTTP 200 and acknowledges the received vp_token.
- The browser refreshes the RP's website, either by polling regularly or by receiving a call from the RP, e.g. through websockets or webhooks.
- The RP matches the session_id from the Cookie
- matches this with the verification result.
- Returns the result to the browser or RS
- The browser continues the UX flow.
DIF Presentation Exchange in-depth
Whenever the response_type
of the Request Object
contains the value vp_token
, code
and
a presentation_definition
is present, the OP/Wallet is expected to return a so called VP Token
, meaning a Verifiable
Presentation in the Authorization Response
(vp_token
) or Token Response
(code
) with presentation_submission
,
binding the Presentation to the definition. Examples will be provided below.
Example JWT (Q)EAA
Below is an example (Q)EAA in JWT format, explaining how the JWT encoded (Q)EAA is being mapped onto the W3C Verifiable Credential Datamodel 1.1 (VCDM). It is important to understand that a JWT (Q)EAA is just a matter of encoding/decoding a 'regular' W3C Verifiable Credential
{
"iss": "https://example.gov/issuers/565049", <-- Maps onto the issuer value of the W3C VDM
"nbf": 1262304000, <-- Maps onto the issuance data of the W3C VDM
"jti": "http://example.gov/credentials/3732", <-- Unique value, maps onto the credential Id of the W3C VDM
"sub": "did:example:ebfeb1f712ebc6f1c276e12ec21", <-- maps onto the credentialSubject id of the W3C VDM, notice no id is present below,
"vc": { <-- maps onto the W3C VCDM Verifiable Credential. The rest below is standard W3C VCDM,
"crdentialSchema": {
"id": "https://eu.com/claims/IDCredential.json"
},
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"type": [
"VerifiableCredential",
"IDCredential"
],
"credentialSubject": {
"given_name": "Max",
"family_name": "Mustermann",
"birthdate": "1998-01-11",
"address": {
"street_address": "Sandanger 25",
"locality": "Musterstadt",
"postal_code": "123456",
"country": "DE"
}
}
}
}
Example Presentation Definitions
As you can see in the below examples the format of the actual credential has an influence on the definition. Both in terms of paths, formats and constraints
The global structure however is the same, and follows the DIF Presentation
Exchange v2.0 specification (opens in a new tab) (PE)
The input_descriptor
array is mandatory. A wallet is supposed to satisfy every single input descriptor from the
definition, unless specific constraints dictate otherwise.
Please be aware that the below are very minimal examples of what PE is capable of.
Things like defining which claims should be used in selectively disclosed credentials, including whether the selective
disclosure is mandatory or optional are for instance possible.
It is also possible to add constraints to an input descriptor, allowing for instance to assign one or more groups to the
descriptor. The in a so called submission_requirements
object you can have rules, like a pick
rule where you define
that for instance 1 or 2 descriptors should be satisfied from a certain group, or for instance a min and max amount of
descriptors
W3C Verifiable Credential Data Model 1.1 JWT Credential
The below example, expects a JWT VP signed using ES256
with a single JWT VC signed using either ES256
or ES256K
.
It also expect the type to be IDCredential
exactly and requires a schema from 2 different locations.
{
"id": "example_jwt_vc, a unique ID value for the whole definition",
"name": "An optional human friendly readable name, to be displayed by a wallet typically",
"purpose": "An optional string to be displayed by the wallet, describing to the user that the purpose of the RPs data request is",
"input_descriptors": [
{
"id": "id_credential", <-- A Unique identifier in the scope of the definition for this input descriptor
"format": {
"jwt_vc_json": {
"proof_type": [ <-- VC signature suites supported for a JWT VP not using Linked Data Processing (@context)
"ES256K",
"ES256"
]
},
"jwt_vp_json": {
"proof_type": [
"ES256" <-- VP signature suite supported for a JWT VP not using Linked Data Processing (@context)
]
}
},
"constraints": {
"fields": [
{
"path": [
"$.vc.type" <-- Search for this path in the credential, using JSONPath
],
"filter": { <-- Can be any JSON schema filter, so partial searches using regexes, matching against constants, singular values as well as arrays
"type": "array",
"contains": {
"const": "IDCredential" <-- The VC path vc.type should be an array containing the string literal IDCredential with an exact match
}
}
},
{
"path": ["$.vc.credentialSchema.id"],
"filter": {
"type": "string",
"pattern": ["https://eu.com/claims/IDCredential.json", "https://us.com/example/US-IDCredential.json"]
}
}
]
}
}
]
}
ISO mobile Driving License (mDL)
{
"id": "mDL-example, a unique ID value for the whole definition",
"name": "An optional human friendly readable name, to be displayed by a wallet typically",
"purpose": "An optional string to be displayed by the wallet, describing to the user that the purpose of the RPs data request is",
"input_descriptors":[
{
"id":"org.iso.18013.5.1.mDL", <-- namespace identifier, according to ISO spec
"format":{
"mso_mdoc":{
"alg":[
"EdDSA",
"ES256"
]
}
},
"constraints":{
"limit_disclosure":"required", <-- Do not accept mDoc disclosures containing more claims than listed below
"fields":[
{
"path":[
"$['org.iso.18013.5.1']['family_name']"
],
"intent_to_retain":false <-- The RP is signaling it will not store these values long term, only used during the actial interaction
},
{
"path":[
"$['org.iso.18013.5.1']['portrait']"
],
"intent_to_retain":false
},
{
"path":[
"$['org.iso.18013.5.1']['driving_privileges']"
],
"intent_to_retain":false
},
{
"path":[
"$['domestic_namespace']['domestic_data_element_id']"
],
"intent_to_retain":false
}
]
}
}
]
}
Presentation Submission
A descriptor called the presentation_submission
needs to be provided in the Response to the RP. This descriptor is
being created by the Wallet and User. The Wallet will use a PE library to filter out any non-conforming (Q)EAAs. If the
Presentation Definition cannot be met, the user will be warned about it. If it can be met, the user will have to
consent. In case multiple (Q)EAAs met a single criteria (input_descriptor) of the definition, the user should choose
which (Q)EAA(s) to use.
The PE library then will generate the presentation_submission
, making a link between the definition input_descriptor
ids and the JSONPaths of the (Q)EAAs in the Presentation. This ensures the RP can check the Presentation without any
ambiguity.
Example presentation submission:
{
"id": "Presentation example 1", <-- Unique value in scope of the holder/wallet
"definition_id": "example_jwt_vc", <-- Matches the definition Id (see earlier)
"descriptor_map": [
{
"id": "id_credential", <-- matches the ID of the descriptor from the definition
"format": "jwt_vp", <-- Format used for the presentation
"path": "$", <-- Since this is a nested descriptor where the outer part is the presentation at the toplevel, we use $ (toplevel)
"path_nested": { <-- We need to point to the VC/(Q)EAA within the presentation
"format": "jwt_vc", <-- format of the VC
"path": "$.vp.verifiableCredential[0]" <-- Path to the (Q)EAA, relative to the path of the parent ($)
}
}
]
}