A digital passport. Verified once, usable everywhere.
Read our documentation or try it!
- Packages
- Installation
- Getting Started
- Verifiable Credential Presentation
- Certificate Authority
- Documentation
- Contributing
| Package | Runtime | Usage | Runtime deps |
|---|---|---|---|
@proof.com/proof-vc-common |
browser or Node | Request a Verifiable Presentation | 0 ✅ |
@proof.com/proof-vc-server |
Node | proof-vc-common plus Presentation Verification, Pushed Authorization Requests, Transaction Templates |
sd-jwt, owf |
Browser / Node:
npm install @proof.com/proof-vc-common
Node:
npm install @proof.com/proof-vc-server
Proof implements the OpenID for Verifiable Presentations 1.0 specification.
Setup an OAuth Application in your Proof account to get your client_id.
You can request a Verifiable Presentation in the browser by using createClient and authorizationUrl to craft an Authorization Request URL.
Either fragment or direct_post Response Mode are supported (defaults to fragment).
- if using
fragment, at the callback URI, useparseAuthorizationResponseto extract thevp_tokenfrom it and send it to your verification endpoint - if using
direct_post, thevp_tokenis sent directly to your verification endpoint
import {
createClient,
parseAuthorizationResponse,
} from "@proof.com/proof-vc-common";
const proof = createClient({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
});
button.onclick = () => {
window.location.href = proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
});
};
const response = parseAuthorizationResponse(); // reads window.location.hash
if (response) {
await fetch("/verify_vp_token", {
method: "POST",
body: JSON.stringify(response),
});
}You can also use buildAuthorizationUrl to provide all the Authorization Request parameters at once:
import { buildAuthorizationUrl } from "@proof.com/proof-vc-common";
window.location.href = buildAuthorizationUrl({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
});You can request a Verifiable Presentation from our backend by using createClient and authorizationUrl to craft an Authorization Request URL.
Either fragment or direct_post Response Mode are supported (defaults to fragment).
With @proof.com/proof-vc-server you can also use Pushed Authorization Requests and Transaction Templates.
import { createClient } from "@proof.com/proof-vc-server";
const proof = createClient({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
responseMode: "direct_post",
});
const redirect = await proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
state: "6A2B4CD830",
loginHint: "frodo.baggins@theshire",
});Proof supports fragment and direct_post response modes (default fragment).
Using fragment the vp_token is returned as a fragment of the callbackUri when the user is 302 redirected from Proof to your website. Use parseAuthorizationResponse() in the browser to read it.
GET https://example.com/verify_vp_token#vp_token=eyJwcm9vZl9pZF9...
Using direct_post the vp_token is returned in the JSON body of a POST request to the callbackUri from Proof to your server. See the OID4VP specification for more details.
POST https://example.com/verify_vp_token
{ "vp_token": "eyJwcm9vZl9pZF9..." }
Proof supports Pushed Authorization Requests (PAR).
You may want to use this feature when using Transaction Templates to avoid hitting URL size limits.
PAR requires a clientSecret and is therefore available only from @proof.com/proof-vc-server.
import { createClient } from "@proof.com/proof-vc-server";
const proof = createClient({
environment: "sandbox",
clientId: "caxdw5a7d",
clientSecret: "…",
callbackUri: "https://example.com/verify_vp_token",
responseMode: "direct_post",
usePushedAuthorizationRequest: true,
});
const redirect = await proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
});Proof issues Verifiable Credentials according to the SD-JWT-VC specification and publishes its OID4VCI Credential Issuer Metadata at https://api.proof.com/.well-known/openid-credential-issuer.
ProofCredentialV1
| claim | type | description |
|---|---|---|
| given_name | string | user's given name as it appears on the verified identity document |
| family_name | string | user's family name as it appears on the verified identity document |
| birthdate | string | user's date of birth in ISO 8601 format (YYYY-MM-DD) |
| age_is_over.18 | boolean | boolean confirming the user is 18 or older |
| age_is_over.21 | boolean | boolean confirming the user is 21 or older |
| age_is_over.65 | boolean | boolean confirming the user is 65 or older |
All attributes are selectively disclosable and will return undefined if the claim wasn't disclosed.
Request a Verifiable Credential Presentation with an OAuth 2.0 Authorization Request. See Frontend and Backend above for the two entry points.
Proof supports the scope parameter of the OID4VP specification. Each scope maps to a pre-defined DCQL query and returns a specific Credential Type.
Supported scope and their associated Credential Type:
| scope | Credential Type |
|---|---|
urn:proof:params:scope:verifiable-credentials:basic |
ProofCredentialV1 |
Transaction Templates allow you to bind specific data to a Verifiable Credential Presentation. Proof uses the Transaction Data parameter of the OID4VP specification. The data is shown to the user during the Presentation flow and the user signs it with a Key Binding JWT (KB-JWT). The KB-JWT is returned as part of the Presentation.
Transaction data is sensitive and is therefore attached only from @proof.com/proof-vc-server. The following Transaction Templates are available:
urn:proof:params:vc:transaction-data:wire-instructions:v1
import { createClient, transactionData } from "@proof.com/proof-vc-server";
const proof = createClient({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
});
const data = transactionData.wireInstructions({
recipient: {
institution_name: "Crestline Financial",
individual_name: "Acme Corp LLC",
routing_number: "055000123",
account_number: "7293",
},
source: {
institution_name: "Sterling & Union",
individual_name: "Sterling & Union",
account_number: "4821",
routing_number: "091000456",
},
amount: 5000,
currency: "USD",
memo: "Invoice #2024-089",
});
const redirect = await proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
state: "6A2B4CD830",
loginHint: "frodo.baggins@theshire",
transactionData: data,
});urn:proof:params:vc:transaction-data:payment-itemized:v1
import { createClient, transactionData } from "@proof.com/proof-vc-server";
const proof = createClient({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
});
const data = transactionData.paymentItemized({
title: "Drive Shaft",
description: "The Roadhouse (18+), May 6 2026",
currency: "USD",
items: [
{ quantity: 2, unit_cost: 40.0, label: "General Admission" },
{ quantity: 2, unit_cost: 11.4, label: "Fees" },
],
});
const redirect = await proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
state: "6A2B4CD830",
loginHint: "frodo.baggins@theshire",
transactionData: data,
});urn:proof:params:vc:transaction-data:payment-mandate:v1
import { createClient, transactionData } from "@proof.com/proof-vc-server";
const proof = createClient({
environment: "sandbox",
clientId: "verifier-demo",
callbackUri: "https://example.com/verify_vp_token",
});
const data = transactionData.paymentMandate({
payment_instrument: {
type: "wallet",
id: "did:example:visa-token-7829",
description: "Visa ••••7829",
},
payee: {
id: "did:example:summitco",
name: "Summit Co",
website: "summitco.com",
},
prompt_summary:
"Find me a 4-season backpacking tent from Summit Co under $500",
amount: 500,
currency: "USD",
});
const redirect = await proof.authorizationUrl({
nonce: "3e8e4918-e9fb-453a-a538-81152be15c1b",
scope: "urn:proof:params:scope:verifiable-credentials:basic",
state: "6A2B4CD830",
loginHint: "frodo.baggins@theshire",
transactionData: data,
});Verification runs server-side. Create a verifier bound to a trust root and reuse it.
Decode and verify a Verifiable Presentation's vp_token:
import { createVerifier } from "@proof.com/proof-vc-server";
const verifier = createVerifier({ trustRoot: "production" });
const vpToken = "eyJwcm9vZl9pZ...";
const presentation = await verifier.verifyVPToken({ encodedVPToken: vpToken });
const verifiableCredential = presentation["proof_id_default"][0];
if (verifiableCredential.isOver18) {
purchaseItem();
} else {
userNotOver18();
}Verify a single SD-JWT-VC:
import { createVerifier } from "@proof.com/proof-vc-server";
const verifier = createVerifier({ trustRoot: "production" });
const encodedSDJWT = "eyJraWQiOiI3...";
const verifiableCredential = await verifier.verify({ encodedSDJWT });
if (verifiableCredential.isOver18) {
purchaseItem();
} else {
userNotOver18();
}Validating the nonce is out of scope of verify and verifyVPToken. The nonce signed in the Key Binding JWT is exposed on the returned credential and should be validated by the caller against the nonce sent in the Request:
const verifiableCredential = await verifier.verify({ encodedSDJWT });
if (
verifiableCredential.getNonce() !== "3e8e4918-e9fb-453a-a538-81152be15c1b"
) {
throw new Error("nonce mismatch");
}Proof's Verifiable Credentials are issued by our Certificate Authority following the CA/B Forum Baseline Requirements for the Issuance and Management of Publicly-Trusted TLS Server Certificates published at https://www.cabforum.org.
The Proof Root CA R1 Certificate is published at http://cert.proof.com/proof-root-ca-r1.crt and is also committed in this repository proof-root-ca-r1.crt.
The sandbox Root CA R1 Development certificate is also committed in this repository proof-root-ca-r1-development.crt and used when trustRoot: "development".
Digital Credentials guides https://dev.proof.com/docs/digital-credentials-overview
API Documentation https://dev.proof.com/reference/authorizeverifiablecredentialpresentation