- Introduction
- Requirements
- Implementation Details
- Credential Application Flow
- Other Considerations
This document proposes a standardized means for PFIs (Participating Financial Institution) to perform KYC (Know Your Customer) and issue a subsequent KCC (Known Customer Credential) on a DID (Decentralized Identifer) controlled by a retail customer for the purpose of providing financial services to that DID in accordance to regulatory requirements.
KCC (Known Customer Credential) is a VC (Verifiable Credential) which is intended to be utilized by a retail customer as proof of an actively compliant KYC verification.
The json schema for the KCC can be found here: KCC Schema. This schema defines the structure and requirements for a Known Customer Credential, ensuring all necessary information is included and correctly formatted.
In the financial industry, KYC (Know Your Customer) is term used to describe a set of policies, procedures, and processes that financial institutions use to determine the true identity of a customer, and assess the on-going risk that a customer poses to an organization during the life-time of a customer relationship. KYC typically encompasses:
- Customer identification and verification (IDV);
- Understanding the nature and purpose of customer relationships to develop a customer risk profile; and
- Ongoing monitoring for reporting suspicious transactions and, using a risk-based approach, maintaining and updating customer information.
Regulatory KYC requirements can vary by region with respect to the information that needs to be collected, the information that needs to be validated and verified, how long that information needs to be retained, and how often various aspects of KYC need to be repeated (e.g. sanctions screening, refreshing IDV, re-collecting source of funds information).
IDV (Identity Verification) is a critical component of KYC wherein the PII (Personally Identifying Information) collected from an individual is verified using third party resources. IDV often includes steps such as verification of a valid government-issued photo ID, liveness checks, and verification of user-submitted PII against authoritative databases.
Financial institutions often leverage IDV Vendors to streamline the IDV process.
Integration with IDV vendors happens in 1 of 2 ways:
- IDV Vendor provides an SDK that takes control of the user interface for PII collection
- PII is submitted directly to the vendor's backend system
- IDV Vendor notifies the PFI via webhook request when IDV is complete
- PFI requests IDV result and PII from IDV Vendor
- PII is submitted directly to the PFI's backend system
- PII is subsequently sent to the IDV vendor via the backend system for verification
Important
The 2 styles of integration provide flexibility for PFI identity verification systems, but they don't impact Mobile Apps at all, as they have the same external behaviour.
This body of work is an extension of the work being done for tbDEX. In effect, this proposal considers the following as requirements:
- Must support all IDV flows described in the IDV Vendor Integrations section of this document
Ensuring that this is possible is essential to reduce friction or pain points for financial institutions interested in providing liquidity on tbDEX.
- Must support the ability to provide other Verifiable Credentials or Verifiable Claims as input for IDV (e.g. mDL, eIDAS, VCs from other issuers)
This requirement is critical to the value proposition of using Verifiable Credentials within the context of KYC. Performing KYC has a non-negligible cost for financial institutions which can be drastically reduced by receiving the necessary PII in a format that has been provably verified by a trusted third party.
Important
We will need to consider scenarios wherein an individual possesses one or more "Identity Wallet" mobile applications that store credentials
Important
We will need to consider issuance to and presentation from identity wallets that did not initiate the flow
- Must ensure that applications initiating KCC issuance / IDV flows for PFIs do not have to store, handle, or relay PII through the initating application's backend systems
Necessitating that PII come in contact with an application's backend systems introduces undesired complexity for many use-cases (e.g. self-custodial wallets that wish to provide on/off-ramps to their users without owning customer relationships)
- Must prevent individuals who already have a pre-existing account with a PFI from having to go through IDV again
A PFI could be a bank that an individual already has an account with. In this scenario, The individual should be able to log in to their pre-existing account via webview vs. having to go through IDV again.
- Must make use of pre-existing standards wherever possible.
Interoperability is critical for the ecosystem as a whole. This is also an implied requirement in order to leverage credentials that are already being issued/presented using pre-existing standards
Concretely, the objective is to implement a solution that allows a mobile application to initiate an IDV flow with a PFI used to perform KYC that results in a Known Customer Credential issued by the PFI. This KCC can be presented by the holder to the PFI to utilize financial services (e.g. tbDEX value exchange)
As a means to provide clarity, many of the examples in this section will refer to an imaginary mobile application named Mobile App.
Mobile App is a mobile application that can be used by individuals to:
- Purchase Stablecoin using fiat currency from a PFI via tbDEX. The PFI acts as the custodian of the purchased Stablecoin
- Send custodied Stablecoin to anyone via tbDEX
- Sell Stablecoin for fiat currency through a PFI via tbDEX.
Mobile App acts as a self-custodial Identity Wallet that:
- creates a DID for each individual and securely stores private keys directly on device.
- stores Verifiable Credentials issued to the user directly on device
- The PFI controls a DID whose method supports
service
endpoints - The Holder controls their own DID
- The Holder has already discovered the PFI's DID
This implementation involves 3 distinct participants that have different responsibilities:
The PFI operates an Issuer, implementing this protocol.
The Mobile app is responsible for:
- Initiating the IDV flow of a PFI
- Acquiring Credential offered by PFI
Note
Triggered by the mobile app as a byproduct of initiating the IDV flow
The Web view is utilized to:
- Render IDV flow of a PFI
- Return an OID4VCI Credential Offer back to Mobile App which is then used to acquire a credential
Important
This implementation has chosen to use Web Views to render IDV flows for the following reasons:
- provides PFI with maximal flexibility as to how they collect Personal Information from an individual
- prevents Wallets / Mobile Apps from having to update source code in order to integrate with different PFIs
- establish a clear distinction between the application initiating the flow and a PFI
Financial service providers often perform KYC incrementally in response to customer's expressed intent to access services. For example, minimal upfront diligence on newly onboarded customers, followed by additional diligence when the customer's activity passes a threshold.
A KCC Issuer may define multiple KCC types to represent different levels of KYC diligence, and describe each with a Presentation Definition. Mobile Apps which hold a previously issued KCC may apply for new credentials to acquire a higher level KCC (see Initiate Application)
sequenceDiagram
actor A as Applicant
participant W as Webview
participant D as Mobile App
participant P as PFI
participant I as IDV
rect rgba(0, 0, 0, 0.1)
D->>+P: Initiate KCC application
Note right of I: Initiate Application
P-->>-D: IDV Request
end
rect rgba(0, 0, 0, 0.1)
D->>+W: Load URL
Note right of I: Collect IDV
A->>W: Provide identity data
W->>+I: Applicant identity data
deactivate W
end
rect rgba(0, 0, 0, 0.1)
D->>+P: Request KCC
Note right of I: Credential Issuance
P-->>-D: Issue KCC
end
PFI's can become publicly discoverable by advertising their IDV endpoint as a Service within their DID Document. In order to increase the likelihood of being discovered, the service
entry SHOULD include the following properties:
Property | Value |
---|---|
id |
MUST be equal to the DID URI with an appended idv fragment, example did:example:123#idv , therefore the service MAY be dereferenced. |
type |
IDV |
serviceEndpoint |
PFI's publicly addressable IDV endpoint for usage in Initiate Application. |
Note
Decentralized discoverability is dependent upon whether the underlying verifiable registry of the selected DID Method is crawlable
The application flow is initiated with a SIOPv2 interaction that authenticates the applicant's DID.
sequenceDiagram
autonumber
participant W as Webview
participant D as Mobile App
participant P as PFI
D->>+P: GET
P->>P: Construct SIOPv2 Authorization Request
P-->>-D: SIOPv2 Authorization Request
D->>D: Construct SIOPv2 Authorization Response
D->>+P: SIOPv2 Authorization Response
P->>P: Construct IDV Request
P-->>-D: IDV Request
D->>D: Verify IDV Request
D->>W: Load URL in IDV Request
- Mobile App sends an HTTP GET request to the
serviceEndpoint
specified in Discover Initiation Endpoint - PFI constructs a SIOPv2 Authorization Request
- PFI URI-encodes SIOPv2 Authorization Request and returns in HTTP response
- Mobile App verifies integrity of SIOPv2 Authorization Request and constructs a SIOPv2 Authorization Response
- Mobile App POSTs SIOPv2 Authorization Response to the
response_uri
from the SIOPv2 Authorization Request - PFI verifies integrity of SIOPv2 Authorization Response and constructs IDV Request
- PFI returns IDV Request in HTTP response
- Mobile App verifies integrity of IDV Request
- Mobile App loads URL provided in IDV Request in Webview
An HTTP GET request begins the IDV and KCC issuance flow.
Query Parameter | Description | Required | References | Comments |
---|---|---|---|---|
presentation_definition_id |
The ID of a presentation definition describing the KCC to be issued. | n | Presentation Exchange 2.0.0 tbDEX Offering | If not provided, the PFI chooses which KCC to issue |
The response is a SIOPv2 Authorization Request.
Field | Description | Required | References | Comments |
---|---|---|---|---|
client_id |
The DID of the Relying Party (the PFI) | y | ||
scope |
What's being requested. 'openid' indicates ID Token is being requested | y | OIDC | |
response_type |
What sort of response the RP is expecting. MUST include id_token . MAY include vp_token |
y | OIDC | |
response_uri |
The URI to which the SIOPv2 Authorization Response will be sent | y | OID4VP | |
response_mode |
The mode in which the SIOPv2 Authorization Response will be sent. MUST be direct_post |
y | OID4VP | |
presentation_definition |
Used by PFI to request VCs as input to IDV process | n | OID4VP | If present, Response Type vp_token MUST also be present |
nonce |
A nonce which MUST be included in the ID Token provided in the SIOPv2 Authorization Response | y | ||
client_metadata |
A JSON object containing the Verifier metadata values | y | OIDC SIOPv2 |
Field | Description | Required | References | Comments |
---|---|---|---|---|
subject_syntax_types_supported |
Array of strings, each a DID method supported for the subject of ID Token | y | SIOPv2 | Example ["did:dht", "did:jwk"] |
client_name |
Human-readable string name of the client to be presented to the end-user during authorization | n | RFC7591 | |
client_uri |
URI of a web page providing information about the client | n | RFC7591 | |
logo_uri |
URI of an image logo for the client | n | RFC7591 | |
contacts |
Array of strings representing ways to contact people responsible for this client, typically email addresses | n | RFC7591 | |
tos_uri |
URI that points to a terms of service document for the client | n | RFC7591 | |
policy_uri |
URI that points to a privacy policy document | n | RFC7591 |
Important
Include vp_formats
in Client Metadata? https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#section-9.1-2.2
Important
the inclusion of presentation_definition
as per OID4VP allows for other verifiable credentials to be provided as input for IDV.
The SIOPv2 Authorization Request is encoded as a URI before being returned to Mobile App, as per SIOPv2. No authorization_endpoint
is used in the URI, so it is the query parameter portion of the URI only.
The Mobile App responds with a Cross-Device SIOPv2 Authorization Response.
Field | Description | Required | References | Comments |
---|---|---|---|---|
id_token |
A self issued, signed JWT which responds to the SIOPv2 Authorization Request | y | JWT SIOPv2 | |
vp_token |
A Verifiable Presentation or an array of VPs in response to presentation_definition |
n | OIDV4VP | |
presentation_submission |
A Presentation Submission that contains mappings between the requested VC and where to find them within vp_token |
n | OIDV4VP |
Field | Description | Required | References | Comments |
---|---|---|---|---|
iss |
Issuer MUST match the value of sub (Applicant's DID) |
y | ||
sub |
Subject. The DID of the customer applying for KCC | y | ||
aud |
Audience MUST match the value of client_id from the SIOPv2 Authorization Request (PFI's DID) |
y | ||
nonce |
Nonce MUST match the value of nonce from the SIOPv2 Authorization Request |
y | ||
exp |
Expiry time | y | ||
iat |
Issued at time | y |
The response is an IDV Request.
Field | Description | Required | References | Comments |
---|---|---|---|---|
url |
URL of form used to collect PII | y | Required for now until we figure out how to support exclusively providing credentials as input | |
credential_offer |
y | OID4VCI |
Field | Description | Required | References | Comments |
---|---|---|---|---|
credential_issuer |
The URL of the Credential Issuer that the Mobile App will interact with in subsequent steps | y | OID4VCI | |
credential_configuration_ids |
Array of unique strings that each identify a credential being offered. Mobile App can use these to request metadata | y | OID4VCI | |
grants |
Object containing Grant Types that the Credential Issuer will accept for this credential offer. MUST contain urn:ietf:params:oauth:grant-type:pre-authorized_code |
y | OID4VCI |
Field | Description | Required | References | Comments |
---|---|---|---|---|
urn:ietf:params:oauth:grant-type:pre-authorized_code |
Grant Type that allows the Mobile App to follow a Pre-Authorized Code Flow to collect the credential | y | OID4VCI |
Field | Description | Required | References | Comments |
---|---|---|---|---|
pre-authorized_code |
The code representing the Credential Issuer's authorization for the Mobile App to obtain a credential | y | OID4VCI |
Warning
TODO: explain rationale behind providing credential_offer
at this stage
The IDV Request sent to the Mobile App contains a url
field. The Mobile App MUST load this URL in an embedded
webview. The Applicant is guided through whatever steps are necessary to collect identity data and submit to the IDV
server. The webview MUST close itself when the application steps are complete.
Important
Whether the PFI is utilizing an IDV vendor is entirely opaque from the originating mobile app's perspective.
sequenceDiagram
autonumber
actor A as Applicant
participant W as Webview
participant D as Mobile App
participant P as PFI
participant V as IDV Vendor
D->>W: Load URL
A->>W: Provide PII, Submit
W->>V: PII
V->>V: Process
V->>W: Callback URI or 200
W->>W: Close
sequenceDiagram
autonumber
actor A as Applicant
participant W as Webview
participant D as Mobile App
participant P as PFI
D->>W: Load URL
loop until 200 response
A->>W: Provide PII, Submit
W->>P: PII
P->>P: Process
P->>W: 200 or 400 with errors
end
W->>W: Close
Credential Issuance is an OID4VCI Pre-Authorized Code flow:
sequenceDiagram
autonumber
participant D as Mobile App
participant P as PFI
D->>+P: GET Authorization metadata
P-->>-D: Authorization Server metadata
D->>+P: Token Endpoint
P-->>-D: Access Token
D->>+P: GET Credential metadata
P-->>-D: Credential Issuer metadata
D->>+P: Issue credential
P-->>-D: Credential
- GET well-known oauth-authorization-server endpoint
- PFI returns Authorization Server Metadata
- Token Request containing
pre-authorized_code
- Token Response containing an Access Token
- GET well-known openid-credential-issuer endpoint
- PFI returns Credential Issuer Metadata
- Request credentials
- PFI issues credential
Metadata resources are hosted by the Credential Issuer as a means of informing clients of endpoint locations, technical capabilities, feature support and (internationalized) display information.
Credential Issuers are required to set up a number of well known endpoints to facilitate authorization and credential issuance as follows.
URLs to retrieve both Credential Issuer Metadata and Authorization Server Metadata are dynamically constructed by the client using .well-known
URI's.
- Credential Issuer Metadata URL:
credential_issuer
+/.well-known/openid-credential-issuer
- Authorization Server Metadata URL:
credential_issuer
+/.well-known/oauth-authorization-server
Where credential_issuer
originates from within the Credential Offer from within the IDV Request.
The Credential Issuer Metadata informs clients of endpoint locations, technical capabilities, supported Credentials, and (internationalized) display information.
Field | Description | Required | References | Comments |
---|---|---|---|---|
credential_issuer |
URL of the Credential Issuer | y | OID4VCI | Same value as the credential_issuer within the Credential Offer |
credential_endpoint |
URL for the Credential Endpoint | y | OID4VCI | |
deferred_credential_endpoint |
URL for the Deferred Credential Endpoint | y | OID4VCI | |
credential_configurations_supported |
Object which defines the specifics of the credentials being issued | y | OID4VCI |
The credential_configurations_supported
is an object which defines the specifics of the credentials being issued. The credential_configurations_supported
is a key/value object wherein each key corresponds to a value within the credential_configuration_ids
from the Credential Offer and the value is defined with the following fields.
Field | Description | Required | References | Comments |
---|---|---|---|---|
format |
Format for the given credential | y | OID4VCI | MUST be jwt_vc_json |
cryptographic_binding_methods_supported |
List of supported DID Methods | y | OID4VCI | MUST be ["did:web", "did:jwk", "did:dht"] |
credential_signing_alg_values_supported |
List of supported cryprographic signing algorithms | y | OID4VCI | MUST be EdDSA or ES256K |
proof_types_supported |
Object that describes the supported key proof | y | OID4VCI | MUST be {"jwt": {"proof_signing_alg_values_supported": ["EdDSA", "ES256K"]}} |
The Credential Issuer's Authorization Server Metadata informs clients of its endpoint locations and authorization server capabilities.
Field | Description | Required | References | Comments |
---|---|---|---|---|
issuer |
URL of then Credential Issuer | y | RFC8414 | Same value as credential_issuer |
token_endpoint |
URL for the Token Request | y | RFC8414 |
Warning
TODO we need to consider additional fields, such as authorization_endpoint
(once we support the Auth Flow) or response_types_supported
The Token Endpoint issues an Access Token in exchange for the pre-authorized_code
from the Credential Offer.
The pre-authorized_code
must be invalidated immediately upon issue of an Access Token.
The Access Token can be used with the Credential Endpoint and Deferred Credential Endpoint.
The URL is the token_endpoint
field of the Authorization Server Metadata.
Field | Description | Required | References | Comments |
---|---|---|---|---|
grant_type |
y | OID4VCI | MUST be urn:ietf:params:oauth:grant-type:pre-authorized_code |
|
pre-authorized_code |
The value of pre-authorized_code from the Credential Offer |
y | OID4VCI | |
client_id |
The client DID | y | OID4VCI |
Warning
TODO we only support pre-auth flow now, but once we support auth flow then code
will be used instead of pre-authorized_code
Clients must use the fields from token response in subsequent calls to the Credential Endpoint and Deferred Credential Endpoint.
Field | Description | Required | References | Comments |
---|---|---|---|---|
access_token |
The access token granted | y | RFC6749 | access_token 's are Compact Serialized JWT's. |
token_type |
y | RFC6749 | MUST be bearer |
|
expires_in |
Seconds from issue until the access token expires | y | RFC6749 | |
c_nonce |
A nonce for use in the subsquent call to Credential Endpoint | y | OID4VCI | |
c_nonce_expires_in |
Seconds from issue until the c_nonce expires |
y | OID4VCI |
Additionally the token response MUST contain HTTP headers "Cache-Control: no-store" and "Pragma: no-cache" as per RC6749
Warning
TODO we need to define refresh token flows
The access_token
granted by the Credential Issuer contains the following JOSE Header fields.
Field | Description | Required | References | Comments |
---|---|---|---|---|
alg |
(Algorithm) The cryptographic algorithm used to sign the JWT | y | RFC7515 | MUST be EdDSA or ES256K |
kid |
(KeyID) The fully qualified DID Key ID of the signer | y | RFC7515 | In the form did:{method}:{identifier}#{key_id} |
typ |
(Type) The explicit JWT type | y | RFC7515 | MUST be at+jwt per RFC9068 |
The access_token
granted by the Credential Issuer contains the following JWT Claim fields.
Field | Description | Required | References | Comments |
---|---|---|---|---|
iss |
(Issuer) The DID of the Authorization Server | y | RFC7519 | |
aud |
(Audience) The resource indicator (DID) of the Credential Issuer | y | RFC7519 | |
sub |
(Subject) The DID of the customer applying for KCC (Applicant DID) | y | RFC7519 | |
client_id |
(Client ID) The DID of the customer applying for KCC (Applicant DID) | y | RFC8693 | |
exp |
(Expiration) The time at which the access_token expires |
y | RFC7519 | |
iat |
(IssuedAt) The time at which the access_token was granted |
y | RFC7519 | |
jti |
(JWT ID) A unique identifier for the token | y | RFC7519 | |
c_nonce |
A nonce to be used in the Proof JWT | y | OID4VCI | |
c_nonce_expiry |
Expiry time of the c_nonce | y | OID4VCI |
The authorization server responds with an HTTP 400 (Bad Request), and an error object in the response body.
Field | Description | Required | References | Comments |
---|---|---|---|---|
error |
A string error code from below list | y | RFC6749 | |
error_description |
A human-readable description of the error | n | RFC6749 | |
error_uri |
A URI that provides additional information about the error | n | RFC6749 |
Error codes are defined in OID4VCI Token Error Response.
invalid_request
invalid_grant
invalid_client
unauthorized_client
unsupported_grant_type
This endpoint issues a Credential (or a Transaction ID) upon presentation of a valid Access Token.
The URL is the credential_endpoint
field of the Credential Issuer Metadata.
sequenceDiagram
autonumber
participant D as Mobile App
participant P as PFI
participant V as IDV Vendor
alt Immediate Issuance
D->>+P: Credential Request
P->>-D: Issued Credential
else Deferred Issuance
D->>+P: Credential Request
P->>-D: transaction_id
loop until credential received
D->>+P: Deferred Credential Request
P->>-D: issuance_pending
end
V->>+P: Webhook Request w. results
P->>P: Evaluate results and Issue Credential or Reject
D->>+P: Deferred Credential Request
P->>-D: Issued Credential
end
The access_token
must be passed as an HTTP Authorization
header (i.e. Authorization: Bearer {access_token}
)
Field | Description | Required | References | Comments |
---|---|---|---|---|
format |
The format of the credential issued | y | OID4VCI | MUST be jwt_vc_json |
proof |
Proof of possession of cryptographic materials | y | OID4VCI |
proof
is an object which contains proof of possession of the client's cryptographic materials.
Field | Description | Required | References | Comments |
---|---|---|---|---|
proof_type |
The type of proof | y | OID4VCI | MUST be jwt |
jwt |
The proof JWT | y | OID4VCI |
For the client to prove possession of their cryptographic materials, they must construct a JWT with the following JOSE Header fields.
Field | Description | Required | References | Comments |
---|---|---|---|---|
alg |
(Algorithm) The cryptographic algorithm used to sign the JWT | y | OID4VCI | MUST be EdDSA or ES256K |
typ |
(Type) The explicit JWT type | y | OID4VCI | MUST be openid4vci-proof+jwt |
kid |
(KeyID) The fully qualified DID Key ID of the signer | y | OID4VCI | In the form did:{method}:{identifier}#{key_id} |
For the client to prove possession of their cryptographic materials, they must construct a JWT with the following JWT Claim fields.
Field | Description | Required | References | Comments |
---|---|---|---|---|
iss |
(Issuer) The DID of the customer applying for KCC (Applicant DID) | y | OID4VCI | |
aud |
(Audience) The DID of the Credential Issuer | y | OID4VCI | |
iat |
(IssuedAt) The time at which the key proof was created | y | OID4VCI | |
nonce |
The value of the c_nonce from the Token Response |
y | OID4VCI |
Credential Response can be immediate or deferred. If the issuer can immediately issue the requested credential, it will
return it in the credential
field, with an HTTP 200 (OK) status code.
If the issuer cannot immediately issue a credential it returns a transaction_id
, which is to be used in a subsequent call
in the Deferred Credential Request. The HTTP status code MUST be 202 (Accepted).
Field | Description | Required | References | Comments |
---|---|---|---|---|
credential |
The credential in jwt_vc_json format |
n | OID4VCI | If missing, transaction_id must be present |
transaction_id |
ID used for subsequent call to Deferred Credential Request | n | OID4VCI | If missing, then credential must be present |
If the Credential Request does not contain a valid Access Token, the response is an authorization error response, as per RFC6750.
Field | Description | Required | References | Comments |
---|---|---|---|---|
error |
A string error code from below list | y | OID4VCI | |
error_description |
A human-readable description of the error | n | OID4VCI |
Error codes are defined by OID4VCI Credential Request Errors
invalid_credential_request
unsupported_credential_type
invalid_proof
invalid_encryption_parameters
invalid_request
This endpoint issues a credential previously requested via the Credential Endpoint, in cases where
the credential issuer was not able to immediately issue the credential.
The URL is the deferred_credential_endpoint
field of the Credential Issuer Metadata.
The access_token
must be passed as an HTTP Authorization
header (i.e. Authorization: Bearer {access_token}
).
Field | Description | Required | References | Comments |
---|---|---|---|---|
transaction_id |
Transaction ID returned by the Credential Response | y | OID4VCI |
Field | Description | Required | References | Comments |
---|---|---|---|---|
credential |
The credential in jwt_vc_json format |
y | OID4VCI |
When the Deferred Credential Request is invalid or the credential is not available yet, the response is an HTTP 400 (Bad Request), and an error object in the response body.
Response body is defined in Credential Error Response
It may very well be the case that this approach works for identity verification in general even outside the purposes of performing KYC but it's far too early to say or have that discussion. Just something to keep in the back of our minds