This repo is for a hands-on lab that walks through how to use the Verification Service.
- Install Node.js version 20.x locally.
- Clone the Sample App repo.
Install dependencies for the hol-mdl project you just cloned with npm.
npm i
Here you will create a Verification Template that verifies credentials. A Verification Template allows Auth0 to prompt and verify credentials matching a particular criteria.
- Using the left nav go to Credentials > Verification.
- Click + Create Verification Template.

4. Add Fields for querying and mark if you want to retain them or not.

6. Copy the Template ID that is displayed below the Verification Template name and take note of it, as you will use it in another section. The Template ID is prefixed vct_.
- First you will need to copy the Domain value. In order to do that using the left nav go to Applications > Applications and open an existing application. Then go to Settings tab and copy the Domain value.
- Using the left nav go to Applications > APIs.
- Click + Create API.
- In the identifier field make sure to use saved Domain from step 1 and append /vdcs. So the format would be:
https://${DOMAIN}.auth0c.com/vdcs.
Example:https://hol-mdl-xyz.iam-foundations-mdl2.auth0c.com/vdcs - Navigate to Permissions tab and add following permissions (scopes):
read:verification_requestcreate:verification_request
Now, you will create an application within your Auth0 tenant that will receive the verifiable credential API calls from your application.
- Using the left nav go to Applications > Applications.
- Click + Create Application.
- Pick Machine to Machine Applications and click Create.


This section walks you through setting up Auth0 as a verifier in a web application. For this lab, we are using a Next.js application as an example.
Edit the .env.local file, and set the missing values:
AUTH0_DOMAIN: the Domain that you copied in the Create an application sectionAUTH0_CLIENT_ID: the Client ID of the application you created in the Create an application sectionAUTH0_SECRET: the Client Secret of the application you created in the Create an application sectionTEMPLATE_ID: the Template ID from the template you created above in the Create a Verification Template section
The code in its current state implements a web server with a simple UI. The UI has a button that starts the verification process.

- When the Start Presentation Flow button is clicked, the app starts a verification request by making an API call to Auth0. In this API call, the app sends the
clientid,templateidvariables to the API. Auth0 replies with averificationId(Verification ID (UUIDv4) ,engagement(MDoc URI).
Theengagementis what you encode into a QR code for a wallet application to scan and start the process. - The
verificationIdis used to call back to another Auth0 API to check if the user submitted credentials. - Then, the application periodically checks Auth0 by making a separate API call to check if the user has successfully submitted a presentation. It passes the
verificationIdthat was received as a response in step 1, and keeps doing it (polling for 60s) until a response indicating the process is complete is received.
To make this flow work, you will create two endpoints in our application:
/api/verify/start/api/verify/check
When the user clicks the button to start the flow, a call needs to be made to the /api/verify/start endpoint, which will then start an interval timer on the UI to call the /api/verify/check endpoint once per second.
For simplicity, the UI is already wired up to handle calling the backend, different states, loading, error, etc. You only need to implement the two endpoints where the core logic is handled.
In order to interact with the APIs we need to first fetch the token.
- In the root folder create a new folder named utils.
- Create a new file named auth.js in the utils folder.
- Import the node-fetch library, load the environment variables. This code is identical to the previous section:\
import fetch from "node-fetch";
const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;
const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;
const AUTH0_SECRET = process.env.AUTH0_SECRET;
if (!AUTH0_DOMAIN) throw new Error("AUTH0_DOMAIN not set");
if (!AUTH0_CLIENT_ID) throw new Error("AUTH0_CLIENT_ID not set");
if (!AUTH0_SECRET) throw new Error("AUTH0_SECRET not set");- Follow the guidelines from this doc to get the access token for the right audience
let cachedToken = null;
let tokenExpiry = null;
export async function getVdcsBearer() {
if (cachedToken && tokenExpiry && Date.now() < tokenExpiry) return cachedToken;
const response = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
client_id: AUTH0_CLIENT_ID,
client_secret: AUTH0_SECRET,
audience: `https://${AUTH0_DOMAIN}/vdcs`,
grant_type: "client_credentials",
}),
});
const data = await response.json();
cachedToken = data.access_token;
tokenExpiry = Date.now() + data.expires_in * 1000;
return cachedToken;
}This endpoint starts a Presentation Request by making a call to the Auth0 API. The API returns a engagement with the presentation request information for the user's wallet to consume.
A Presentation Request keeps track in Auth0 that the sample app requested a credential from a user.
- Create a new folder named api in the api the pages folder.
- Create a new folder named verify in the api folder you created in the previous step.
- Create a new file named start.js in the pages/api/verify folder.
- You will need to make API calls to Auth0. Add the following snippet to the start.js file. The snippet imports the node-fetch module, which you will use to make HTTP calls to the Auth0 API.
import fetch from "node-fetch";- Import the function for fetching the token.
import { getVdcsBearer } from "../../../utils/auth";- Assign the environment variables from the
.env.localfile to variables. By default, Next.js parses this file and sets the variables on theprocess.envobject.
const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;
const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;
const AUTH0_SECRET = process.env.AUTH0_SECRET;
const TEMPLATE_ID = process.env.TEMPLATE_ID;
if (!AUTH0_DOMAIN) throw new Error("AUTH0_DOMAIN not set");
if (!AUTH0_CLIENT_ID) throw new Error("AUTH0_CLIENT_ID not set");
if (!AUTH0_SECRET) throw new Error("AUTH0_SECRET not set");
if (!TEMPLATE_ID) throw new Error("TEMPLATE_ID not set");- Add the function to handle the HTTP request. This is mostly Next.js boilerplate. The relevant part is the call to
run()that will do the bulk of the work.
export default async function handler(req, res) {
try {
const result = await run();
res.status(200).json(result);
} catch (err) {
res.status(500).send({ error: err.message });
}
}- Define the
run()function. It makes a POST HTTP request to the Auth0 verification API to start a Verifiable Presentation request and returns an object with two variables from the response.verificationId: Verification ID (UUIDv4) as received from the verification initiation. This ID is used for subsequent interactions with the verification API, such as polling for status.engagement: MDoc URI for the webapi protocol. This URI should be presented to the user to initiate the wallet interaction using the protocols. It conforms to the structure defined in ISO/IEC DTS 18013-7 (Annex 4).
async function run() {
const bearerToken = await getVdcsBearer();
const result = await fetch(`https://${AUTH0_DOMAIN}/vdcs/verification`, {
method: "post",
headers: {
"authorization": `bearer ${bearerToken}`,
"content-type": "application/json",
},
body: JSON.stringify({
template_id: TEMPLATE_ID,
protocol: "mdoc/webapi/v1.0",
}),
})
const { verificationId, engagement } = await result.json();
return {verificationId, engagement };
},Once a Presentation Request has been created, the sample verifier app needs to know if the user submitted a credential to Auth0. The app does this by calling the /api/verify/check endpoint periodically. This endpoint calls an Auth0 API to check the status of the request. If the presentation was successful, the API will return the JSON from the presentation.
- Create a new file named check.js in the pages/api/verify folder.
- Import the node-fetch library, function for fetching the token, load the environment variables. This code is identical to the previous section:
import fetch from "node-fetch";
import { getVdcsBearer } from "../../../utils/auth";
const AUTH0_DOMAIN = process.env.AUTH0_DOMAIN;
const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;
const AUTH0_SECRET = process.env.AUTH0_SECRET;
const TEMPLATE_ID = process.env.TEMPLATE_ID;
if (!AUTH0_DOMAIN) throw new Error("AUTH0_DOMAIN not set");
if (!AUTH0_CLIENT_ID) throw new Error("AUTH0_CLIENT_ID not set");
if (!AUTH0_SECRET) throw new Error("AUTH0_SECRET not set");
if (!TEMPLATE_ID) throw new Error("TEMPLATE_ID not set");- The HTTP handler will be very similar to the previous one. However,
verificationIdmust be extracted from the POST body, so the sample app can query Auth0 for the status of the Presentation Request. Then, pass theverificationIdto therun()function where most of the work will be done:
export default async function handler(req, res) {
try {
const verificationId = req.body.verificationId;
const result = await run(verificationId);
res.status(200).json(result);
} catch (err) {
res.status(500).send({ error: err.message });
}
}- Implement the
run()function. It uses the ID of the Presentation Request (verificationId) to check the status of that request in Auth0 and returns the result.
Important
Once the presentation request completes, Auth0 responds to the HTTP request with an object that has a presentation property. The value of this property is a JSON object represented as a string. Before returning the result to the caller, we are turning the string into a JSON object.
async function run(verificationId) {
if (!verificationId) throw new Error("verificationId not found");
const bearerToken = await getVdcsBearer();
const result = await fetch(
`https://${AUTH0_DOMAIN}/vdcs/verification/${verificationId}`,
{
method: "get",
headers: {
"authorization": `bearer ${bearerToken}`,
"content-type": "application/json",
},
}
);
const data = await result.json();
if (data.presentation) {
data.presentation = JSON.parse(data.presentation);
}
return data;
}That is all that is needed to implement verification through Auth0. To test the flow follow these steps.
- In a terminal,
run npm run dev. This should start the app on localhost:3000. - Open the application at http://localhost:3000/.
- Click Start Presentation Flow.

Note
The full completed code for this lab is available on the endstate branch of the repo: https://github.com/Auth0/hol-mdl/tree/endstate
