A minimal REST service that demonstrates signing and verification functionality. It relies on sign-and-verify-core
The REST service implements a subset of the W3C CCG vc-api draft standard.
Follow these steps to properly configure a sign-and-verify
service deployment for your organization:
- Configure the service with the following environment variables:
- *
AUTH_TYPE
: the mechanism by which to authorize access to credential (required) - *
DID_SEED
: a secret seed used to generate DID document for issuer (required) OIDC_ISSUER_URL
: OIDC issuer discovery URL (required)ISSUER_MEMBERSHIP_REGISTRY_URL
: location of registry used to confirm issuer's membership status in DCC (required)DID_WEB_URL
: the url used to generatedid:web
document and keys for issuer (optional, default:undefined
)PORT
: the port the web service will bind to (optional, default:5000
)DB_USER
: database client username (optional)DB_PASS
: database client password (optional)DB_HOST
: database client hostname (optional)DB_NAME
: database name (optional)DB_COLLECTION
: database credentials collection name (optional)ENABLE_HTTPS_FOR_DEV
: set totrue
to enable https for a local dev instance, using 'server-dev-only.cert' and 'server-dev-only.key` It goes without saying that this is NOT for production. It was introduced to make it easier to test against the JFF VC-API tests, which require an https endpoint.
-
Copy
.env.example
to.env
, whichnpm run start
will pick up, to test these values. -
Modify the
DatabaseClient
class in./src/database.ts
to suit your organization's DBMS deployment infrastructure -
Modify the content of
./src/issuer.ts
to suit your organization's DBMS/OIDC deployment infrastructure -
Run
npm run setup
oryarn setup
and follow output deployment instructions (please use Node version 14 or higher)
NOTE: AUTH_TYPE
accepts the following values:
oidc_token
: retrieves email fromuserinfo
endpoint using OIDC token and fetches matching credentialvp_challenge
: fetches credential with matching VP challenge
NOTE: the DID_SEED
included as an example is just for your reference. Do not check in production did seeds, private keys, or the equivalent.
npm install
npm run build
npm run start
npm run test
This assumes familiarity with the basics of the W3C Verifiable Credentials Data Model. Two key concepts are:
The REST API exposed by this service implements a subset of the vc-api draft standard and also includes some non-standard convenience endpoints.
The vc-api can be confusing when getting started, partly because it contains APIs for issuer, holders, and verifiers. Actual deployments would involve different endpoints for differely roles; or, put differently, a single instance of this service is not intended to be used by issuers, holders, and verifiers. The vc-api currently lacks high-level documentation, so this README provides verbose descriptions about what these APIs are used for. But these APIs ultimately (should eventually) comply with vc-api.
For issuers when signing a Verifiable Credential.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"credential": <Verifiable Credential To Sign>, \
"options": <Signing Options>' \
<sign-and-verify-service>/issue/credentials
Request:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"credential": {"@context":["https://www.w3.org/2018/credentials/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"id":"http://example.gov/credentials/3732","type":["VerifiableCredential"],"issuer":"did:web:digitalcredentials.github.io","issuanceDate":"2020-03-10T04:24:12.164Z","credentialSubject":{"id":"did:example:abcdef"}}, "options": {"verificationMethod":"did:web:digitalcredentials.github.io#z6MkrXSQTybtqyMasfSxeRBJxDvDUGqb7mt9fFVXkVn6xTG7"}}' \
http://127.0.0.1:5000/issue/credentials
Response Codes:
- 201: success (with signed Verifiable Credential)
- 400: invalid input
- 500: error
Reference: vc-api /issue/credential
For verifiers to verify (check the proof) of a Verifiable Presentation (VP).
Current DCC implementations also use this endpoint for a special case of VP verification, to implement a lightweight version of DID auth. The learner's wallet generates a VP proving control over the DID (it's a VP without a VC), and the issuer checks the proof.
Additional implementation details are Overview of Credential Request Flow
curl --header "Content-Type: application/json" \
--request POST \
--data '{"verifiablePresentation": <Verifiable Presentation>, \
"options": <Verification Options>' \
<sign-and-verify-service>/verify/presentations
Request:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"verifiablePresentation": {"@context":["https://www.w3.org/2018/credentials/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"type":["VerifiablePresentation"],"id":"123","holder":"did:key:z6MkoSu3TY7zYt7RF9LAqXbW7VegC3SFAdLp32VWudSfv8Qy","proof":{"type":"Ed25519Signature2020","created":"2021-05-01T23:38:10Z","verificationMethod":"did:key:z6MkoSu3TY7zYt7RF9LAqXbW7VegC3SFAdLp32VWudSfv8Qy#z6MkoSu3TY7zYt7RF9LAqXbW7VegC3SFAdLp32VWudSfv8Qy","proofPurpose":"authentication","challenge":"test123","proofValue":"z3Ukrcvwg59pPywog48R6xB6Fd5XWmPazqPCjdpaXpdKzaeNAc1Un1EF8VnVLbf4nvRk5SGiVDvgxddS66bi7kdAo"}}, "options": {"verificationMethod":"did:web:digitalcredentials.github.io#z6MkrXSQTybtqyMasfSxeRBJxDvDUGqb7mt9fFVXkVn6xTG7","challenge":"test123"}}' \
http://127.0.0.1:5000/verify/presentations
Response Codes:
- 200: success
- Specifically, it means the API request was successful AND the VP was verified
- VerificationResult details below
- 400: invalid input
- 500: error
Note: VerificationResult from vc-api isn't especially helpful at the moment, so we pass along non-standard verification metadata. Response code 200 means it's successfully verified. Additionally, in case of success, we return the non-standard holder
field for convenience. In this example, the VerificationResult is:
{
holder : did:web:digitalcredentials.github.io
}
Reference: vc-api /verify/presentations
Used to verify a Verifable Credential.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"verifiableCredential": <Verifiable Credential>, \
"options": <Verification Options>' \
<sign-and-verify-service>/verify/credentials
Request:
curl --header "Content-Type: application/json" \
--request POST \
--data '{"verifiableCredential": {"@context":["https://www.w3.org/2018/credentials/v1","https://w3id.org/security/suites/ed25519-2020/v1"],"id":"http://example.gov/credentials/3732","type":["VerifiableCredential"],"issuer":"did:web:digitalcredentials.github.io","issuanceDate":"2020-03-10T04:24:12.164Z","credentialSubject":{"id":"did:example:abcdef"},"proof":{"type":"Ed25519Signature2020","created":"2021-05-04T18:59:42Z","verificationMethod":"did:web:digitalcredentials.github.io#z6MkrXSQTybtqyMasfSxeRBJxDvDUGqb7mt9fFVXkVn6xTG7","proofPurpose":"assertionMethod","proofValue":"z4jnMia8Q1EDAQDNnurAnQgNmc1PmhrXx87j6zr9rjvrpGqSFxcHqJf55HjQPJm7Qj712KU3DXpNF1N6gYh77k9M3"}}, "options": {"verificationMethod":"did:web:digitalcredentials.github.io#z6MkrXSQTybtqyMasfSxeRBJxDvDUGqb7mt9fFVXkVn6xTG7"}}' \
http://127.0.0.1:5000/verify/credentials
Response Codes:
- 200: success, and Verifiable Credential successfully verified.
- 400: invalid input
- 500: error
Note: VerificationResult from vc-api isn't especially helpful at the moment, so we pass along verification metadata. But response code 200 means it's successfully verified.
<REQUEST_PAYLOAD>
is a Verifiable Presentation proving control of the did. See details below.
curl --header "Content-Type: application/json" \
--request POST \
--data <REQUEST_PAYLOAD> \
http://127.0.0.1:5000/request/democredential
curl --header "Content-Type: application/json" \
--request POST \
--data '{"holder": "did:example:me"}' \
http://127.0.0.1:5000/request/democredential/nodidproof
The vc-api standard doesn't include specific methods related to credential request. Similarly, the credential request protocol is not overly-specified by DCC, to allow issuers flexibility to adapt it to their processes. But there are certain request/response elements expected by the DCC wallet, which this service can help with. This section describes how the DCC credential request flow relates to sign-and-verify
A credential request coming from the DCC wallet will be of the form:
curl --header "Content-Type: application/json" \
--header "Authorization: Bearer <TOKEN>" \
--request POST \
--data <REQUEST_PAYLOAD> \
<request_endpoint>
request_endpoint
is provided by the issuer as part of the DEEP_LINK to the DCC wallet. The DCC wallet will parse it from the DEEP_LINK and call it during the credential request.
REQUEST_PAYLOAD
has structure described by the generate proof of control response. It's recommended that issuers verify several aspects of payload; relevant fields are highlighted below.
{
"@context": ...
...
"type": ["VerifiablePresentation"],
"holder": <HOLDER_DID>,
"proof": {
"challenge": "<Expected 1-time challenge>",
"verificationMethod": {
"id": <Used for VP verification options>
}
...
}
}
About REQUEST_PAYLOAD
:
HOLDER_DID
is the subject DID the issuer would issue the credential tochallenge
is expected to match the challenge the issuer previously provided in the linksproof.verificationMethod.id
will be used as an argument when verifying the VP
It's recommended that the issuer verify the REQUEST_PAYLOAD
provided by the learner's DCC wallet before issuing the credential
Issuers can use the /verify/presentations
endpoint described above to verify the DID contained in the subject's credential request.
The general structure of a /verify/presentations
call looks like this:
curl --header "Content-Type: application/json" \
--request POST \
--data '{ verifiablePresentation: "<REQUEST_PAYLOAD>", \
options: { \
verificationMethod: "<REQUEST_PAYLOAD.proof.verificationMethod.id>", \
challenge: "<Expected 1-time challenge>" }' \
<sign-and-verify-endpoint>/verify/presentations
As described in Verify Presentation, response code 200 means it's successfully verified. Additionally, in case of success, we return the non-standard holder
field for convenience. In this example, the VerificationResult is:
{
holder : did:web:digitalcredentials.github.io
}
This generates a sample payload that would come from a wallet
curl --header "Content-Type: application/json" \
--request POST \
--data '$PARAMS' \
<sign-and-verify-service>/generate/controlproof
Where PARAMS looks like:
PARAMS = {
"presentationId": "456",
"holder": "did:web:digitalcredentials.github.io",
"verificationMethod": "did:web:digitalcredentials.github.io#z6MkrXSQTybtqyMasfSxeRBJxDvDUGqb7mt9fFVXkVn6xTG7",
"challenge": "123"
}
Use following configurations for creating a new AWS EC2 instance:
- Image: Ubuntu 20.04
- Instance size:
t2.medium
- Storage:
32GB
- Security group: Add a target tcp for port 5000 and allow access from
0.0.0.0/0, ::/0
(both ipv4 and ipv6 addresses)
During the setup, you'll receive a .pem
-file that you need to use to connect to the instance.
This is specific to users who use Ubuntu as their local machines, users of Windows / MacOS might need to adapt commands. Details for anyone can be found here.
- Go to your folder
~/.ssh/
or create it, if not available (mkdir ~/.ssh/
). - Edit or create the
config
file to include following lines. Also replaceIP_ADDRESS
with the IP-address of your instance as well check if the path to your.pem
file is correct.
Host sign-and-verify
HostName IP_ADDRESS
Port 22
user ubuntu
IdentityFile ~/.ssh/PEMFILE.pem
- Connect to your instance by executing
ssh sign-and-verify
Prepare your instance by executing following steps:
- Update the machine by executing
sudo apt update -y && sudo apt upgrade -y
(Questions about keeping the old config can be answered with yes; only means that a ssh-login with password is disabled)
Please follow the Docker installation guide for Ubuntu.
If you want to work with a non-root user without using sudo
, log in to your instance with the respective user and execute following commands. Source
sudo groupadd docker
sudo gpasswd -a $USER docker
- Either do a
newgrp docker
or log out/in to activate the changes to groups.
- Clone the
sign-and-verify
repo withgit clone https://github.com/digitalcredentials/sign-and-verify.git
Copy the docker-compose example file:
cp docker-compose.example.yml docker-compose.yml
Optional: If you want to have a automatically generated TLS-certificate provided by Let's Encrypt, then instead copy the .yml
file from the Docker
folder to your main folder by
-
cp Docker/docker-compose-acme.yml docker-compose.yml
-
Change the
.env
variables to your liking; first, rename the.env.example
to.env
bymv .env.example .env
-
Second, edit the
.env
file with vim (cheatsheet) or your preferred editor. Variable descriptions can be found here. (In case you change the port in the.env
file, you need to also change the ports a) in AWS and b) in thedocker-compose.yml
file.) -
Check if the
.dockerignore
file includes.env
. If so, remove it. -
Start the docker container by
docker-compose up -d
- Get the docker logs with
docker logs sign-and-verify
- Attach (observe the log) to docker by
docker attach --sig-proxy=false signandverify
(cancel with Ctrl+C) - Shut down the Docker container with
docker-compose down
If everything works as intended, then you should be able to execute any of the above-mentioned commands. The sign-and-verify
service is available at YOUR_IPADDRESS:3000
. If you use SSL/TLS-certificates, your service will be available at https://yourdomain.edu
on standard port 443.
Please check step 6 for an automatic TLS certificate with Let's Encrypt and ACME.
The docker-container might not rebuild if the git-repository is updated. To update, follow these commands:
docker-compose down
git pull
- Check if your
.env
-file has all the contents required for running, e.g., compare with the.env.example
-file docker-compose up -d --build