Learn how to build a web application that can verify mDocs
Introduction
In this tutorial we will use the Verifier Web SDK and a MATTR VII tenant to build a web application that can verify an mDoc presented via an online presentation workflow as per ISO/IEC 18013-7:2024 and OID4VP.
This web application will support both same-device and cross-device workflows to accommodate flexible user journeys.
- The user interacts with your web application on their mobile device browser.
- The user is asked to present information as part of the interaction.
- The user is redirected to their wallet application.
- The wallet application authenticates the user.
- The user is informed of what information they are about to share and provide their consent.
- The user is redirected back to the web application where verification results are displayed, enabling them to continue with the interaction.
The result will look something like this:
Prerequisites
Before we get started, letβs make sure you have everything you need.
Prior knowledge
- We recommend you make yourself familiar with the following concepts to support your
understanding of the concepts included in this tutorial:
- What is Credential verification?
- What are mDocs?
- What steps does an online verification workflow comprise?
Assets
- You will need access to a MATTR VII tenant and obtain an access token that enables you to make API requests.
We recommend using the MATTR VII Postman collection in this tutorial. While this isnβt an explicit prerequisite it can really speed things up.
Testing device
- You will need an mDoc stored on a mobile device with an application that supports ISO/IEC 18013-7:2024 (online presentation of mDocs). We recommend downloading the MATTR GO Hold example app and using it to claim an mDoc.
Development environment
- Download and install npm.
- Install ngrok and sign up for an account. We will use ngrok to test the presentation workflow.
- You will need a code editor such as VS Code.
Got everything? Letβs get going!
Overview
The following diagram depicts the workflow we will build in this tutorial:
- The user triggers the workflow by interacting with the web application.
- The web application uses the embedded Verifier Web SDK capabilities to start a presentation-based verification session with the configured MATTR VII tenant.
- The MATTR VII tenant responds with a link to invoke a matching mobile application.
- The web application uses the link to invoke a matching mobile application, by either using a redirect (same-device) or rendering a QR code (cross-device).
- The mobile application makes a request to the MATTR VII tenant to retrieve a request object, defining what information is requested for verification.
- The MATTR VII tenant returns the request object to the mobile application.
- The mobile application (upon user consent) returns an authorization response to the MATTR VII tenant, which includes the information required for verification.
- The MATTR VII tenant returns the verification results to the web application.
- The web application surfaces the verification results to the user and the interaction continues.
We will build this workflow in three parts:
-
Part 2: Build a web application with mDocs verification capabilities.
-
Part 3: Integrate a backend (optional).
Part 1: Setup the MATTR VII Verifier tenant
The MATTR VII tenant will be used to interact with your web application (generating a verification request) and the wallet application (presenting an mDoc for verification) as per OID4VP and ISO/IEC 18013-7. To enable this, you must:
- Create a verifier application configuration: Define what applications can create verification sessions with the MATTR VII tenant, and how to handle these requests.
- Create a trusted wallet provider configuration: Define how to invoke specific wallet applications as part of an online verification workflow.
- Configure a trusted issuer: The MATTR VII verifier tenant will only accept mDocs issued by these trusted issuers.
Create a verifier application configuration
Each MATTR VII tenant can interact with multiple verifier applications, and can handle requests differently for each application. This means we must create a verifier application configuration that defines how to handle verification requests from your web application.
Request
Make the following request to your MATTR VII tenant to create a verifier application configuration:
POST /v2/presentations/applications
Request body
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "place-holder.com",
"openid4vpConfiguration": {
"supportedMode": "all",
"redirectUris": ["https://place-holder.com"],
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": true
}
name
: You can use whatever name youβd like, as long as it is unique on your tenant.type
: We will useweb
as we are building a web application.domain
: Here we define the web application domain name so that the MATTR VII tenant can verify incoming requests are from known and trusted applications. Aslocalhost
is not supported we will use a tunneling service (ngrok) to test this tutorial. We will update this placeholder value later.openid4vpConfiguration
:supportedMode
: Setting this toall
indicates that your web application will support both same-device and cross-device workflows.redirectUris
: This is the URI the user would be redirected to after presenting a credential on a same-device flow. We will update this placeholder value later.display
: Adjust the iframe modal appearance in cross-device flows:logoImage
:url
: Insert any publicly available URL of a logo image which is displayed on the top left corner of the iframe modal.altText
: Edit the logo image alternative text.
headerText
: Edit the header displayed in the iframe modal.bodyText
: Edit the text displayed in the iframe modal.
resultAvailableInFrontChannel
: Setting this totrue
makes the verification results available directly to the web application. We will change this setting later when we integrate a backend into the workflow.
Response
{
"id": "0eaa8074-8cc4-41ec-9e42-072d36e2acb0",
"name": "My Verifier Web Application"
//... rest of application configuration
}
id
: We will use this value later to initialize the SDK so that requests coming from your web application can be recognized and trusted by the MATTR VII tenant.
Create a trusted wallet provider configuration
Verifier web applications can define specific wallet applications to accept mDocs from as part of their verification workflows. The MATTR VII verifier tenant needs to be configured with a specific URI scheme that will be used to invoke these wallets.
Request
Make the following request to your MATTR VII tenant to create a trusted wallet provider configuration:
POST /v2/presentations/wallet-providers
Request body
{
"name": "MATTR GO Hold",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
name
: Unique name to identify this trusted wallet provider.authorizationEndpoint
: URI scheme that will be used to invoke the wallet application. You can learn more about URI scheme selection logic here.
The authorizationEndpoint
configured in the example above (mdoc-openid4vp://
) is the default
OID4VP scheme. While this is technically redundant, we chose to include this step to explain how
to configure this endpoint for wallet application using different schemes.
Response
{
"id": "99890c34-e4b7-4a23-84d6-e5de57114c00",
"name": "MATTR GO Hold",
"openid4vpConfiguration": {
"authorizationEndpoint": "mdoc-openid4vp://"
}
}
id
: We will use this value later to indicate this is the wallet the web application expects to receive mDocs from.
Configure a trusted issuer
You must configure trusted issuers on your MATTR VII verifier tenant, as presented mDocs will only be verified if they had been issued by a trusted issuer.
This is achieved by providing the PEM certificate of the IACA used by these issuers to sign mDocs. In production environments the issuer can provide it out of band or you can obtain it via their issuerβs metadata.
Request
Make the following request to your MATTR VII tenant to configure a truster issuer:
POST /v2/credentials/mobile/trusted-issuers
Request body
{
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIICYzCCAgmgAwIBAgIKXhjLoCkLWBxREDAKBggqhkjOPQQDAjA4MQswCQYDVQQG\nEwJBVTEpMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0Ew\nHhcNMjQwMTE4MjMxNDE4WhcNMzQwMTE1MjMxNDE4WjA4MQswCQYDVQQGEwJBVTEp\nMCcGA1UEAwwgbW9udGNsaWZmLWRtdi5tYXR0cmxhYnMuY29tIElBQ0EwWTATBgcq\nhkjOPQIBBggqhkjOPQMBBwNCAASBnqobOh8baMW7mpSZaQMawj6wgM5e5nPd6HXp\ndB8eUVPlCMKribQ7XiiLU96rib/yQLH2k1CUeZmEjxoEi42xo4H6MIH3MBIGA1Ud\nEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRFZwEOI9yq\n232NG+OzNQzFKa/LxDAuBgNVHRIEJzAlhiNodHRwczovL21vbnRjbGlmZi1kbXYu\nbWF0dHJsYWJzLmNvbTCBgQYDVR0fBHoweDB2oHSgcoZwaHR0cHM6Ly9tb250Y2xp\nZmYtZG12LnZpaS5hdTAxLm1hdHRyLmdsb2JhbC92Mi9jcmVkZW50aWFscy9tb2Jp\nbGUvaWFjYXMvMjk0YmExYmMtOTFhMS00MjJmLThhMTctY2IwODU0NWY0ODYwL2Ny\nbDAKBggqhkjOPQQDAgNIADBFAiAlZYQP95lGzVJfCykhcpCzpQ2LWE/AbjTGkcGI\nSNsu7gIhAJfP54a2hXz4YiQN4qJERlORjyL1Ru9M0/dtQppohFm6\n-----END CERTIFICATE-----"
}
certificatePem
: This is the IACA identifying the MATTR Labs demo issuer which issues the credential referenced in the Testing device prerequisite.
If you intend to test this tutorial with a credential different than the one recommended in our
Testing device prerequisite, replace the certificatePem
value with your own
issuerβs IACA.
Response
A successful 201
response indicates that this issuerβs certificate was added to your MATTR VII
tenantβs trusted issuerβs list. This means that mDocs that use this IACA as their root certificate
can be trusted and verified.
Part 2: Build a web application with mDocs verification capabilities
Now that the MATTR VII verifier tenant is properly configured, we can proceed with the steps required to embed verification capabilities into your web application:
- Setup your environment: Setup the required infrastructure for your web application.
- Update the domain and redirect URI: Once your tunneling service is up and running you can update the placeholder values in the MATTR VII verifier application configuration.
- Initialize the SDK: So that the SDK functions are available in your web application.
- Create a credential query: Define what information is required for verification and how to handle the interaction with the user.
- Handle verification results: Create the logic that enables your web application to retrieve verification results in different workflows.
Setup your environment
- Open your terminal and create a new NextJS default project:
npx create-next-app@latest --src-dir --yes
- Approve installing any required packages.
- Insert a name for your project (e.g.
my-verifier-web-application
). - Open the project appβs folder:
cd my-verifier-web-application
- Install the SDK:
npm install @mattrglobal/verifier-sdk-web
- Open the
src/app/page.tsx
file in your preferred code editor and replace all existing code with the following:
'use client'
// Step 3.2 - Add dependencies
export default function Home() {
// Step 5.1 - Store results state
// Step 6.1 - Create createRequest function
// Step 4.1 - Create requestCredentials function
// Step 5.3 - Handle same-device redirect
// Step 6.3 - Create retrieveResults function
// Step 3.3 - Initialize the SDK
// Step 5.4 - Add effect to handle redirect
return (
<div className="max-w-2xl mt-12 mx-auto px-8">
<h1 className="text-3xl font-bold my-8">mDocs Online Verification Tutorial</h1>
{/* Step 4.3 - Add request button */}
{/* Step 5.5 - Render results */}
</div>
)
}
This will serve as the basic structure for your application. We will copy and paste
different code snippets into specific locations to achieve the different functionalities.
These locations are indicated by comments that reference the corresponding tutorial step (e.g. // Step 3.2 - Add dependencies
).
We recommend copying and pasting the comment text to easily locate it in the code.
- Run the project:
npm run dev
This will run the app and make it available at http://localhost:3000 (or the next available port if 3000 is already used).
- Start an ngrok tunnel in a new terminal window:
ngrok http http://localhost:3000
If your web application is running on a port different than 3000, use that value in the ngrok command above.
Your terminal window should show the tunnel details:
- Make note of the
Forwarding
value, we will use it in the next step.
Update the domain and redirect URI
Now that our tunneling service is up and running, we can update the MATTR VII verifier application configuration with the domain and redirect URI details.
Make the following request to your MATTR VII tenant to update your verifier application configuration:
PUT /v2/presentations/applications/{id}
id
: Replace with theid
from the response returned when you created the verifier application configuration.
Request body
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "b0de-3-24-117-154.ngrok-free.app",
"openid4vpConfiguration": {
"supportedMode": "all",
"redirectUris": ["https://b0de-3-24-117-154.ngrok-free.app"],
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": true
}
The request body should be identical to the original request with the exception of the following properties:
domain
: Replace with your ngrok tunnelForwarding
URL, removing thehttp://
prefix.redirectUris
: Replace with your ngrok tunnelForwarding
URL.
Initialize SDK
-
Create a new file named
.env.local
in the projects root folder and add the following code to add your tenantβs URL to the projectβs variables:NEXT_PUBLIC_TENANT_URL=https://learn.vii.au01.mattr.global
NEXT_PUBLIC_TENANT_URL
: Replace the parameter value with yourtenant_url
.
-
Copy and paste the following code under the
Step 3.2 - Add dependencies
comment to add required dependencies to your web application:tsximport * as MATTRVerifierSDK from '@mattrglobal/verifier-sdk-web' import { useCallback, useEffect, useState } from 'react'
-
Copy and paste the following code under the
Step 3.3 - Initialize the SDK
comment to create a new function that will be used by your web application to initialize the SDK:tsxuseEffect(() => { MATTRVerifierSDK.initialise({ apiBaseUrl: process.env.NEXT_PUBLIC_TENANT_URL as string, applicationId: '0eaa8074-8cc4-41ec-9e42-072d36e2acb0' }) }, [])
The function calls the SDKβs initialise function that makes the SDK methods available in your web application while defining the following parameters:
apiBaseUrl
: This indicates to the Verifier Web SDK what MATTR VII tenant to interact with in order to verify presented mDocs. It is retrieved from theNEXT_PUBLIC_TENANT_URL
project variable defined in step 1 above.applicationId
: This will be used by the MATTR VII tenant to identify verification requests from your web application. Replace with theid
from the response returned when you created the verifier application configuration.
Create credential request
Now that we have a function and a UI in place, we need to define what the function does by creating a credential request. This request will define what information is requested from the user, and how to interact with the MATTR VII verifier tenant.
-
Copy and paste the following code under the
Step 4.1 - Create requestCredential function
comment to create a newrequestCredentials
function:tsxconst requestCredentials = useCallback(async () => { const credentialQuery = { profile: MATTRVerifierSDK.OpenidPresentationCredentialProfileSupported.MOBILE, docType: 'org.iso.18013.5.1.mDL', nameSpaces: { 'org.iso.18013.5.1': { age_over_18: {}, given_name: {}, family_name: {}, portrait: {} } } } as MATTRVerifierSDK.CredentialQuery // Step 4.2 - Add credential request }, [])
This function currently includes a
credentialQuery
variable that defines what information is requested from the user for verification:This example credential query will request the
age_over_18
,given_name
,family_name
andportrait
claims from a credential of profilemobile
and docTypeorg.iso.18013.5.1.mDL
, under theorg.iso.18013.5.1
namespace. Presenting a credential with different names, docType or namespace will fail verification. -
Copy and paste the following code under the
Step 4.2 - Add credential request
comment so that the newrequestCredentials
function calls the SDKβsrequestCredentials
function, passing thecredentialQuery
variable as a parameter:tsxconst options: MATTRVerifierSDK.RequestCredentialsOptions = { credentialQuery: [credentialQuery], // Step 6.2 - Use challenge from backend challenge: MATTRVerifierSDK.utils.generateChallenge(), redirectUri: window.location.origin, crossDeviceCallback: { onComplete: (callbackResponse) => { // Step 5.2 - Retrieve cross-device verification results }, onFailure: (error) => { if (error.type === 'Abort') { alert('The request was aborted') return } alert(`There was an error processing the request - ${error.message}`) } } } await MATTRVerifierSDK.requestCredentials(options)
options
: This object defines the required parameters for calling the SDKβsrequestCredentials
function. It is passed as a parameter to the function when calling it.credentialQuery
: Defines what information is requested from the user for verification. In our example we are using the credential query created in the previous step.challenge
: Unique challenge generated by the SDK to identify this specific presentation session. We will use it in part 3 of the tutorial.redirectUri
: Indicates that the user will be redirected to the same web application screen when completing a same-device workflow. It can be any other path on your application domain, as long as it is handled by the application.crossDeviceCallback
: We will update this property later to configure how the web application handles the callback when a response is received as part of a cross-device workflow.
-
Copy and paste the following code under the
Step 4.3 - Add request credentials button
comment to create a new button that the user will use to interact with the web application:tsx<button onClick={requestCredentials} className="px-4 py-2 rounded border-2 border-black font-bold" > Request Credential </button>
Once the user selects this button, the web application will trigger the verification workflow by calling the
requestCredentials
function created above:- This function uses calls the SDKβs
requestCredentials
method which starts a presentation session with the configured MATTR VII session. - Once a session is established, the MATTR VII verifier tenant will interact with the userβs wallet application to request and verify the details provided in the query.
- When the verification process is complete, the MATTR VII verifier tenant will return the verification results to your web application.
- This function uses calls the SDKβs
Handle verification results
-
Copy and paste the following code under the
Step 5.1 - Store results state
comment to create a variable that will store the verification state:tsxconst [results, setResults] = useState<MATTRVerifierSDK.PresentationSessionResult | null>(null)
-
Copy and paste the following code under the
Step 5.2 - Retrieve cross-device verification results
comment to retrieve verification results when they are available in cross-device workflows (onComplete
ofrequestCredentials
):tsxif ('result' in callbackResponse) { // Step 6.4 - Call retrieveResults function on cross-device callback setResults(callbackResponse.result as MATTRVerifierSDK.PresentationSessionResult) }
-
Copy and paste the following code under the
Step 5.3 - Handle same-device redirect
comment to create a newhandleRedirect
function:tsxconst handleRedirect = useCallback(async () => { const result = await MATTRVerifierSDK.handleRedirectCallback() if (result.isErr()) { alert(`Error retrieving results: ${result.error.message}`) return } const presentationResult = result.value.result if (!('sessionId' in presentationResult) || 'error' in presentationResult) { alert('Something went wrong') return } // Step 6.5 - Call retrieveResults function on same-device redirect setResults(presentationResult as MATTRVerifierSDK.PresentationSessionResult) }, [])
This function calls the SDKβs handleRedirectCallback function, which returns the verification results as the user is redirected following a same-device presentation workflow. These results (
result.value.result
) are stored by the web applicationhandleRedirect
function in thepresentationResult
variable. -
Copy and paste the following code under the
Step 5.4 - Add effect to handle redirect
comment to add an effect that calls thehandleRedirect
function when the web application is refreshed with a hash, indicating a redirect after completing a same-device presentation workflow:tsxuseEffect(() => { if (window.location.hash) { handleRedirect() } }, [handleRedirect])
-
Copy and paste the following code under the
Step 5.5 - Render results
comment to retrieve different properties of theresults
object once it is available, and display them to the user as part of the interaction:tsx<h2 className="text-xl font-bold mt-8 mb-4">Results</h2> { !results && 'No results available' } { results && 'credentials' in results && ( <div className="flex flex-col gap-2"> <div className="flex flex-row"> <div className="w-1/3 font-bold">Verification Result</div> <div className="w-2/3"> {results.credentials?.[0].verificationResult.verified ? 'verified' : 'not verified'} </div> </div> <div className="flex flex-row"> <div className="w-1/3 font-bold">Valid Until</div> <div className="w-2/3">{results.credentials?.[0].validityInfo.validUntil}</div> </div> <div className="flex flex-row"> <div className="w-1/3 font-bold">Issuer</div> <div className="w-2/3">{results.credentials?.[0].issuerInfo.commonName}</div> </div> <div className="flex flex-row"> <div className="w-1/3 font-bold">Given Name</div> <div className="w-2/3"> {(results.credentials?.[0].claims?.['org.iso.18013.5.1'].given_name .value as string) ?? ''} </div> </div> <div className="flex flex-row"> <div className="w-1/3 font-bold">Age over 18</div> <div className="w-2/3"> {results.credentials?.[0].claims?.['org.iso.18013.5.1'].age_over_18.value ? 'Yes' : 'No'} </div> </div> </div> ) } { results && 'error' in results && ( <div className="flex flex-row"> <div className="w-1/3 font-bold">{results.error.type}</div> <div className="w-2/3">{results.error.message}</div> </div> ) }
In this example when verification is successful (
"credentials" in results
) the app retrieves and displays the following properties of the first credential (credentials?.[0]
) of theresults
object:verificationResult.verified
: Indicates whether or not the mDoc was verified.validityInfo.validUntil
: Indicates the mDoc expiry date.issuerInfo.commonName
: Indicates the issuerβs name.claims?.['org.iso.18013.5.1'].given_name
: Indicates the holderβs given name.claims?.['org.iso.18013.5.1'].age_over_18.value
: Indicates whether or not the holder is over 18.
When verification fails (
"error" in results
) the app retrieves and displays error details (error.type
anderror.message
) from theresults
response.
Your web application will now continue the interaction based on the workflow type:
- For cross-device workflows, the web application will display the verification results in the desktop browser where the interaction started. The user can then continue the interaction on this desktop browser.
- For same-device workflows, the MATTR VII verifier tenant will redirect the user back to the web application in their mobile browser, where verification will be displayed. The user can then continue the interaction on this mobile browser.
Congratulations, your web application is now ready to verify mDocs.
Letβs test what weβve built!
Test the front channel interaction
- Open your ngrok forwarding URL in a browser on a mobile device where the MATTR GO Hold example app is installed.
- Select the Request Credential button.
- Select Allow to open the GO Hold app.
- The app should launch and display what information is requested for verification.
- Select Start.
- Select Confirm to share a credential which includes the requested information (claim one here if you havenβt done so already).
- The web application should display the verification results as a string.
The result should look something like this:
If your goal was just to see mDocs online verification in action, thereβs no need to proceed to the next step of the tutorial. However, production solutions often integrate a backend, and if youβre curious about how that works, feel free to continue to the next stepβitβs optional and wonβt change the overall workflow, just whatβs happening behind the scenes.
Part 3: Integrate a backend into your online verification workflow
Our online verification workflow is now working well. However, to make the solution more secure it is recommend to integrate your web application backend into the workflow. In this adjusted workflow, verification results are only shared with the backend and only surfaced to the front channel after validation. Refer to the detailed workflow page for more information.
In this part of the tutorial we will configure a simple backend that would retrieve the verification results from the MATTR VII tenant, validate and pass them to the front channel web application.
Update verifier application configuration
The first thing we need to do is to make sure verification results are not returned directly to the web applicationβs front channel. This setting is part of your MATTR VII verifier application configuration.
Make the following request to your MATTR VII tenant to update your verifier application
configuration (donβt change any of the values other than setting resultAvailableInFrontChannel
to
false
):
PUT /v2/presentations/applications/{id}
id
: Replace with theid
from the response returned when you created the verifier application configuration.
Request body
{
"name": "My Verifier Web Application",
"type": "web",
"domain": "b0de-3-24-117-154.ngrok-free.app",
"openid4vpConfiguration": {
"supportedMode": "all",
"redirectUris": ["https://b0de-3-24-117-154.ngrok-free.app"],
"display": {
"logoImage": {
"url": "https://static.mattr.global/logos/mattr/s/gFC.svg",
"altText": "MATTR Logo image"
},
"headerText": "Share your information.",
"bodyText": "Please scan the QR code to provide information required for completing this interaction."
}
},
"resultAvailableInFrontChannel": false
}
resultAvailableInFrontChannel
: When set to thefalse
, the MATTR VII verifier tenant does not return the verification results directly to the front channel. Instead, it notifies the front channel that verification results for a specific presentation session are available for retrieval. In the next step we will configure your web application backend to retrieve these results based on the returned sessionID
.
Add your MATTR VII login details
Your backend will need to make a request to a protected MATTR VII endpoint to retrieve the verification results. To enable this, we must provide it with your MATTR VII tenant login details.
-
Add the following code to your
.env.local
file to add your MATTR VII tenant login details:MATTR_CLIENT_ID=ZAGvD******************************** MATTR_CLIENT_SECRET=uc8NM****************************************
MATTR_CLIENT_ID
: Replace with yourclient_id
value.MATTR_CLIENT_SECRET
: Replace with yourclient_secret
value.
Create a session and associate a challenge
The web application backend needs to generate a unique challenge in the context of an existing user session. In a typical application, the user would likely already interact with the application inside an active session, and often that session is tracked using a cookie. In this tutorial we are mimicking this by setting a random value as the user session ID.
We are also creating a random challenge
and storing it inside a cookie to ensure that the
presentation results we are retrieving belong to the user in the current user session.
- Create a new file at
src/app/api/request/route.ts
with the following content:
import { cookies } from 'next/headers'
export async function POST(_request: Request) {
const sessionId = crypto.randomUUID()
const challenge = crypto.randomUUID()
const cookieStore = await cookies()
cookieStore.set('session_id', sessionId, { httpOnly: true, secure: true, sameSite: true })
cookieStore.set('challenge', challenge, { httpOnly: true, secure: true, sameSite: true })
return Response.json({ challenge }, { status: 201 })
}
While itβs not strictly necessary to keep the challenge
secret, you might want to only use a
single encrypted session cookie to not leak any information about your session management.
If you use a database to store the sessions state, you could use that database to store the
challenge
against the user session.
Retrieve verification results
Next we need to create an API route that will make a request to the MATTR VII verifier tenant and retrieve the results of a specific presentation session based on its ID.
-
Create a new file at
src/app/api/results/[id]/route.ts
with the following content:tsimport { cookies } from 'next/headers' async function getToken() { const response = await fetch('https://auth.au01.mattr.global/oauth/token', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ client_id: process.env.MATTR_CLIENT_ID, client_secret: process.env.MATTR_CLIENT_SECRET, audience: process.env.NEXT_PUBLIC_TENANT_URL, grant_type: 'client_credentials' }) }) const { access_token } = await response.json() return access_token } export async function GET(_: Request, { params }: { params: Promise<{ id: string }> }) { const id = (await params).id const cookiesStore = await cookies() if (!cookiesStore.get('session_id')) { return Response.json({ error: 'No valid session for user' }, { status: 401 }) } const response = await fetch( `${process.env.NEXT_PUBLIC_TENANT_URL}/v2/presentations/sessions/${id}/result`, { headers: { authorization: `Bearer ${await getToken()}` } } ) const results = await response.json() if (results.challenge !== cookiesStore.get('challenge')?.value) { return Response.json({ error: 'Challenge does not match' }) } return Response.json({ results }) }
The
getToken
function first uses project variables to retrieve a MATTR VII access token, as the results are retrieved from a protected endpoint.The route must be called with a dynamic
id
route parameter. This is the unique identifier of the presentation session. It is used in the authenticated request to the/v2/presentations/sessions/${id}/result
endpoint to retrieve the corresponding verification results.Your backend must then validate the returned results. The exact implementation would depend on your user session management, but you should always ensure that:
- The request to retrieve the results comes from an active user session (as per its
session_id
). - The
challenge
included in the response from the MATTR VII tenant matches thechallenge
you have stored for that active user session.
- The request to retrieve the results comes from an active user session (as per its
Adjust front channel to retrieve results via backend
Now that the backend is configured to retrieve the verification results, we need to adjust the front channel web application so that it can retrieve these results from the backend.
-
Copy and paste the following code under the
Step 6.1 - Create createRequest function
comment.tsxasync function createRequest() { const response = await fetch('/api/request', { method: 'POST' }) const { challenge } = await response.json() return challenge }
-
Replace the
challenge
assignment under theStep 6.2 - Use challenge from backend
comment with the following code to call thecreateRequest
function to generate a challenge:tsxchallenge: await createRequest(),
-
Add the following code under the
Step 6.3 - Create retrieveResults function
comment to create a new function that retrieves the verification results:tsxconst retrieveResults = useCallback(async (id: string) => { const response = await fetch(`api/results/${id}`) const { results } = await response.json() setResults(results as MATTRVerifierSDK.PresentationSessionResult) }, [])
This function uses the API route created above to retrieve the verification results and assign them to the
presentationResults
variable. -
Replace the
setResults
call under theStep 6.4 - Call retrieveResults function on cross-device callback
with the following code to call the newretrieveResults
function in the callback of a cross-device workflow:tsxretrieveResults(callbackResponse.result.sessionId)
-
Replace the
setResults
call under theStep 6.5 - Call retrieveResults function on same-device redirect
with the following code to call the newretrieveResults
function when the user is redirected to the app following a same-device workflow:tsxretrieveResults(presentationResult.sessionId)
Your web application now has an integrated backend that can validate verification results before passing them to the front channel, making the solution more secure.
Letβs test to ensure everything is working as expected.
Test the backend interaction
- Repeat the steps in the front channel testing step above.
- The interaction should look the same, but behind the scenes it will actually be handled by your backend.
Whatβs next?
- This project was run and tested locally using tunneling services. To build similar online
verification capabilities into a production web application, all you need to do is replace the
domain
andredirectUri
with the corresponding values of your production web application. - This project was created to showcase happy paths. It is recommended to review our comprehensive SDK reference documentation to learn more about available functions and classes.
- As always, feel free to reach out if you have any questions or encounter any issues we can help with.