From d8565cc3869dade4a0e214e67778d01879034d05 Mon Sep 17 00:00:00 2001 From: Avner Matan <137777701+avner-m@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:18:12 +1200 Subject: [PATCH 1/2] feat(claims-source-app): support claim set lookups and fix license lookup Look up records by recordId (mapped from the claim set identifier) for the new Claim sets tutorial, and by licenseNumber for the Claims source tutorial. The lookup previously read an email query parameter that no record in database.json has, so every request returned 404. --- README.md | 2 +- claims-source-app/README.md | 12 +++++++++--- claims-source-app/database.json | 27 +++++++++++++++++++++++++++ claims-source-app/src/index.js | 31 ++++++++++++++++++++++++------- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3e71438..7c45fcd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of developer apps and API tooling that you can use to interface with the MATTR Platform. ## Projects -* [claims-source-app](/claims-source-app/README.md) – A mock Claims Source that you can use for our [OID4VCI Claims Source tutorial](). +* [claims-source-app](/claims-source-app/README.md) – A mock Claims Source that you can use for our [Claims source tutorial](https://learn.mattr.global/docs/issuance/claims-source/tutorial) and [Claim sets tutorial](https://learn.mattr.global/docs/issuance/claim-sets/tutorial). * [verifier-web-tutorial](/verifier-web-tutorial/README.md) – A NextJS app that uses MATTRs Verifier Web SDK to verify mDocs presented via an online presentation workflow as per ISO/IEC 18013-7:2024 and OID4VP. * [webhook-app](/webhook-app/README.md) – A webhook receiver for MATTR VII webhooks with HTTP signature validation. diff --git a/claims-source-app/README.md b/claims-source-app/README.md index bcded4b..b3b160f 100644 --- a/claims-source-app/README.md +++ b/claims-source-app/README.md @@ -2,17 +2,23 @@ ## Description -This project provides the minimal implementation of a claims source to be integrated into a OpenID4VCI flow and can be used for our [Claims Source Tutorial](https://learn.mattr.global/guides/oid4vci/claim-source-tutorial). +This project provides the minimal implementation of a claims source to be integrated into a OpenID4VCI flow. It is used by two MATTR Learn tutorials: + +* The [Claims source tutorial](https://learn.mattr.global/docs/issuance/claims-source/tutorial), which looks up a driver license by `licenseNumber`. +* The [Claim sets tutorial](https://learn.mattr.global/docs/issuance/claim-sets/tutorial), which looks up individual birth certificates by `recordId`, so that one holder can receive several credentials of the same type. ## Database -The `database.json` file acts as our database and holds an array of user objects. Add your own claims to that array and ensure that every object has the `licenseNumber` claim used to query the array, plus any claims that your credential configuration requires. +The `database.json` file acts as our database and holds an array of records. Add your own records to that array, plus any claims that your credential configuration requires. Every record needs one of these keys, which is used to query the array: + +* `licenseNumber`: used by the Claims source tutorial, for example `GET /claims?licenseNumber=DL-123456`. Returns `404` when no record matches. +* `recordId`: used by the Claim sets tutorial, where MATTR VII maps it from the claim set identifier, for example `GET /claims?recordId=BC-1002`. Returns an empty object when no record matches, so MATTR VII can issue the credential using claims supplied on the credential offer instead. ## Configuration The claims source must be publicly accessible. This project uses ngrok and starts a tunnel for you. You can get a free account at [ngrok.com](https://ngrok.com/), and need to configure your `NGROK_AUTHTOKEN` in the `.env` file. -You also need to add the claims source API secret to the `.env` file. If you are following the [Claims Source Tutorial](https://learn.mattr.global/guides/oid4vci/claim-source-tutorial), the value being used there is `supersecretapikey`. +You also need to add the claims source API secret to the `.env` file. Both tutorials use the value `supersecretapikey`. The project contains an `env-template` file that you can copy to `.env`. In the end, the `.env` file should look like this: diff --git a/claims-source-app/database.json b/claims-source-app/database.json index 0ac733f..55ccfd0 100644 --- a/claims-source-app/database.json +++ b/claims-source-app/database.json @@ -19,5 +19,32 @@ "resident_city": "Austin", "resident_state": "TX", "resident_postal_code": "73301" + }, + { + "recordId": "BC-1001", + "given_name": "Alex", + "family_name": "Rivera", + "birth_date": "1988-04-12", + "birth_place": "Springfield", + "parent_1_name": "Morgan Rivera", + "parent_2_name": "Casey Rivera" + }, + { + "recordId": "BC-1002", + "given_name": "Sam", + "family_name": "Rivera", + "birth_date": "2016-09-03", + "birth_place": "Springfield", + "parent_1_name": "Alex Rivera", + "parent_2_name": "Jordan Rivera" + }, + { + "recordId": "BC-1003", + "given_name": "Riley", + "family_name": "Rivera", + "birth_date": "2019-02-21", + "birth_place": "Portland", + "parent_1_name": "Alex Rivera", + "parent_2_name": "Jordan Rivera" } ] diff --git a/claims-source-app/src/index.js b/claims-source-app/src/index.js index d4f41a5..59c6b8f 100644 --- a/claims-source-app/src/index.js +++ b/claims-source-app/src/index.js @@ -14,22 +14,39 @@ app.get('/claims', (req, res) => { return res.status(401).json({ error: "Unauthorized" }); } - // Get the email form the request query. - const { email } = req.query; + // The Claims source tutorial queries by `licenseNumber`. The Claim sets + // tutorial queries by `recordId`, which MATTR VII maps from the claim set + // identifier on the credential offer. + const { licenseNumber, recordId } = req.query; // Query your database. In a production use case, you would connect // to your user database, we are simply searching in a local JSON file. - const user = database.find((user) => user.email === email) + if (recordId) { + const record = database.find((record) => record.recordId === recordId) - // Return 404 Not Found when there is no user with the provided email - // address. + // Return an empty object when the record is not in the database yet, so + // that MATTR VII issues the credential using claims supplied on the offer. + if (!record) { + console.log(`No record found with recordId "${recordId}", returning no claims`) + return res.json({}) + } + + console.log(`Returning record data for "${recordId}"`); + console.log(record) + return res.json(record) + } + + const user = database.find((user) => user.licenseNumber === licenseNumber) + + // Return 404 Not Found when there is no user with the provided license + // number. if (!user) { - console.error(`User not found with email "${email}"`) + console.error(`User not found with licenseNumber "${licenseNumber}"`) return res.status(404).json({ error: "User not found" }); } // Debug logs for the user data that are return to be used as claims. - console.log(`Returning user data for "${email}"`); + console.log(`Returning user data for "${licenseNumber}"`); console.log(user) res.json(user) From 5ea2237b8603de7cbab811ea333715448527a302 Mon Sep 17 00:00:00 2001 From: Avner Matan <137777701+avner-m@users.noreply.github.com> Date: Thu, 24 Sep 2026 16:18:12 +1200 Subject: [PATCH 2/2] chore(android-holder-tutorial-sample-app): bump holder SDK to 7.2.0 --- android-holder-tutorial-sample-app/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android-holder-tutorial-sample-app/app/build.gradle.kts b/android-holder-tutorial-sample-app/app/build.gradle.kts index b86e408..e0802be 100644 --- a/android-holder-tutorial-sample-app/app/build.gradle.kts +++ b/android-holder-tutorial-sample-app/app/build.gradle.kts @@ -56,7 +56,7 @@ dependencies { androidTestImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.test.manifest) - implementation("global.mattr.mobilecredential:holder:7.0.1") + implementation("global.mattr.mobilecredential:holder:7.2.0") implementation("androidx.navigation:navigation-compose:2.9.0") implementation("com.google.accompanist:accompanist-permissions:0.36.0") implementation("com.journeyapps:zxing-android-embedded:4.3.0")