Become a relying party
Overview
A relying party is a service or app that requests a user's verifiable credentials from an issuer. Relying parties use an identity provider, such as Internet Identity, to communicate with the issuer. Identity providers must support the verifiable credentials specification. Communication with the identity provider uses the window.postMessage()
communication channel.
The relying party must perform two main tasks:
- Request the credentials from the issuer through an identity provider.
- Verify the credentials received from an identity provider.
Requesting credentials
There are two ways to request credentials:
Using the verifiable credentials JavaScript SDK.
Manually integrating with an identity provider.
JavaScript SDK for verifiable credentials
You can request credentials to issuers through identity providers with the JS client for relying parties NPM package @dfinity/verifiable-credentials. Below is an example:
import { requestVerifiablePresentation } from "@dfinity/verifiable-credentials/request-verifiable-presentation";
requestVerifiablePresentation({
onSuccess: async (verifiablePresentation: VerifiablePresentationResponse) => {
// Called when the flow finishes successfully.
},
onError() {
// Called when there is a technical error.
},
issuerData: {
origin: "<url of the origin>",
canisterId: "<[optional] canister id>",
},
credentialData: {
credentialSpec: {
credentialType: '<credential type as expected by issuer>',
arguments: {
// Arguments to verify with the credential
},
},
credentialSubject: "<user's principal>",
},
identityProvider: "<[optional] url identity provider>",
derivationOrigin: "<[optional] origin for delegated identity>",
windowOpenerFeatures: "<[optional] window opener config string>",
});
Learn more about the JavaScript SDK for VCs.
This code executes the following steps:
Opens a new window or tab with the identity provider (Internet Identity (II)).
Waits for a window post message from II.
Sends a request to II through the window post message.
Waits for the response from II.
Calls an
onSuccess
callback if the process was successful, but that does not necessarily mean that a credential was received.Calls an
onError
callback if the process has some technical error, if the user closes the II window, or if II times out.
Learn more about how this workflow works.
Manual integration
To implement a relying party service, your canister must perform three steps:
Load an identity provider such as Internet Identity: Open a window to an identity provider that will act as the communication bridge between the relying party and the issuer.
Request credentials: Send a JSON RPC request to the issuer API for a user's credentials.
Receive a response: Receive either a success or error message from the identity provider.
Step 1: Load the identity provider in a new window
First, your canister must initiate and load an identity provider (in this example, Internet Identity) in a new window with the URL path /vc-flow
. Opening this window will trigger the identity provider to load and send a notification to the window.postMessage()
interface with the following JSON-RPC content:
{
"jsonrpc": "2.0",
"method": "vc-flow-ready"
}
The user will interact with this window to authenticate with their Internet Identity. If this window is closed by the user or times out, the request for credentials will be closed and an error will be returned to the relying party.
Step 2: Request a verifiable credential
Once the user has logged into Internet Identity and the service is ready, the relying party service can request a verifiable credential for the user by sending a JSON RPC request to the issuer API. The user must be logged into Internet Identity for the request to be successful. Below is an example of a JSON RPC request to request a credential:
{
"id": 1,
"jsonrpc": "2.0",
"method": "request_credential",
"params": {
"issuer": {
"origin": "https://employment-info.com",
"canisterId": "rwlgt-iiaaa-aaaaa-aaaaa-cai"
},
"credentialSpec": {
"credentialType": "VerifiedEmployee",
"arguments": {
"employerName": "XYZ Ltd."
}
},
"credentialSubject": "2mdal-aedsb-hlpnv-qu3zl-ae6on-72bt5-fwha5-xzs74-5dkaz-dfywi-aqe"
}
}
In this request, the following parameters must be set:
method
: Pass the expected method"request_credential"
.params
: Configuration parameters to identify which credential is being requested.issuer
: The information to identify which issuer API instance the request is sent to.origin
: The origin URL of the issuer.canisterId
: The canister ID of the issuer API. This field is optional.
credentialSpec
: The specification of the credential being requested.credentialType
: The type of the requested credential.arguments
: Key value pairs that are specific to the credential. This field is optional.
credentialSubject
: The principal of the user requesting the credential.derivationOrigin
: An origin that should be used for the relying party's principal derivation as an alternative to the frontend hostname. This should match thederivationOrigin
used when logging in the relying party.
Additional examples can be found in the verifiable credentials specification.
Step 3: Receive a response
If the JSON RPC request is successfully sent and received by the issuer API, Internet Identity will return the following JSON-RPC response to the relying party:
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"verifiablePresentation": "eyJQ..."
}
}
In this response, verifiablePresentation
is a JWT-based verifiable presentation (VP) containing two credentials:
A verifiable credential issued by Internet Identity that relates the
credentialSubject
to the user principal.A verifiable credential issued by the issuer to the user principal.
The specification ensures unlinkability between the relying party and the issuer. That's why two credentials are needed. The first credential proves the link between the relying party's principal and id_alias
created in the identity provider. The second credential is the actual credential to the id_alias
.
Note how the issuer's principal never appears in the credentials. Instead, the relying party and the issuer know about the id_alias
. Therefore, this id_alias
needs the credential to verify the link between the user's principal and the id_alias
.
The order of the credentials in the VP should match the order above, such that the id_alias
credential linking to the relying party's principal should come first.
View an example of a verifiable presentation for reference.
The relying party should not expect to receive an immediate response, as it should wait for one of the following events:
II sends a JSON-RPC response as described above.
II sends a JSON-RPC error response as described below.
The user closes the II window. This should be treated as an error.
The relying party may also close the II window after some timeout. The user would then be notified by the relying party that the request failed.
Error messages
If the workflow fails for any reason, Internet Identity will return an error. The error does not provide details on the cause of the failure to provide privacy protection.
An example error can be found below:
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"version": "1",
"code": "UNKNOWN"
}
}
Verifying the credentials
To verify the credentials, the relying party needs to validate the JWT received from the identity provider.
Credentials format
The relying party receives the credential in a JWT format, containing long text split into common parts:
<header>.<payload>.
The ICP verifiable credentials feature is based on the w3c VC credentials data model.
There is no signature in this first presentation.
When this is decoded, the format is the following:
{
"iss": <issuer>,
"vp": {
"@context": "https://www.w3.org/2018/credentials/v1",
"type": "VerifiablePresentation",
"verifiableCredential": [
"<header>.<payload>.<signature>",
"<header>.<payload>.<signature>"
]
}
}
The field verifiableCredential
within vp
should contain two more verifiable credentials in the specified order:
An
id_alias
credential which links the effective subject of the credential to a temporary alias. This credential is signed by the identity provider.The actual credential requested by the user. The subject of this credential is
id_alias
, and it should be signed by the issuer.
When the id_alias
credential is decoded, the following info is returned:
{
"exp": <expiration>,
"iss": "https://identity.ic0.app/",
"nbf": <not-before>,
"jti": <jwt-id>,
"sub": <relying-party-user-principal>
"vc": {
"@context": "https://www.w3.org/2018/credentials/v1",
"type": [
"VerifiableCredential",
"InternetIdentityIdAlias"
],
"credentialSubject": {
"InternetIdentityIdAlias": {
"hasIdAlias": <id-alias>,
"derivationOrigin": <user-principal-derivation-origin>
}
}
}
}
When the actual credential decoded, the following info is returned:
{
"exp": <expiration>,
"iss": <isuer>,
"nbf": <not-before>,
"jti": <jwt-id>,
"sub": <did-id-alias>,
"vc": {
"@context": "https://www.w3.org/2018/credentials/v1",
"type": [
"VerifiableCredential",
"<credential-type>"
],
"credentialSubject": {
<credential-type>: {
<argument1-key>: <argument1-value>,
<argument2-key>: <argument2-value>,
...
}
}
}
}
Cryptographic verification
To verify the two JWT signatures, verify the signature of the id_alias
signed by the identity provider, and the signature of the actual credential signed by the issuer. Both are found when you decode the received JWT.
These signatures are canister signatures and there is an example on how to verify those in the Internet Identity repository.
Semantic verification
The relying party should verify the following data:
From the received credential JWT:
The
iss
matches the identity provider canister ID.The decoded initial credential contains two credentials within
vp.verifiableCredential
: first theid_alias
credential, then the requested one.
From the
id_alias
JWT:The
iss
is the expected identity provider.The
sub
inid_alias
contains the principal of the user in the relying party.The credential type is
InternetIdentityIdAlias
.The
derivationOrigin
is the derivation origin when logging in the relying party.
From the requested credential JWT:
The credential type within
vc.type
matches the first field invc.credentialSubject
and both are the same that was requested.The arguments within
vc.credentialSubject.<credential-type>
are the expected ones.
Cross credentials:
- The
sub
from the requested credential contains the value in theid_alias
credential fromvc.credentialSubject.InternetIdentityIdAlias
.
- The
To learn more about verifying credentials, check out the demo relying party's code and the helper used from the Internet Identity repository.