Skip to content

Commit 3fe0296

Browse files
authored
Update to latest DC API call structure (#7)
This PR accommodates changes made to DC API `.get()` options, including an updated protocol identifier, and drops older call signatures previously required when testing with Chrome.
1 parent 48b0984 commit 3fe0296

File tree

10 files changed

+172
-126
lines changed

10 files changed

+172
-126
lines changed

example/main.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { Hono } from "hono";
22
import { serveStatic } from "hono/deno";
33

4-
import { generateRequestOptions } from "../packages/server/src/index.ts";
4+
import {
5+
CredentialRequestOptions,
6+
generateRequestOptions,
7+
verifyResponse,
8+
} from "../packages/server/src/index.ts";
9+
10+
let currentOptions: CredentialRequestOptions;
511

612
const app = new Hono();
713

@@ -16,31 +22,24 @@ app.get("/options", async (ctx) => {
1622

1723
console.log(JSON.stringify(options, null, 2));
1824

19-
/**
20-
* This is an older format of the DC API call. We have to call it this way for now till a
21-
* future Chrome update (currently on Chrome Canary 134) adds support for the newer DC API
22-
* call structure
23-
*/
24-
const _options = {
25-
digital: {
26-
providers: [{
27-
protocol: "openid4vp",
28-
request: options.digital.requests[0],
29-
}],
30-
},
31-
};
32-
33-
return ctx.json(_options);
25+
currentOptions = options;
26+
27+
return ctx.json(options);
3428
});
3529

3630
app.post("/verify", async (ctx) => {
3731
const body = await ctx.req.json();
3832

3933
console.log("verifying presentation", body);
4034

41-
return ctx.json({
42-
verified: "TODO",
35+
const verified = await verifyResponse({
36+
response: body,
37+
options: currentOptions,
4338
});
39+
40+
console.log("verified claims:\n", JSON.stringify(verified, null, 2));
41+
42+
return ctx.json({ verified });
4443
});
4544

4645
Deno.serve({ hostname: "localhost" }, app.fetch);

example/static/index.html

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ <h1>SimpleDigiCreds Example Site</h1>
7070
let response;
7171
try {
7272
response = await navigator.credentials.get(optionsJSON);
73-
console.log("Raw response:", response);
74-
printDebug("Response", response.data);
73+
console.log("Raw presentation:", response);
74+
printDebug(
75+
"Raw Presentation",
76+
JSON.stringify(response.data, null, 2)
77+
);
7578
} catch (err) {
7679
elemError.innerText = `${err}`;
7780
console.error(err);
@@ -80,7 +83,10 @@ <h1>SimpleDigiCreds Example Site</h1>
8083

8184
const verification = await fetch("/verify", {
8285
method: "post",
83-
body: response.data,
86+
body: JSON.stringify(response.data),
87+
headers: {
88+
"Content-Type": "application/json",
89+
},
8490
});
8591
const verificationJSON = await verification.json();
8692

@@ -89,7 +95,7 @@ <h1>SimpleDigiCreds Example Site</h1>
8995
JSON.stringify(verificationJSON, null, 2)
9096
);
9197

92-
if (verificationJSON && verificationJSON.verified === true) {
98+
if (verificationJSON && verificationJSON.verified) {
9399
elemSuccess.innerHTML = "Successfully requested a credential!";
94100
} else {
95101
elemError.innerHTML = `Oh no, something went wrong! Response: <pre>${JSON.stringify(

packages/server/src/dcapi.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
import type { OID4VPCredentialQuery, OID4VPCredentialQueryMdoc } from './protocols/oid4vp.ts';
22

3+
/**
4+
* Options suitable for passing into `navigator.credentials.get({ digital: { ... } })` in the
5+
* browser to request the presentation of a verifiable credential via the Digital Credentials API
6+
*/
7+
export type CredentialRequestOptions = {
8+
digital: DigitalCredentialRequestOptions;
9+
};
10+
11+
export type DigitalCredentialRequestOptions = {
12+
requests: DigitalCredentialRequest[];
13+
};
14+
15+
export type DigitalCredentialRequest = {
16+
protocol: string;
17+
data: DCAPIRequestOID4VP;
18+
};
319
/**
420
* OID4VP-specific request parameters
521
*
@@ -20,16 +36,6 @@ export type DCAPIRequestOID4VP = {
2036
};
2137
};
2238

23-
/**
24-
* Options suitable for passing into `navigator.credentials.get({ digital: { ... } })` in the
25-
* browser to request the presentation of a verifiable credential via the Digital Credentials API
26-
*/
27-
export type DCAPIRequestOptions = {
28-
digital: {
29-
requests: DCAPIRequestOID4VP[];
30-
};
31-
};
32-
3339
/**
3440
* The shape of the value returned from a call to `navigator.credentials.get({ digital: { ... } })`
3541
*/

packages/server/src/formats/mdoc/generateSessionTranscript.test.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,40 @@
11
import { assertEquals } from '@std/assert';
22

3-
import type { DCAPIRequestOptions } from '../../dcapi.ts';
3+
import type { CredentialRequestOptions } from '../../dcapi.ts';
44
import { generateSessionTranscript } from './generateSessionTranscript.ts';
55
import { base64url } from '../../helpers/index.ts';
66

77
Deno.test('should generate OID4VP-specific session transcript', async () => {
8-
const options: DCAPIRequestOptions = {
8+
const options: CredentialRequestOptions = {
99
digital: {
1010
requests: [
1111
{
12-
response_type: 'vp_token',
13-
response_mode: 'dc_api',
14-
client_id: 'web-origin:http://localhost:8000',
15-
nonce: 'Y_Sl5cgcgTiw7XnikC24SCDvPEFb81gcOp3lrsdSwZ8',
16-
dcql_query: {
17-
credentials: [
18-
{
19-
id: 'cred1',
20-
format: 'mso_mdoc',
21-
meta: { doctype_value: 'org.iso.18013.5.1.mDL' },
22-
claims: [
23-
{ path: ['org.iso.18013.5.1', 'family_name'] },
24-
{ path: ['org.iso.18013.5.1', 'given_name'] },
25-
],
26-
},
27-
],
12+
protocol: 'openid4vp',
13+
data: {
14+
response_type: 'vp_token',
15+
response_mode: 'dc_api',
16+
client_id: 'web-origin:http://localhost:8000',
17+
nonce: 'Y_Sl5cgcgTiw7XnikC24SCDvPEFb81gcOp3lrsdSwZ8',
18+
dcql_query: {
19+
credentials: [
20+
{
21+
id: 'cred1',
22+
format: 'mso_mdoc',
23+
meta: { doctype_value: 'org.iso.18013.5.1.mDL' },
24+
claims: [
25+
{ path: ['org.iso.18013.5.1', 'family_name'] },
26+
{ path: ['org.iso.18013.5.1', 'given_name'] },
27+
],
28+
},
29+
],
30+
},
2831
},
2932
},
3033
],
3134
},
3235
};
3336

34-
const sessionTranscript = await generateSessionTranscript(options.digital.requests[0]);
37+
const sessionTranscript = await generateSessionTranscript(options.digital.requests[0].data);
3538

3639
assertEquals(
3740
sessionTranscript,

packages/server/src/formats/mdoc/verifyMdocPresentation.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { DecodedCredentialResponse } from './types.ts';
55
import { verifyIssuerSigned } from './verifyIssuerSigned.ts';
66
import { verifyDeviceSigned } from './verifyDeviceSigned.ts';
77
import { type VerifiedNamespace, verifyNameSpaces } from './verifyNameSpaces.ts';
8+
import { convertX509BufferToPEM } from '../../helpers/x509/index.ts';
89

910
/**
1011
* Verify an mdoc presentation as returned through the DC API
@@ -42,13 +43,20 @@ export async function verifyMdocPresentation(
4243
// TODO: In case it's a bad idea to flatten claims like we're doing above
4344
// verifiedValues[id] = verifiedItems;
4445

46+
/**
47+
* TODO: It's probably not important to return this, and instead verify the certificate chain
48+
* as part of verification using trust anchors specified when calling `verifyResponse()`.
49+
* If the chain can't be verified then reject the presentation.
50+
*/
51+
const x5cPEM = issuerX5C.map(convertX509BufferToPEM);
52+
4553
return {
4654
verifiedClaims,
47-
issuerX5C,
55+
issuerX5C: x5cPEM,
4856
};
4957
}
5058

5159
type VerifiedMdocPresentation = {
5260
verifiedClaims: VerifiedNamespace;
53-
issuerX5C: Uint8Array[];
61+
issuerX5C: string[];
5462
};

packages/server/src/generateRequestOptions.test.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,28 @@ Deno.test('Should generate options', () => {
2222
digital: {
2323
requests: [
2424
{
25-
response_type: 'vp_token',
26-
response_mode: 'dc_api',
27-
client_id: 'web-origin:https://digital-credentials.dev',
28-
nonce: '9kMlSgHQW8oBv_AdkSaZKM0ajrEUatzg2f24vV6AgnI',
29-
dcql_query: {
30-
credentials: [
31-
{
32-
id: 'cred1',
33-
format: 'mso_mdoc',
34-
meta: {
35-
doctype_value: 'org.iso.18013.5.1.mDL',
25+
protocol: 'openid4vp',
26+
data: {
27+
response_type: 'vp_token',
28+
response_mode: 'dc_api',
29+
client_id: 'web-origin:https://digital-credentials.dev',
30+
nonce: '9kMlSgHQW8oBv_AdkSaZKM0ajrEUatzg2f24vV6AgnI',
31+
dcql_query: {
32+
credentials: [
33+
{
34+
id: 'cred1',
35+
format: 'mso_mdoc',
36+
meta: {
37+
doctype_value: 'org.iso.18013.5.1.mDL',
38+
},
39+
claims: [
40+
{ path: ['org.iso.18013.5.1', 'family_name'] },
41+
{ path: ['org.iso.18013.5.1', 'given_name'] },
42+
{ path: ['org.iso.18013.5.1', 'age_over_21'] },
43+
],
3644
},
37-
claims: [
38-
{ path: ['org.iso.18013.5.1', 'family_name'] },
39-
{ path: ['org.iso.18013.5.1', 'given_name'] },
40-
{ path: ['org.iso.18013.5.1', 'age_over_21'] },
41-
],
42-
},
43-
],
45+
],
46+
},
4447
},
4548
},
4649
],

packages/server/src/generateRequestOptions.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type {
33
OID4VPCredentialQueryMdoc,
44
OID4VPSupportedMdocClaimName,
55
} from './protocols/oid4vp.ts';
6-
import type { DCAPIRequestOptions } from './dcapi.ts';
6+
import type { CredentialRequestOptions } from './dcapi.ts';
77

88
/**
99
* Generate credential presentation request options suitable for passing into
@@ -20,7 +20,7 @@ export function generateRequestOptions(
2020
desiredClaims: OID4VPSupportedMdocClaimName[];
2121
requestOrigin: string;
2222
},
23-
): DCAPIRequestOptions {
23+
): CredentialRequestOptions {
2424
const mdocCredentialRequest: OID4VPCredentialQueryMdoc = {
2525
id: 'cred1',
2626
format: 'mso_mdoc',
@@ -36,15 +36,19 @@ export function generateRequestOptions(
3636
digital: {
3737
requests: [
3838
{
39-
response_type: 'vp_token',
40-
response_mode: 'dc_api',
41-
client_id: `web-origin:${requestOrigin}`,
42-
nonce: generateNonce(),
43-
// https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#dcql_query
44-
dcql_query: {
45-
credentials: [
46-
mdocCredentialRequest,
47-
],
39+
// https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#name-protocol
40+
protocol: 'openid4vp',
41+
data: {
42+
response_type: 'vp_token',
43+
response_mode: 'dc_api',
44+
client_id: `web-origin:${requestOrigin}`,
45+
nonce: generateNonce(),
46+
// https://openid.net/specs/openid-4-verifiable-presentations-1_0-24.html#dcql_query
47+
dcql_query: {
48+
credentials: [
49+
mdocCredentialRequest,
50+
],
51+
},
4852
},
4953
},
5054
],

packages/server/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
export { generateRequestOptions } from './generateRequestOptions.ts';
2+
export { type VerifiedResponse, verifyResponse } from './verifyResponse.ts';
23

3-
export type { DCAPIRequestOID4VP, DCAPIRequestOptions } from './dcapi.ts';
4+
export type {
5+
CredentialRequestOptions,
6+
DCAPIRequestOID4VP,
7+
DigitalCredentialRequest,
8+
DigitalCredentialRequestOptions,
9+
} from './dcapi.ts';
410

511
export type {
612
OID4VPClaimQuery,

0 commit comments

Comments
 (0)