Skip to content

Latest commit

 

History

History
828 lines (714 loc) · 24.6 KB

ewc-rfc001-issue-verifiable-credential.md

File metadata and controls

828 lines (714 loc) · 24.6 KB

EWC RFC001: Issue Verifiable Credential - v1.0

Authors:

  • Mr George Padaytti (iGrant.io, Sweden)
  • Mr Lal Chandran (iGrant.io, Sweden)
  • Dr Andreas Abraham (ValidatedID, Spain)

Reviewers:

  • Dr Nikos Triantafyllou (University of the Aegean, Greece)
  • Mr Florin Coptil (Bosch, Germany)
  • Mr Matteo Mirabelli (Infocert, Italy)
  • Dr Mikael Linden (Vero, Finland)
  • Mr Renaud Murat (Archipels, France)
  • Mr. Sebastian Bickerle (Lissi ID, Germany)

Status: Approved for v1.0 release

Table of Contents

1.0 Summary

This specification implements OID4VCI workflow for any issuer as per reference specification [1]. This minimises risks towards interoperability across the European Wallet Ecosystem with a standard specification in the EUDI wallet ecosystem as per the ARF [2] requirements.

2.0 Motivation

The EWC LSP must align with the standard protocol for issuing credentials. This is the basis of interoperability between Issuers and Holders across the EWC LSPs. The assumption is that the user is familiar with the EWC-chosen protocols and standards and can refer to original standards references when necessary.

3.0 Messages

The credential issuance can be an authorisation flow or a pre-authorised one. These are depicted in the following diagrams, the assumption is that credential offer is obtained by holder wallet prior to discovery using same device (i.e. clicking on a link) or cross device (i.e scanning a QR code) flows.

  sequenceDiagram
    participant I as Individual using EUDI Wallet
    participant O as Organisational Wallet (Issuer)

    Note over I,O: Discovery issuer capabilities
    I->>O: GET: /.well-known/openid-credential-issuer
    O-->>I: OpenID credential issuer configuration
    I->>O: GET: /.well-known/oauth-authorization-server
    O-->>I: OAuth authorisation server metadata

    Note over I,O: Authenticate and Authorise
    I->>O: Authorisation request
    O-->>I: Authorisation response
    I->>O: Token request
    O-->>I: Token response

    Note over I,O: Issue credential
    I->>O: POST: Credential request with token
    O-->>I: Credential response with acceptance token
Loading

Figure 1: Issuance using Authorisation Code Flow based on [1]

  sequenceDiagram
      participant I as Individual using EUDI Wallet
      participant O as Organisational Wallet (Issuer)
      
      Note over I,O: Discovery issuer capabilities
      I->>O: GET: /.well-known/openid-credential-issuer
      O-->>I: OpenID credential issuer configuration
      I->>O: GET: /.well-known/oauth-authorization-server
      O-->>I: OAuth authorisation server metadata

      Note over I,O: Authenticate and Authorise
      I->>O: POST: Pre-authorised token request with PIN
      O-->>I: Token response

      Note over I,O: Issue credential
      I->>O: POST: Credential request with token
      O-->>I: Credential response with acceptance token
Loading

Figure 2: Issuance using Pre-Authorisation Code Flow based on [1]

3.1 Credential offer

The recommendation is to send the Credential Offer by Reference Using the credential_offer_uri Parameter to avoid QR Code data overload. The endpoint that is expected to be embedded in the QR code is:

openid-credential-offer://?credential_offer_uri=https://server.example.com/credential-offer

Here, the credential_offer_uri query param contains the URL in which the credential offer from the issuer can be resolved.

3.2 Credential offer response

Once the credential_offer_uri query param is resolved, the response can be either of the following formats.

{
  "credential_issuer": "https://server.example.com",
  "credentials": [
    "VerifiablePortableDocumentA1"
  ],
  "grants": {
    "authorization_code": {
      "issuer_state": "eyJhbGciOiJSU0Et...FYUaBy"
    }
  }
}

Note

In order to support all EBSI conformant wallets, the following format for the response is also valid, but not mandatory to be supported:

{
  "credential_issuer": "https://server.example.com",
  "credentials": [
    {
      "format": "jwt_vc_json",
      "types": [
        "VerifiableCredential",
        "VerifiableAttestation",
        "VerifiablePortableDocumentA1"
      ],
      "trust_framework": {
        "name": "ewc-issuer-trust-list",
        "type": "Accreditation",
        "uri": "TIR link towards accreditation"
      }
    }
  ],
   "grants": {
        "authorization_code": {
            "issuer_state": "eyJhbGciOiJSU0Et...FYUaBy"
        }
    }
}

The holder wallet obtains the JSON and can process it. The format is the credential format. In the context of EWC LSPs, it can be jwt_vc or sd-jwt_vc.

For pre-authorised flow, the credential response is as given:

{
  "credential_issuer": "https://server.example.com",
  "credentials": [
    {
      "format": "jwt_vc_json",
      "types": [
        "VerifiableCredential",
        "VerifiableAttestation",
        "VerifiablePortableDocumentA1"
      ],
      "trust_framework": {
        "name": "ebsi",
        "type": "Accreditation",
        "uri": "TIR link towards accreditation"
      }
    }
  ],
  "grants": {
    "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
      "pre-authorized_code": "eyJhbGciOiJSU0Et...FYUaBy",
      "user_pin_required": true
    }
  }
}

3.3 Discover request

Here, the holder wallet requests the issuer’s authorisation server configurations.

Resolve /.well-known/openid-credential-issuer endpoint for credential_issuer URI in the credential offer response.

GET https://server.example.com/.well-known/openid-credential-issuer

Resolve /.well-known/oauth-authorization-server endpoint for authorization_server URI present in the response for the above.

GET https://server.example.com/.well-known/oauth-authorization-server

3.4 Discover response

Once the well-known endpoint for issuer server configuration is resolved, the response is as given below with credentials_supported as defined by [6]:

{
  "credential_issuer": "https://server.example.com",
  "authorization_server": "https://server.example.com",
  "credential_endpoint": "https://server.example.com/credential",
  "deferred_credential_endpoint": "https://server.example.com/credential_deferred",
  "display": [
    {
      "name": "Issuer",
      "location": "Belgium",
      "locale": "en-GB",
      "description": "For queries about how we are managing your data please contact the Data Protection Officer."
    }
  ],
  "credentials_supported": {
    "VerifiablePortableDocumentA1SdJwt": {
      "format": "vc+sd-jwt",
      "scope": "VerifiablePortableDocumentA1",
      "cryptographic_binding_methods_supported": [
        "jwk"
      ],
      "cryptographic_suites_supported": [
        "ES256"
      ],
      "display": [
        {
          "name": "Portable Document A1",
          "locale": "en-GB",
          "background_color": "#12107c",
          "text_color": "#FFFFFF"
        }
      ],
      "credential_definition": {
        "type": "VerifiablePortableDocumentA1",
        "claims": {
          "given_name": {
            "display": [
              {
                "name": "Given Name",
                "locale": "en-GB"
              },
              {
                "name": "Vorname",
                "locale": "de-DE"
              }
            ]
          },
          "last_name": {
            "display": [
              {
                "name": "Surname",
                "locale": "en-GB"
              },
              {
                "name": "Nachname",
                "locale": "de-DE"
              }
            ]
          }
        }
      }
    }
  }
}

Note

In order to support all EBSI conformant wallets, the following format for the response is also valid, but not mandatory to be supported:

{
  "credential_issuer": "https://server.example.com",
  "authorization_server": "https://server.example.com",
  "credential_endpoint": "https://server.example.com/credential",
  "deferred_credential_endpoint": "https://server.example.com/credential_deferred",
  "display": {
    "name": "Issuer",
    "location": "Belgium",
    "locale": "en-GB",
    "description": "For queries about how we are managing your data please contact the Data Protection Officer."
  },
  "credentials_supported": [
    {
      "format": "jwt_vc",
      "types": [
        "VerifiableCredential",
        "VerifiableAttestation",
        "VerifiablePortableDocumentA1"
      ],
      "trust_framework": {
        "name": "ebsi",
        "type": "Accreditation",
        "uri": "TIR link towards accreditation"
      },
      "display": [
        {
          "name": "Portable Document A1",
          "locale": "en-GB"
        }
      ]
    }
  ]
}

Once the well-known endpoint for authorisation server configuration is resolved, the response is as given below:

{
  "issuer": "https://server.example.com",
  "authorization_endpoint": "https://server.example.com/authorize",
  "token_endpoint": "https://server.example.com/token",
  "jwks_uri": "https://server.example.com/jwks",
  "scopes_supported": [
    "openid"
  ],
  "response_types_supported": [
    "vp_token",
    "id_token"
  ],
  "response_modes_supported": [
    "query"
  ],
  "grant_types_supported": [
    "authorization_code"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "ES256"
  ],
  "request_object_signing_alg_values_supported": [
    "ES256"
  ],
  "request_parameter_supported": true,
  "request_uri_parameter_supported": true,
  "token_endpoint_auth_methods_supported": [
    "private_key_jwt"
  ],
  "request_authentication_methods_supported": {
    "authorization_endpoint": [
      "request_object"
    ]
  },
  "vp_formats_supported": {
    "jwt_vp": {
      "alg_values_supported": [
        "ES256"
      ]
    },
    "jwt_vc": {
      "alg_values_supported": [
        "ES256"
      ]
    }
  },
  "subject_syntax_types_supported": [
    "did:key:jwk_jcs-pub",
    "did:ebsi:v1",
    "did:ebsi:v2"
  ],
  "subject_trust_frameworks_supported": [
    "ebsi",
    "ewc-issuer-trust-list"
  ],
  "id_token_types_supported": [
    "subject_signed_id_token",
    "attester_signed_id_token"
  ]
}

Currently, we retain the trust framework specified by EBSI. Subsequently, we will specify an additional RFC defining the EWC trusted issuer list.

3.5 Authorisation request

The authorisation request is to grant access to the credential endpoint. Below is an example of such a request:

GET https://my-issuer.rocks/auth/authorize?

&response_type=code
&scope=openid
&issuer_state=tracker%3Dvcfghhj
&state=client-state
&client_id=did%3Akey%3Az2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsEYvdrjxMjQ4tpnje9BDBTzuNDP3knn6qLZErzd4bJ5go2CChoPjd5GAH3zpFJP5fuwSk66U5Pq6EhF4nKnHzDnznEP8fX99nZGgwbAh1o7Gj1X52Tdhf7U4KTk66xsA5r
&authorization_details=%5B%7B%22format%22%3A%22jwt_vc%22%2C%22locations%22%3A%5B%22https%3A%2F%2Fissuer.example.com%22%5D%2C%22type%22%3A%22openid_credential%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22VerifiablePortableDocumentA1%22%5D%7D%5D
&redirect_uri=openid%3A
&nonce=glkFFoisdfEui43
&code_challenge=YjI0ZTQ4NTBhMzJmMmZhNjZkZDFkYzVhNzlhNGMyZDdjZDlkMTM4YTY4NjcyMTA5M2Q2OWQ3YjNjOGJlZDBlMSAgLQo%3D
&code_challenge_method=S256
&client_metadata=%7B%22vp_formats_supported%22%3A%7B%22jwt_vp%22%3A%7B%22alg%22%3A%5B%22ES256%22%5D%7D%2C%22jwt_vc%22%3A%7B%22alg%22%3A%5B%22ES256%22%5D%7D%7D%2C%22response_types_supported%22%3A%5B%22vp_token%22%2C%22id_token%22%5D%2C%22authorization_endpoint%22%3A%22openid%3A%2F%2F%22%7D

Host: https://server.example.com

Query params for the authorisation request are given below:

response_type The value must be ‘code’
scope The value must be ‘openid’
state The client uses an opaque value to maintain the state between the request and callback.
client_id Decentralised identifier
authorization_details As specified in OAuth 2.0 Rich Authorization Requests specification to specify fine-grained access [4]. An example is as given below:
{
   "type": "openid_credential",
   "locations": [
      "https://credential-issuer.example.com"
   ],
   "format": "jwt_vc_json",
   "credential_definition": {
      "type": [
         "VerifiableCredential",
         "UniversityDegreeCredential"
      ]
   }
}

Note

You may also use the earlier version as supported by EBSI.

{
 "format": "jwt_vc",
 "locations": [
   "https://issuer.example.com"
 ],
 "type": "openid_credential",
 "types": [
   "VerifiableCredential",
   "VerifiableAttestation",
   "VerifiablePortableDocumentA1"
 ]
}
redirect_uri For redirection of the response
code_challenge As specified in PKCE for OAuth Public Client specification [5]
code_challenge_method As specified in PKCE for OAuth Public Client specification
client_metadata Holder wallets are non-reachable and can utilise this field in the Authorisation Request to deliver configuration
issuer_state If present in the credential offer

3.6 Authorisation response

The credential issuer can optionally request additional details to authenticate the client e.g. DID authentication. In this case, the authorisation response will contain a response_type parameter with the value direct_post. A sample response is as given:

HTTP/1.1 302 Found
Location: http://localhost:8080?state=22857405-1a41-4db9-a638-a980484ecae1&client_id=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock&redirect_uri=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock%2Fdirect_post&response_type=id_token&response_mode=direct_post&scope=openid&nonce=a6f24536-b109-4623-a41a-7a9be932bdf6&request_uri=https%3A%2F%2Fapi-conformance.ebsi.eu%2Fconformance%2Fv3%2Fauth-mock%2Frequest_uri%2F111d2819-9ab7-4959-83e5-f414c57fdc27

Query params for the authorisation response are given below:

state The client uses an opaque value to maintain the state between the request and callback.
client_id Decentralised identifier
redirect_uri For redirection of the response
response_type The value must be id_token if the issuer requests DID authentication.
response_mode The value must be direct_post
scope The value must be openid
nonce A value used to associate a client session with an ID token and to mitigate replay attacks
request_uri The authorisation server’s private key signed the request.

The holder wallet then responds with an id_token signed by the DID to the direct post endpoint.

POST /direct_post
Content-Type: application/x-www-form-urlencoded
&id_token=eyJraWQiOiJkaW...a980484ecae1

If additional details are not requested, the credential issuer will send an authorisation response with a code query parameter containing the short-lived authorisation code. A sample response is given below:

HTTP/1.1 302 Found
Location: https://Wallet.example.org/cb?code=SplxlOBeZQQYbYS6WxSbIA

3.7 Token request

3.7.1 Authorisation code flow

The token request for authorised code flow is as given:

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

&grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
&redirect_uri=https%3A%2F%2FWallet.example.org%2Fcb

This request is made with the following query params:

grant_type Grant type for authorisation. E.g. authorization_code
client_id Decentralised identifier
code Authorisation code
code_verifier Wallet-generated secure random token used to validate the original code_challenge provided in the initial Authorization Request

3.7.2 Pre-authorised code flow

The token request for pre-authorised code flow is as given:

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

&grant_type=urn:ietf:params:oauth:grant-type:pre-authorized_code
&pre-authorized_code=SplxlOBeZQQYbYS6WxSbIA
&user_pin=493536

This request is made with the following query params:

grant_type Grant type for authorisation. E.g. urn:ietf:params:oauth:grant-type:pre-authorized_code
pre-authorized_code Code representing the Credential Issuer's authorisation for the Wallet to obtain Credentials of a certain type. This code must be short-lived and single-use.
user_pin The end user pin is decided by the issuer and sent to the holder through an out-of-band process. E.g. Email, SMS

3.8 Token response

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6Ikp..sHQ",
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI4a5k..zEF",
    "token_type": "bearer",
    "expires_in": 86400,
    "id_token": "eyJodHRwOi8vbWF0dHIvdGVuYW50L..3Mz",
    "c_nonce": "PAPPf3h9lexTv3WYHZx8ajTe",
    "c_nonce_expires_in": 86400
}

3.9 Credential request

The Holder wallet makes a Credential Request to the Credential Endpoint as below:

POST /credential
Content-Type: application/json
Authorization: Bearer eyJ0eXAi...KTjcrDMg

{
   "format": "vc+sd-jwt",
   "credential_definition": {
      "type": "VerifiablePortableDocumentA1"
   },
   "proof": {
      "proof_type": "jwt",
      "jwt":"eyJraW...KWjceMcr"
   }
}

If specified, you can request specific fields to be included in the issued credential. If its not specified, all fields in the credential is included.

Note

In order to support all EBSI conformant wallets, the following format for the request is also valid, but not mandatory to be supported:

POST /credential
Content-Type: application/json
Authorization: Bearer eyJ0eXAi...KTjcrDMg

{
  "format": "jwt_vc_json",
  "proof": {
    "jwt": "eyJraWQiOiJkaWQ6a2...su7UFClz9NQnw",
    "proof_type": "jwt"
  },
  "types": [
    "VerifiableCredential",
    "VerifiableAttestation",
    "VerifiablePortableDocumentA1"
  ]
}

3.10 Credential response

The credential response can happen in-time or can be deferred as described below.

3.10.1 In-time

The In-time flow indicates that the credential is available immediately and the response format is as below:

{
  "format": "vc+sd-jwt",
  "credential": "eyJ0eXAiOi...F0YluuK2Cog",
  "c_nonce": "fGFF7UkhLa",
  "c_nonce_expires_in": 86400
}

3.10.2 Deferred

If the credential is unavailable, the issuer responds with an acceptance token, indicating credential issuance is deferred.

{
  "acceptance_token": "eyJ0eXAiOiJKV1QiLCJhbGci..zaEhOOXcifQ",
  "c_nonce": "wlbQc6pCJp",
  "c_nonce_expires_in": 86400
}

If the response contains acceptance_token field, then it can be understood the credential is not available now and should be later available through the deferred credential endpoint.

POST /deferred-credential
Authorization: BEARER eyJ0eXAiOiJKV1QiLCJhbGci..zaEhOOXcifQ

4.0 Alternate response format

Standard HTTP response codes shall be supported. Any additional ones can be formulated in the following format.

{
  "error": "invalid_request",
  "error_description": "The verifiable credential is expired"
}

The table below summarises the success/error responses that can be used:

Response format Description
invalid_request Request failed. E.g. The verifiable credential is expired
invalid_grant
  • The Authorization Server expects a PIN in the Pre-Authorized Code Flow but the Client provides the wrong PIN.
  • The end user provides the wrong Pre-Authorized Code or the Pre-Authorized Code has expired.
invalid_client The Client tried to send a Token Request with a Pre-Authorized Code without Client ID, but the Authorization Server does not support anonymous access

5.0 Implementers

Please refer to the implementers table.

6.0 Reference

  1. OpenID Foundation (2023), 'OpenID for Verifiable Credential Issuance (OID4VCI)', Available at: https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-12.html (Accessed: January 10, 2024).
  2. European Commission (2023) The European Digital Identity Wallet Architecture and Reference Framework (2023-04, v1.1.0) [Online]. Available at: https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/releases (Accessed: October 16, 2023).
  3. OpenID Foundation (2023), 'Self-Issued OpenID Provider v2 (SIOP v2)', Available at: https://openid.net/specs/openid-connect-self-issued-v2-1_0.html (Accessed: October 01, 2023)
  4. OAuth 2.0 Rich Authorization Requests, Available at: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-11 (Accessed: February 01, 2024)
  5. Proof Key for Code Exchange by OAuth Public Clients, Available at: https://datatracker.ietf.org/doc/html/rfc7636 (Accessed: February 01, 2024)
  6. OpenID4VC High Assurance Interoperability Profile with SD-JWT VC - draft 00, Available at https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-sd-jwt-vc-1_0.html (Accessed: February 16, 2024)

Appendix A: Public key resolution

For a JWT there are multiple ways for resolving the public key using the kid header claim:

  • If the key identifier is a DID then use a DID resolver to obtain the public key
  • If the key identifier is not a DID, then resolve the JWKs endpoint in the AS configuration and match the public key from the JWK set using the key identifier.

Additionally, it is possible to specify JWK directly in the header using jwk header claim.