@gramota/oid4vp 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @gramota/oid4vp
2
2
 
3
- > OpenID for Verifiable Presentations transport — authorization request/response wire format for EUDIW.
3
+ > OpenID for Verifiable Presentations Final 1.0 — authorization request/response wire format, RFC 9101 JAR signing, X.509 verifier-identity certs. Accepts both DCQL and DIF Presentation Exchange response shapes.
4
4
 
5
5
  Part of [Gramota](https://github.com/gramota-org/gramota) — the TypeScript
6
6
  SDK for the EU Digital Identity Wallet (EUDIW).
@@ -13,17 +13,57 @@ pnpm add @gramota/oid4vp
13
13
  # or: yarn add @gramota/oid4vp
14
14
  ```
15
15
 
16
- ## Usage
16
+ ## Quick example — verifier emits a signed JAR
17
17
 
18
18
  ```ts
19
- import { buildAuthorizationRequest, parseAuthorizationResponse } from "@gramota/oid4vp";
19
+ import {
20
+ generateSigningCert,
21
+ signAuthorizationRequest,
22
+ buildAuthorizationRequestUrl,
23
+ } from "@gramota/oid4vp";
20
24
 
21
- const req = buildAuthorizationRequest({ client_id, nonce, ... });
22
- const res = parseAuthorizationResponse(formData);
25
+ // Self-signed cert with SAN-DNS = the verifier's hostname
26
+ const cert = await generateSigningCert({ sanDns: "verifier.example" });
27
+
28
+ // Build the OID4VP request, sign as a JAR (RFC 9101) embedded in the URL
29
+ const request = {
30
+ response_type: "vp_token" as const,
31
+ client_id: `x509_san_dns:${cert.sanDns}`,
32
+ response_mode: "direct_post" as const,
33
+ response_uri: "https://verifier.example/oid4vp/response",
34
+ nonce: "n-12345",
35
+ state: "s-67890",
36
+ dcql_query: {
37
+ credentials: [
38
+ { id: "pid", format: "dc+sd-jwt", meta: { vct_values: ["urn:eudi:pid:1"] } },
39
+ ],
40
+ },
41
+ };
42
+ const jar = await signAuthorizationRequest({ request, cert });
43
+ const deepLink = `openid4vp://?client_id=${encodeURIComponent(request.client_id)}&request=${encodeURIComponent(jar)}`;
44
+ ```
45
+
46
+ ## Quick example — verifier parses the wallet's response
47
+
48
+ ```ts
49
+ import { parseAuthorizationResponseFromParams } from "@gramota/oid4vp";
50
+
51
+ // req.body is { vp_token, presentation_submission?, state }
52
+ const response = parseAuthorizationResponseFromParams(req.body);
53
+ // vp_token can be a string (PEX, single), string[] (PEX, multi),
54
+ // or { [credentialId]: string } (DCQL, OID4VP Final 1.0)
23
55
  ```
24
56
 
25
- For full docs, examples, and the high-level Verifier/Issuer/Holder API,
26
- see the [main repo](https://github.com/gramota-org/gramota).
57
+ ## What's inside
58
+
59
+ - `buildAuthorizationRequestUrl` / `parseAuthorizationRequestUrl` — wire format (§5)
60
+ - `parseAuthorizationResponseFromParams` / `buildAuthorizationResponseBody` — wire format (§6); accepts both PEX and DCQL response shapes
61
+ - `generateSigningCert` — self-signed X.509 with SAN-DNS for `x509_san_dns` client_id_prefix
62
+ - `signAuthorizationRequest` — RFC 9101 JAR signing with cert in the JWS `x5c` header
63
+ - `signingCertToJwks` — convert the cert+key bundle into a JWK pair for `@gramota/issuer`
64
+
65
+ For the high-level Verifier API, see the
66
+ [main repo](https://github.com/gramota-org/gramota).
27
67
 
28
68
  ## License
29
69
 
package/dist/cert.d.ts ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * X.509 self-signed cert generation for OID4VP verifiers.
3
+ *
4
+ * The EU reference wallet's `X509SanDns` client_id_prefix accepts
5
+ * Authorization Requests signed by a cert whose Subject Alternative Name
6
+ * matches the verifier's hostname. For local dev, mock environments, and
7
+ * pinned-trust deployments we generate a fresh ES256 keypair + a leaf
8
+ * cert with the right SAN. Production deployments swap this for an
9
+ * externally issued cert (ACME / corporate CA) — the {@link SigningCert}
10
+ * shape is the same, only the origin differs.
11
+ *
12
+ * The cert+key pair is consumed by:
13
+ * 1. `signAuthorizationRequest` — signs the JAR JWS, embedding the
14
+ * cert in the `x5c` header.
15
+ * 2. The wallet's TLS / trust-anchor store — it must trust this cert
16
+ * out-of-band (in production: via a CA the wallet already trusts;
17
+ * for emulators: via patching the wallet's trust list).
18
+ */
19
+ import "reflect-metadata";
20
+ import type { JsonWebKey } from "@gramota/jose";
21
+ import { type SigningCert } from "./types.js";
22
+ export interface GenerateSigningCertOptions {
23
+ /** Primary DNS name for the cert's Subject Alternative Name. Must match
24
+ * the verifier's hostname (the part the wallet sees in the OID4VP
25
+ * `client_id` after the `x509_san_dns:` prefix). */
26
+ readonly sanDns: string;
27
+ /** Additional SAN-DNS entries — used for wildcards (e.g. `*.localtest.me`)
28
+ * so a single cert covers per-tenant subdomains, or for serving multiple
29
+ * verifier hostnames behind one identity. */
30
+ readonly extraSanDns?: readonly string[];
31
+ /** Cert subject CN — purely cosmetic, shown in cert viewers. Defaults
32
+ * to `sanDns`. */
33
+ readonly commonName?: string;
34
+ /** Org name for the cert subject. Defaults to `"Gramota Verifier"`. */
35
+ readonly organizationName?: string;
36
+ /** Validity in days. Default 365. */
37
+ readonly validDays?: number;
38
+ }
39
+ /**
40
+ * Generate an ES256 keypair + self-signed X.509 cert with the given
41
+ * SAN-DNS entries.
42
+ *
43
+ * The cert carries the standard verifier-identity extensions: serverAuth
44
+ * + clientAuth EKUs (so wallets that look for a TLS-style cert accept
45
+ * it), digitalSignature + keyEncipherment KU flags, and CA:false.
46
+ *
47
+ * Returns a {@link SigningCert} ready to feed into
48
+ * {@link signAuthorizationRequest}.
49
+ *
50
+ * @throws {@link Oid4vpError} with `oid4vp.cert_generation_failed` if
51
+ * the underlying crypto / X.509 generation fails.
52
+ */
53
+ export declare function generateSigningCert(input: GenerateSigningCertOptions): Promise<SigningCert>;
54
+ /**
55
+ * Convert a {@link SigningCert} (PEM bundle) into the JWK pair that
56
+ * `@gramota/issuer` and other JOSE consumers expect.
57
+ *
58
+ * Unlike `generateSigningCert` this is a pure conversion — it doesn't
59
+ * touch crypto state or generate anything new. It just bridges the two
60
+ * representations of the same key material:
61
+ *
62
+ * - Input shape: PKCS#8 PEM (private) + X.509 PEM (public-via-cert)
63
+ * - Output shape: two RFC 7517 JWKs with `alg: "ES256"` set
64
+ *
65
+ * Throws {@link Oid4vpError} with `oid4vp.cert_generation_failed` on a
66
+ * malformed PEM or an unrecognised key type.
67
+ */
68
+ export declare function signingCertToJwks(cert: SigningCert): Promise<{
69
+ publicJwk: JsonWebKey;
70
+ privateJwk: JsonWebKey;
71
+ }>;
72
+ //# sourceMappingURL=cert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert.d.ts","sourceRoot":"","sources":["../src/cert.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAc3D,MAAM,WAAW,0BAA0B;IACzC;;wDAEoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;iDAE6C;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC;sBACkB;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,WAAW,CAAC,CAyFtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAmC5D"}
package/dist/cert.js ADDED
@@ -0,0 +1,191 @@
1
+ /**
2
+ * X.509 self-signed cert generation for OID4VP verifiers.
3
+ *
4
+ * The EU reference wallet's `X509SanDns` client_id_prefix accepts
5
+ * Authorization Requests signed by a cert whose Subject Alternative Name
6
+ * matches the verifier's hostname. For local dev, mock environments, and
7
+ * pinned-trust deployments we generate a fresh ES256 keypair + a leaf
8
+ * cert with the right SAN. Production deployments swap this for an
9
+ * externally issued cert (ACME / corporate CA) — the {@link SigningCert}
10
+ * shape is the same, only the origin differs.
11
+ *
12
+ * The cert+key pair is consumed by:
13
+ * 1. `signAuthorizationRequest` — signs the JAR JWS, embedding the
14
+ * cert in the `x5c` header.
15
+ * 2. The wallet's TLS / trust-anchor store — it must trust this cert
16
+ * out-of-band (in production: via a CA the wallet already trusts;
17
+ * for emulators: via patching the wallet's trust list).
18
+ */
19
+ // `@peculiar/x509@2.x` uses tsyringe for dependency injection, which in
20
+ // turn requires the Reflect.metadata polyfill at module load time. We
21
+ // import it here so consumers don't have to. The package.json marks
22
+ // `dist/cert.js` as a side-effecting module so bundlers don't drop it.
23
+ import "reflect-metadata";
24
+ import * as x509 from "@peculiar/x509";
25
+ import { webcrypto } from "node:crypto";
26
+ import { exportJWK, importPKCS8, importX509 } from "jose";
27
+ import { Oid4vpError } from "./types.js";
28
+ // @peculiar/x509 needs a Web Crypto provider — Node's webcrypto suffices.
29
+ // Safe to call multiple times; no-op after first.
30
+ let cryptoProviderSet = false;
31
+ function ensureCryptoProvider() {
32
+ if (cryptoProviderSet)
33
+ return;
34
+ // @peculiar/x509's CryptoProvider type wants a DOM `Crypto`. Node's
35
+ // webcrypto is API-compatible — the cast is just to satisfy TS.
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ x509.cryptoProvider.set(webcrypto);
38
+ cryptoProviderSet = true;
39
+ }
40
+ /**
41
+ * Generate an ES256 keypair + self-signed X.509 cert with the given
42
+ * SAN-DNS entries.
43
+ *
44
+ * The cert carries the standard verifier-identity extensions: serverAuth
45
+ * + clientAuth EKUs (so wallets that look for a TLS-style cert accept
46
+ * it), digitalSignature + keyEncipherment KU flags, and CA:false.
47
+ *
48
+ * Returns a {@link SigningCert} ready to feed into
49
+ * {@link signAuthorizationRequest}.
50
+ *
51
+ * @throws {@link Oid4vpError} with `oid4vp.cert_generation_failed` if
52
+ * the underlying crypto / X.509 generation fails.
53
+ */
54
+ export async function generateSigningCert(input) {
55
+ if (typeof input.sanDns !== "string" || input.sanDns.length === 0) {
56
+ throw new Oid4vpError("oid4vp.cert_generation_failed", "generateSigningCert: sanDns is required");
57
+ }
58
+ ensureCryptoProvider();
59
+ const validDays = input.validDays ?? 365;
60
+ const notBefore = new Date();
61
+ const notAfter = new Date(notBefore.getTime() + validDays * 24 * 60 * 60 * 1000);
62
+ // ES256 — the algorithm the EU wallet uses for KB-JWT and what most
63
+ // OID4VP implementations default to.
64
+ const algorithm = { name: "ECDSA", namedCurve: "P-256" };
65
+ const keys = await webcrypto.subtle.generateKey(algorithm, true, [
66
+ "sign",
67
+ "verify",
68
+ ]);
69
+ const subjectName = buildSubjectDn({
70
+ commonName: input.commonName ?? input.sanDns,
71
+ organizationName: input.organizationName ?? "Gramota Verifier",
72
+ });
73
+ let cert;
74
+ try {
75
+ cert = await x509.X509CertificateGenerator.createSelfSigned({
76
+ serialNumber: randomSerial(),
77
+ name: subjectName,
78
+ notBefore,
79
+ notAfter,
80
+ signingAlgorithm: { name: "ECDSA", hash: "SHA-256" },
81
+ keys,
82
+ extensions: [
83
+ // Subject Alternative Name — what the wallet validates against
84
+ // `client_id` (OID4VP `x509_san_dns:<host>`) and what TLS clients
85
+ // match the host header against.
86
+ new x509.SubjectAlternativeNameExtension([
87
+ { type: "dns", value: input.sanDns },
88
+ ...(input.extraSanDns ?? []).map((value) => ({
89
+ type: "dns",
90
+ value,
91
+ })),
92
+ ]),
93
+ // Standard verifier-identity usage flags.
94
+ new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment),
95
+ new x509.ExtendedKeyUsageExtension([
96
+ // serverAuth — wallets that expect a TLS-style cert accept this
97
+ "1.3.6.1.5.5.7.3.1",
98
+ // clientAuth — used by some wallets to bind the verifier identity
99
+ "1.3.6.1.5.5.7.3.2",
100
+ ]),
101
+ // Leaf cert, not a CA.
102
+ new x509.BasicConstraintsExtension(false),
103
+ ],
104
+ });
105
+ }
106
+ catch (err) {
107
+ throw new Oid4vpError("oid4vp.cert_generation_failed", `generateSigningCert: X.509 generation failed: ${err instanceof Error ? err.message : String(err)}`);
108
+ }
109
+ // Export private key as PKCS#8 PEM
110
+ const pkcs8 = await webcrypto.subtle.exportKey("pkcs8", keys.privateKey);
111
+ const privateKeyPem = derToPem(new Uint8Array(pkcs8), "PRIVATE KEY");
112
+ // Cert PEM
113
+ const certificatePem = cert.toString("pem");
114
+ // x5c header value: array of base64-encoded DER certs (no PEM wrappers).
115
+ const certDer = new Uint8Array(cert.rawData);
116
+ const x5c = [bytesToBase64(certDer)];
117
+ return {
118
+ privateKeyPem,
119
+ certificatePem,
120
+ x5c,
121
+ sanDns: input.sanDns,
122
+ };
123
+ }
124
+ /**
125
+ * Convert a {@link SigningCert} (PEM bundle) into the JWK pair that
126
+ * `@gramota/issuer` and other JOSE consumers expect.
127
+ *
128
+ * Unlike `generateSigningCert` this is a pure conversion — it doesn't
129
+ * touch crypto state or generate anything new. It just bridges the two
130
+ * representations of the same key material:
131
+ *
132
+ * - Input shape: PKCS#8 PEM (private) + X.509 PEM (public-via-cert)
133
+ * - Output shape: two RFC 7517 JWKs with `alg: "ES256"` set
134
+ *
135
+ * Throws {@link Oid4vpError} with `oid4vp.cert_generation_failed` on a
136
+ * malformed PEM or an unrecognised key type.
137
+ */
138
+ export async function signingCertToJwks(cert) {
139
+ if (cert === null ||
140
+ typeof cert !== "object" ||
141
+ typeof cert.privateKeyPem !== "string" ||
142
+ typeof cert.certificatePem !== "string") {
143
+ throw new Oid4vpError("oid4vp.cert_generation_failed", "signingCertToJwks: cert must include privateKeyPem and certificatePem");
144
+ }
145
+ let privateJwk;
146
+ let publicJwk;
147
+ try {
148
+ const priv = await importPKCS8(cert.privateKeyPem, "ES256");
149
+ const pub = await importX509(cert.certificatePem, "ES256");
150
+ privateJwk = (await exportJWK(priv));
151
+ publicJwk = (await exportJWK(pub));
152
+ }
153
+ catch (err) {
154
+ throw new Oid4vpError("oid4vp.cert_generation_failed", `signingCertToJwks: failed to import key material: ${err instanceof Error ? err.message : String(err)}`);
155
+ }
156
+ // Annotate with `alg` so downstream consumers (e.g. @gramota/issuer)
157
+ // can pick a JWS algorithm without re-deriving it.
158
+ privateJwk.alg = "ES256";
159
+ publicJwk.alg = "ES256";
160
+ return { publicJwk, privateJwk };
161
+ }
162
+ // ---------------------------------------------------------------------------
163
+ // helpers
164
+ // ---------------------------------------------------------------------------
165
+ function buildSubjectDn(parts) {
166
+ return `CN=${escapeDn(parts.commonName)}, O=${escapeDn(parts.organizationName)}`;
167
+ }
168
+ function escapeDn(value) {
169
+ // Minimal RFC 4514 escaping — commas, equals, plus.
170
+ return value.replace(/([,=+])/g, "\\$1");
171
+ }
172
+ function randomSerial() {
173
+ // 20-byte hex string — RFC 5280 §4.1.2.2 says serials must be positive
174
+ // integers up to 20 octets.
175
+ const bytes = new Uint8Array(20);
176
+ webcrypto.getRandomValues(bytes);
177
+ // Ensure leading byte isn't zero so it's a positive integer.
178
+ if (bytes[0] === 0)
179
+ bytes[0] = 1;
180
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
181
+ }
182
+ function derToPem(der, label) {
183
+ const b64 = bytesToBase64(der);
184
+ // 64-char wrap per RFC 7468.
185
+ const wrapped = b64.replace(/(.{64})/g, "$1\n");
186
+ return `-----BEGIN ${label}-----\n${wrapped}\n-----END ${label}-----\n`;
187
+ }
188
+ function bytesToBase64(bytes) {
189
+ return Buffer.from(bytes).toString("base64");
190
+ }
191
+ //# sourceMappingURL=cert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cert.js","sourceRoot":"","sources":["../src/cert.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { buildAuthorizationRequestUrl, parseAuthorizationRequestUrl, parseAuthorizationRequestSearchParams, } from "./request.js";
2
2
  export { buildAuthorizationResponseBody, parseAuthorizationResponseBody, parseAuthorizationResponseFromParams, } from "./response.js";
3
- export { Oid4vpError, type AuthorizationRequest, type AuthorizationResponse, type ClientIdScheme, type Oid4vpErrorCode, type ResponseMode, } from "./types.js";
3
+ export { generateSigningCert, signingCertToJwks, type GenerateSigningCertOptions, } from "./cert.js";
4
+ export { signAuthorizationRequest, type SignAuthorizationRequestOptions, } from "./jar.js";
5
+ export { Oid4vpError, type AuthorizationRequest, type AuthorizationResponse, type ClientIdScheme, type Oid4vpErrorCode, type ResponseMode, type SigningCert, } from "./types.js";
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,EAC5B,qCAAqC,GACtC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,EACX,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,EAC5B,qCAAqC,GACtC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,0BAA0B,GAChC,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,wBAAwB,EACxB,KAAK,+BAA+B,GACrC,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,WAAW,EACX,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export { buildAuthorizationRequestUrl, parseAuthorizationRequestUrl, parseAuthorizationRequestSearchParams, } from "./request.js";
2
2
  export { buildAuthorizationResponseBody, parseAuthorizationResponseBody, parseAuthorizationResponseFromParams, } from "./response.js";
3
+ export { generateSigningCert, signingCertToJwks, } from "./cert.js";
4
+ export { signAuthorizationRequest, } from "./jar.js";
3
5
  export { Oid4vpError, } from "./types.js";
4
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,EAC5B,qCAAqC,GACtC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,WAAW,GAMZ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,4BAA4B,EAC5B,qCAAqC,GACtC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,8BAA8B,EAC9B,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAElB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,wBAAwB,GAEzB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,WAAW,GAOZ,MAAM,YAAY,CAAC"}
package/dist/jar.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * RFC 9101 JAR (JWT-Secured Authorization Request) signing for OID4VP.
3
+ *
4
+ * verifier ─── builds AuthorizationRequest object ──►
5
+ * ─── wraps as compact-serialised JWS ──►
6
+ * ─── sets x5c header = [leaf cert DER] ──►
7
+ * wallet
8
+ *
9
+ * The wallet:
10
+ * 1. Fetches the JAR (inline `request=<jwt>` or via `request_uri=<url>`)
11
+ * 2. Sees `application/oauth-authz-req+jwt`, parses as a JWS
12
+ * 3. Reads the `x5c` header → leaf cert
13
+ * 4. Validates: cert chain trusted? SAN matches `client_id` value?
14
+ * 5. Verifies the JWS signature with the leaf cert's public key
15
+ * 6. Decodes the payload as the AuthorizationRequest, processes it
16
+ *
17
+ * For the EUDIW HAIP profile + the EU reference wallet's demo build,
18
+ * `x509_san_dns` (with this signed JAR shape) is the only `client_id_prefix`
19
+ * the wallet accepts. Production verifiers MUST sign their requests.
20
+ */
21
+ import { type AuthorizationRequest, type SigningCert } from "./types.js";
22
+ export interface SignAuthorizationRequestOptions {
23
+ /** The verifier's Authorization Request to sign. Must already include
24
+ * the verifier-side fields (`client_id`, `nonce`, `response_uri`, etc).
25
+ * The signer doesn't validate semantic content — only wire-encodes. */
26
+ readonly request: AuthorizationRequest;
27
+ /** The verifier's signing material — produced by `generateSigningCert`
28
+ * or supplied externally. */
29
+ readonly cert: SigningCert;
30
+ }
31
+ /**
32
+ * Sign an OID4VP Authorization Request as a compact-serialised JWS,
33
+ * formatted per RFC 9101 + OID4VP §5.10.
34
+ *
35
+ * The JWS header carries:
36
+ * - `alg: "ES256"` (the only algorithm the EU wallet currently accepts
37
+ * for x509_san_dns prefix; future iterations can lift this)
38
+ * - `typ: "oauth-authz-req+jwt"` per RFC 9101
39
+ * - `x5c: [<base64-DER leaf cert>]` so the wallet doesn't need a
40
+ * separate key-discovery step
41
+ *
42
+ * The JWS payload IS the request object — claims at the top level, not
43
+ * wrapped in some envelope (RFC 9101 §10.2). Caller serves the result
44
+ * with content type `application/oauth-authz-req+jwt`.
45
+ *
46
+ * @throws {@link Oid4vpError} with `oid4vp.jar_signing_failed` if the
47
+ * private key is malformed or signing fails.
48
+ */
49
+ export declare function signAuthorizationRequest(options: SignAuthorizationRequestOptions): Promise<string>;
50
+ //# sourceMappingURL=jar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jar.d.ts","sourceRoot":"","sources":["../src/jar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,+BAA+B;IAC9C;;2EAEuE;IACvE,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC;iCAC6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
package/dist/jar.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * RFC 9101 JAR (JWT-Secured Authorization Request) signing for OID4VP.
3
+ *
4
+ * verifier ─── builds AuthorizationRequest object ──►
5
+ * ─── wraps as compact-serialised JWS ──►
6
+ * ─── sets x5c header = [leaf cert DER] ──►
7
+ * wallet
8
+ *
9
+ * The wallet:
10
+ * 1. Fetches the JAR (inline `request=<jwt>` or via `request_uri=<url>`)
11
+ * 2. Sees `application/oauth-authz-req+jwt`, parses as a JWS
12
+ * 3. Reads the `x5c` header → leaf cert
13
+ * 4. Validates: cert chain trusted? SAN matches `client_id` value?
14
+ * 5. Verifies the JWS signature with the leaf cert's public key
15
+ * 6. Decodes the payload as the AuthorizationRequest, processes it
16
+ *
17
+ * For the EUDIW HAIP profile + the EU reference wallet's demo build,
18
+ * `x509_san_dns` (with this signed JAR shape) is the only `client_id_prefix`
19
+ * the wallet accepts. Production verifiers MUST sign their requests.
20
+ */
21
+ import { SignJWT, importPKCS8 } from "jose";
22
+ import { Oid4vpError, } from "./types.js";
23
+ /**
24
+ * Sign an OID4VP Authorization Request as a compact-serialised JWS,
25
+ * formatted per RFC 9101 + OID4VP §5.10.
26
+ *
27
+ * The JWS header carries:
28
+ * - `alg: "ES256"` (the only algorithm the EU wallet currently accepts
29
+ * for x509_san_dns prefix; future iterations can lift this)
30
+ * - `typ: "oauth-authz-req+jwt"` per RFC 9101
31
+ * - `x5c: [<base64-DER leaf cert>]` so the wallet doesn't need a
32
+ * separate key-discovery step
33
+ *
34
+ * The JWS payload IS the request object — claims at the top level, not
35
+ * wrapped in some envelope (RFC 9101 §10.2). Caller serves the result
36
+ * with content type `application/oauth-authz-req+jwt`.
37
+ *
38
+ * @throws {@link Oid4vpError} with `oid4vp.jar_signing_failed` if the
39
+ * private key is malformed or signing fails.
40
+ */
41
+ export async function signAuthorizationRequest(options) {
42
+ if (options.cert === null ||
43
+ typeof options.cert !== "object" ||
44
+ typeof options.cert.privateKeyPem !== "string") {
45
+ throw new Oid4vpError("oid4vp.jar_signing_failed", "signAuthorizationRequest: cert.privateKeyPem is required");
46
+ }
47
+ if (!Array.isArray(options.cert.x5c) || options.cert.x5c.length === 0) {
48
+ throw new Oid4vpError("oid4vp.jar_signing_failed", "signAuthorizationRequest: cert.x5c must be a non-empty array");
49
+ }
50
+ let privateKey;
51
+ try {
52
+ privateKey = await importPKCS8(options.cert.privateKeyPem, "ES256");
53
+ }
54
+ catch (err) {
55
+ throw new Oid4vpError("oid4vp.jar_signing_failed", `signAuthorizationRequest: failed to import private key: ${err instanceof Error ? err.message : String(err)}`);
56
+ }
57
+ try {
58
+ // Spread into a fresh mutable object — `jose.SignJWT` wants
59
+ // `JWTPayload` (mutable, index-signature) while `AuthorizationRequest`
60
+ // is strict-readonly. The spread copies own enumerable props and
61
+ // produces a value of structurally-compatible type without an
62
+ // `as unknown as` double-cast.
63
+ const payload = { ...options.request };
64
+ const jwt = await new SignJWT(payload)
65
+ .setProtectedHeader({
66
+ alg: "ES256",
67
+ typ: "oauth-authz-req+jwt",
68
+ x5c: [...options.cert.x5c],
69
+ })
70
+ .sign(privateKey);
71
+ return jwt;
72
+ }
73
+ catch (err) {
74
+ throw new Oid4vpError("oid4vp.jar_signing_failed", `signAuthorizationRequest: JWS signing failed: ${err instanceof Error ? err.message : String(err)}`);
75
+ }
76
+ }
77
+ //# sourceMappingURL=jar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jar.js","sourceRoot":"","sources":["../src/jar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAC5C,OAAO,EACL,WAAW,GAGZ,MAAM,YAAY,CAAC;AAYpB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAwC;IAExC,IACE,OAAO,CAAC,IAAI,KAAK,IAAI;QACrB,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAChC,OAAO,OAAO,CAAC,IAAI,CAAC,aAAa,KAAK,QAAQ,EAC9C,CAAC;QACD,MAAM,IAAI,WAAW,CACnB,2BAA2B,EAC3B,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,WAAW,CACnB,2BAA2B,EAC3B,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC;IACf,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,2BAA2B,EAC3B,2DACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,4DAA4D;QAC5D,uEAAuE;QACvE,iEAAiE;QACjE,8DAA8D;QAC9D,+BAA+B;QAC/B,MAAM,OAAO,GAA4B,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;aACnC,kBAAkB,CAAC;YAClB,GAAG,EAAE,OAAO;YACZ,GAAG,EAAE,qBAAqB;YAC1B,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;SAC3B,CAAC;aACD,IAAI,CAAC,UAAU,CAAC,CAAC;QACpB,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,2BAA2B,EAC3B,iDACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/request.d.ts CHANGED
@@ -3,16 +3,58 @@ import { type AuthorizationRequest } from "./types.js";
3
3
  * Serialise an Authorization Request to a URL with query parameters per
4
4
  * OID4VP §5.1 (parameter encoding rules).
5
5
  *
6
- * The base URL is typically `https://wallet.example.com/authorize` for web
7
- * flows or a custom scheme like `openid4vp://`. Both work — we just append
8
- * the query string.
6
+ * The base URL is typically a custom scheme like `openid4vp://` for the
7
+ * native wallet handoff, or `https://wallet.example.com/authorize` for
8
+ * web flows — both work, the function just appends the query string.
9
+ *
10
+ * Object-valued parameters (`presentation_definition`, `dcql_query`,
11
+ * `client_metadata`) are JSON-encoded into the query string per §5.1.
12
+ *
13
+ * For HAIP / EUDIW-compliant verifiers, this output is typically wrapped
14
+ * in a signed JAR (RFC 9101) via {@link signAuthorizationRequest} and
15
+ * delivered as `request=<jwt>` rather than as raw query params.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const url = buildAuthorizationRequestUrl("openid4vp://", {
20
+ * response_type: "vp_token",
21
+ * client_id: "x509_san_dns:verifier.example",
22
+ * nonce: "n-12345",
23
+ * response_mode: "direct_post",
24
+ * response_uri: "https://verifier.example/oid4vp/response",
25
+ * dcql_query: { credentials: [{ id: "pid", format: "dc+sd-jwt", meta: {...} }] },
26
+ * });
27
+ * ```
28
+ *
29
+ * @throws {@link Oid4vpError} `oid4vp.required_field_missing`,
30
+ * `oid4vp.unsupported_response_type`, `oid4vp.mutually_exclusive_fields`,
31
+ * `oid4vp.response_uri_required`, or `oid4vp.invalid_value_type`.
9
32
  */
10
33
  export declare function buildAuthorizationRequestUrl(baseUrl: string, request: AuthorizationRequest): string;
11
- /** Parse an OID4VP Authorization Request from a URL string. */
34
+ /**
35
+ * Parse an OID4VP Authorization Request from a URL string.
36
+ *
37
+ * Inverse of {@link buildAuthorizationRequestUrl}. Validates required
38
+ * fields and mutually-exclusive flag combinations; does NOT verify a
39
+ * signed JAR (use the wallet's JWS verification step for that, then
40
+ * call this function on the JAR's payload claims).
41
+ *
42
+ * @throws {@link Oid4vpError} with `oid4vp.invalid_url`,
43
+ * `oid4vp.required_field_missing`, or other field-validation codes.
44
+ */
12
45
  export declare function parseAuthorizationRequestUrl(rawUrl: string): AuthorizationRequest;
13
46
  /**
14
- * Parse from a `URLSearchParams` (or anything that quacks like one) — useful
15
- * when the verifier already has a parsed `req.query` from Express, Hono, etc.
47
+ * Parse an OID4VP Authorization Request from URL query parameters.
48
+ *
49
+ * Useful when the verifier already has parsed query params from a web
50
+ * framework (`req.query` from Express, Hono, Fastify, ...). Accepts
51
+ * either a `URLSearchParams` instance or a plain `Record<string, string>`.
52
+ *
53
+ * Object-valued params (`presentation_definition`, `dcql_query`,
54
+ * `client_metadata`) are JSON-decoded; everything else is left as-is.
55
+ *
56
+ * @throws {@link Oid4vpError} with `oid4vp.invalid_json` if a JSON-valued
57
+ * param is malformed, plus the standard field-validation codes.
16
58
  */
17
59
  export declare function parseAuthorizationRequestSearchParams(params: URLSearchParams | Record<string, string>): AuthorizationRequest;
18
60
  //# sourceMappingURL=request.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAYpE;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAkBR;AAED,+DAA+D;AAC/D,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,GACb,oBAAoB,CAWtB;AAED;;;GAGG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/C,oBAAoB,CA0CtB"}
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAYpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAkBR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,GACb,oBAAoB,CAWtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/C,oBAAoB,CA0CtB"}
package/dist/request.js CHANGED
@@ -11,9 +11,32 @@ const JSON_FIELDS = new Set([
11
11
  * Serialise an Authorization Request to a URL with query parameters per
12
12
  * OID4VP §5.1 (parameter encoding rules).
13
13
  *
14
- * The base URL is typically `https://wallet.example.com/authorize` for web
15
- * flows or a custom scheme like `openid4vp://`. Both work — we just append
16
- * the query string.
14
+ * The base URL is typically a custom scheme like `openid4vp://` for the
15
+ * native wallet handoff, or `https://wallet.example.com/authorize` for
16
+ * web flows — both work, the function just appends the query string.
17
+ *
18
+ * Object-valued parameters (`presentation_definition`, `dcql_query`,
19
+ * `client_metadata`) are JSON-encoded into the query string per §5.1.
20
+ *
21
+ * For HAIP / EUDIW-compliant verifiers, this output is typically wrapped
22
+ * in a signed JAR (RFC 9101) via {@link signAuthorizationRequest} and
23
+ * delivered as `request=<jwt>` rather than as raw query params.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const url = buildAuthorizationRequestUrl("openid4vp://", {
28
+ * response_type: "vp_token",
29
+ * client_id: "x509_san_dns:verifier.example",
30
+ * nonce: "n-12345",
31
+ * response_mode: "direct_post",
32
+ * response_uri: "https://verifier.example/oid4vp/response",
33
+ * dcql_query: { credentials: [{ id: "pid", format: "dc+sd-jwt", meta: {...} }] },
34
+ * });
35
+ * ```
36
+ *
37
+ * @throws {@link Oid4vpError} `oid4vp.required_field_missing`,
38
+ * `oid4vp.unsupported_response_type`, `oid4vp.mutually_exclusive_fields`,
39
+ * `oid4vp.response_uri_required`, or `oid4vp.invalid_value_type`.
17
40
  */
18
41
  export function buildAuthorizationRequestUrl(baseUrl, request) {
19
42
  validateRequest(request);
@@ -33,7 +56,17 @@ export function buildAuthorizationRequestUrl(baseUrl, request) {
33
56
  }
34
57
  return url.toString();
35
58
  }
36
- /** Parse an OID4VP Authorization Request from a URL string. */
59
+ /**
60
+ * Parse an OID4VP Authorization Request from a URL string.
61
+ *
62
+ * Inverse of {@link buildAuthorizationRequestUrl}. Validates required
63
+ * fields and mutually-exclusive flag combinations; does NOT verify a
64
+ * signed JAR (use the wallet's JWS verification step for that, then
65
+ * call this function on the JAR's payload claims).
66
+ *
67
+ * @throws {@link Oid4vpError} with `oid4vp.invalid_url`,
68
+ * `oid4vp.required_field_missing`, or other field-validation codes.
69
+ */
37
70
  export function parseAuthorizationRequestUrl(rawUrl) {
38
71
  let url;
39
72
  try {
@@ -45,8 +78,17 @@ export function parseAuthorizationRequestUrl(rawUrl) {
45
78
  return parseAuthorizationRequestSearchParams(url.searchParams);
46
79
  }
47
80
  /**
48
- * Parse from a `URLSearchParams` (or anything that quacks like one) — useful
49
- * when the verifier already has a parsed `req.query` from Express, Hono, etc.
81
+ * Parse an OID4VP Authorization Request from URL query parameters.
82
+ *
83
+ * Useful when the verifier already has parsed query params from a web
84
+ * framework (`req.query` from Express, Hono, Fastify, ...). Accepts
85
+ * either a `URLSearchParams` instance or a plain `Record<string, string>`.
86
+ *
87
+ * Object-valued params (`presentation_definition`, `dcql_query`,
88
+ * `client_metadata`) are JSON-decoded; everything else is left as-is.
89
+ *
90
+ * @throws {@link Oid4vpError} with `oid4vp.invalid_json` if a JSON-valued
91
+ * param is malformed, plus the standard field-validation codes.
50
92
  */
51
93
  export function parseAuthorizationRequestSearchParams(params) {
52
94
  const get = (k) => params instanceof URLSearchParams ? params.get(k) ?? undefined : params[k];
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA6B,MAAM,YAAY,CAAC;AAEpE,qCAAqC;AACrC,MAAM,eAAe,GAAG,CAAC,eAAe,EAAE,WAAW,EAAE,OAAO,CAAU,CAAC;AAEzE,sEAAsE;AACtE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS;IAClC,yBAAyB;IACzB,YAAY;IACZ,iBAAiB;CAClB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAe,EACf,OAA6B;IAE7B,eAAe,CAAC,OAAO,CAAC,CAAC;IAEzB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,WAAW,CACnB,2BAA2B,EAC3B,+BAA+B,GAAG,MAAM,OAAO,KAAK,EAAE,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,4BAA4B,CAC1C,MAAc;IAEd,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CACnB,oBAAoB,EACpB,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,qCAAqC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qCAAqC,CACnD,MAAgD;IAEhD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAsB,EAAE,CAC5C,MAAM,YAAY,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAE7E,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG;QACZ,eAAe;QACf,WAAW;QACX,kBAAkB;QAClB,eAAe;QACf,cAAc;QACd,cAAc;QACd,OAAO;QACP,OAAO;QACP,yBAAyB;QACzB,6BAA6B;QAC7B,YAAY;QACZ,iBAAiB;QACjB,OAAO;KACR,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,IAAI,WAAW,CACnB,qBAAqB,EACrB,+BAA+B,GAAG,MAChC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE,CACH,CAAC;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IAED,eAAe,CAAC,GAAoC,CAAC,CAAC;IACtD,OAAO,GAAsC,CAAC;AAChD,CAAC;AAED,SAAS,eAAe,CAAC,GAAkC;IACzD,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,WAAW,CACnB,+BAA+B,EAC/B,wDAAwD,KAAK,EAAE,CAChE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;QACrC,MAAM,IAAI,WAAW,CACnB,kCAAkC,EAClC,gEAAgE,GAAG,CAAC,aAAa,GAAG,CACrF,CAAC;IACJ,CAAC;IACD,IACE,GAAG,CAAC,uBAAuB,KAAK,SAAS;QACzC,GAAG,CAAC,2BAA2B,KAAK,SAAS,EAC7C,CAAC;QACD,MAAM,IAAI,WAAW,CACnB,kCAAkC,EAClC,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,IACE,GAAG,CAAC,uBAAuB,KAAK,SAAS;QACzC,GAAG,CAAC,UAAU,KAAK,SAAS,EAC5B,CAAC;QACD,MAAM,IAAI,WAAW,CACnB,kCAAkC,EAClC,yFAAyF,CAC1F,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,aAAa,KAAK,aAAa,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QAC1E,MAAM,IAAI,WAAW,CACnB,8BAA8B,EAC9B,+DAA+D,CAChE,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
@@ -3,9 +3,11 @@ import { type AuthorizationResponse } from "./types.js";
3
3
  * Serialise an Authorization Response to a URL-encoded form body, suitable
4
4
  * for `direct_post` (POST application/x-www-form-urlencoded). Per OID4VP §6:
5
5
  *
6
- * - `vp_token` is the raw presentation string, or a JSON-encoded array if
7
- * multiple credentials are presented.
8
- * - `presentation_submission` is JSON-encoded.
6
+ * - `vp_token` is the raw presentation string, a JSON-encoded array if
7
+ * multiple credentials are presented (PEX), or a JSON-encoded object
8
+ * keyed by DCQL credential id (DCQL response form).
9
+ * - `presentation_submission` is JSON-encoded — only included for PEX
10
+ * responses; DCQL responses omit it.
9
11
  * - `state` (optional) and `iss` (optional) pass through as strings.
10
12
  */
11
13
  export declare function buildAuthorizationResponseBody(response: AuthorizationResponse): string;
@@ -1 +1 @@
1
- {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKrE;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,CAuBR;AAED,2EAA2E;AAC3E,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,MAAM,GACd,qBAAqB,CAavB;AAED;+BAC+B;AAC/B,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/C,qBAAqB,CAqEvB"}
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAErE;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,CA+BR;AAED,2EAA2E;AAC3E,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,MAAM,GACd,qBAAqB,CAavB;AAED;+BAC+B;AAC/B,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/C,qBAAqB,CAsGvB"}
package/dist/response.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { Oid4vpError } from "./types.js";
2
- /** Required fields per OID4VP §6.1. */
3
- const REQUIRED_FIELDS = ["vp_token", "presentation_submission"];
4
2
  /**
5
3
  * Serialise an Authorization Response to a URL-encoded form body, suitable
6
4
  * for `direct_post` (POST application/x-www-form-urlencoded). Per OID4VP §6:
7
5
  *
8
- * - `vp_token` is the raw presentation string, or a JSON-encoded array if
9
- * multiple credentials are presented.
10
- * - `presentation_submission` is JSON-encoded.
6
+ * - `vp_token` is the raw presentation string, a JSON-encoded array if
7
+ * multiple credentials are presented (PEX), or a JSON-encoded object
8
+ * keyed by DCQL credential id (DCQL response form).
9
+ * - `presentation_submission` is JSON-encoded — only included for PEX
10
+ * responses; DCQL responses omit it.
11
11
  * - `state` (optional) and `iss` (optional) pass through as strings.
12
12
  */
13
13
  export function buildAuthorizationResponseBody(response) {
@@ -19,10 +19,17 @@ export function buildAuthorizationResponseBody(response) {
19
19
  else if (typeof response.vp_token === "string") {
20
20
  body.set("vp_token", response.vp_token);
21
21
  }
22
+ else if (response.vp_token !== null &&
23
+ typeof response.vp_token === "object") {
24
+ // DCQL response — JSON object keyed by credential id.
25
+ body.set("vp_token", JSON.stringify(response.vp_token));
26
+ }
22
27
  else {
23
- throw new Oid4vpError("oid4vp.invalid_value_type", "vp_token must be a string or an array of strings");
28
+ throw new Oid4vpError("oid4vp.invalid_value_type", "vp_token must be a string, array of strings, or DCQL credential map");
29
+ }
30
+ if (response.presentation_submission !== undefined) {
31
+ body.set("presentation_submission", JSON.stringify(response.presentation_submission));
24
32
  }
25
- body.set("presentation_submission", JSON.stringify(response.presentation_submission));
26
33
  if (response.state !== undefined)
27
34
  body.set("state", response.state);
28
35
  if (response.iss !== undefined)
@@ -49,39 +56,74 @@ export function parseAuthorizationResponseFromParams(params) {
49
56
  if (vpRaw === undefined) {
50
57
  throw new Oid4vpError("oid4vp.required_field_missing", "Authorization Response is missing required parameter: vp_token");
51
58
  }
52
- if (submissionRaw === undefined) {
53
- throw new Oid4vpError("oid4vp.required_field_missing", "Authorization Response is missing required parameter: presentation_submission");
54
- }
55
- let submission;
56
- try {
57
- submission = JSON.parse(submissionRaw);
58
- }
59
- catch (err) {
60
- throw new Oid4vpError("oid4vp.invalid_json", `presentation_submission is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
61
- }
62
- if (submission === null ||
63
- typeof submission !== "object" ||
64
- Array.isArray(submission)) {
65
- throw new Oid4vpError("oid4vp.malformed_submission", "presentation_submission must be a JSON object (DIF Presentation Exchange v2)");
66
- }
67
- // vp_token may be a string OR a JSON-encoded array of strings.
59
+ // vp_token may be:
60
+ // - a single SD-JWT-VC string (PEX, single credential)
61
+ // - a JSON-encoded string[] (PEX, multiple credentials)
62
+ // - a JSON-encoded object keyed by DCQL credential id (DCQL response,
63
+ // OID4VP Final 1.0 — what production EU wallets send)
68
64
  let vp_token = vpRaw;
69
- if (vpRaw.trim().startsWith("[")) {
65
+ const trimmed = vpRaw.trim();
66
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
70
67
  try {
71
68
  const parsed = JSON.parse(vpRaw);
72
- if (Array.isArray(parsed) &&
73
- parsed.every((p) => typeof p === "string")) {
69
+ if (Array.isArray(parsed) && parsed.every((p) => typeof p === "string")) {
74
70
  vp_token = parsed;
75
71
  }
72
+ else if (parsed !== null &&
73
+ typeof parsed === "object" &&
74
+ !Array.isArray(parsed)) {
75
+ // DCQL response form — keys are credential ids, values are
76
+ // (single-credential) strings or arrays.
77
+ const obj = parsed;
78
+ const flat = {};
79
+ for (const [id, val] of Object.entries(obj)) {
80
+ if (typeof val === "string") {
81
+ flat[id] = val;
82
+ }
83
+ else if (Array.isArray(val) &&
84
+ val.length === 1 &&
85
+ typeof val[0] === "string") {
86
+ // Some wallets wrap each credential in a single-element array
87
+ // (anticipating multi-instance presentations). Flatten for our
88
+ // SD-JWT-VC verifier which expects a single string per id.
89
+ flat[id] = val[0];
90
+ }
91
+ else {
92
+ throw new Oid4vpError("oid4vp.invalid_value_type", `vp_token[${id}] must be a string or single-element array of strings`);
93
+ }
94
+ }
95
+ vp_token = flat;
96
+ }
76
97
  }
77
- catch {
78
- // Not a JSON array — treat as a literal string.
98
+ catch (err) {
99
+ if (err instanceof Oid4vpError)
100
+ throw err;
101
+ // Not a JSON array/object — treat as a literal string.
79
102
  }
80
103
  }
81
- const result = {
82
- vp_token,
83
- presentation_submission: submission,
84
- };
104
+ // presentation_submission is required for PEX responses (string or
105
+ // string[] vp_token), optional for DCQL responses (object vp_token).
106
+ const isDcqlResponse = typeof vp_token === "object" && !Array.isArray(vp_token);
107
+ let submission;
108
+ if (submissionRaw !== undefined) {
109
+ let parsed;
110
+ try {
111
+ parsed = JSON.parse(submissionRaw);
112
+ }
113
+ catch (err) {
114
+ throw new Oid4vpError("oid4vp.invalid_json", `presentation_submission is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
115
+ }
116
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
117
+ throw new Oid4vpError("oid4vp.malformed_submission", "presentation_submission must be a JSON object (DIF Presentation Exchange v2)");
118
+ }
119
+ submission = parsed;
120
+ }
121
+ else if (!isDcqlResponse) {
122
+ throw new Oid4vpError("oid4vp.required_field_missing", "Authorization Response is missing required parameter: presentation_submission");
123
+ }
124
+ const result = { vp_token };
125
+ if (submission !== undefined)
126
+ result.presentation_submission = submission;
85
127
  const state = get("state");
86
128
  if (state !== undefined)
87
129
  result.state = state;
@@ -92,15 +134,23 @@ export function parseAuthorizationResponseFromParams(params) {
92
134
  return result;
93
135
  }
94
136
  function validateResponse(resp) {
95
- for (const field of REQUIRED_FIELDS) {
96
- if (resp[field] === undefined) {
97
- throw new Oid4vpError("oid4vp.required_field_missing", `Authorization Response is missing required parameter: ${field}`);
98
- }
137
+ if (resp.vp_token === undefined) {
138
+ throw new Oid4vpError("oid4vp.required_field_missing", "Authorization Response is missing required parameter: vp_token");
139
+ }
140
+ const isString = typeof resp.vp_token === "string";
141
+ const isStringArray = Array.isArray(resp.vp_token) &&
142
+ resp.vp_token.every((v) => typeof v === "string");
143
+ const isDcqlMap = !Array.isArray(resp.vp_token) &&
144
+ resp.vp_token !== null &&
145
+ typeof resp.vp_token === "object" &&
146
+ Object.values(resp.vp_token).every((v) => typeof v === "string");
147
+ if (!isString && !isStringArray && !isDcqlMap) {
148
+ throw new Oid4vpError("oid4vp.invalid_value_type", "vp_token must be a string, array of strings, or DCQL credential map");
99
149
  }
100
- if (typeof resp.vp_token !== "string" &&
101
- !(Array.isArray(resp.vp_token) &&
102
- resp.vp_token.every((v) => typeof v === "string"))) {
103
- throw new Oid4vpError("oid4vp.invalid_value_type", "vp_token must be a string or an array of strings");
150
+ // PEX path requires presentation_submission; DCQL path doesn't.
151
+ if ((isString || isStringArray) &&
152
+ resp.presentation_submission === undefined) {
153
+ throw new Oid4vpError("oid4vp.required_field_missing", "Authorization Response is missing required parameter: presentation_submission");
104
154
  }
105
155
  }
106
156
  //# sourceMappingURL=response.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../src/response.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { GramotaError } from "@gramota/core";
1
2
  /**
2
3
  * OID4VP Final 1.0 wire-format types.
3
4
  * Spec: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
@@ -47,21 +48,68 @@ export interface AuthorizationRequest {
47
48
  * is willing to accept. */
48
49
  scope?: string;
49
50
  }
50
- /** OID4VP §6 — Authorization Response from wallet to verifier. */
51
+ /** OID4VP §6 — Authorization Response from wallet to verifier.
52
+ *
53
+ * The wire shape depends on which query language the request used:
54
+ *
55
+ * - **Presentation Exchange (DIF PEX)**: `vp_token` is a string or
56
+ * string[], paired with `presentation_submission` mapping descriptors
57
+ * to vp_token positions.
58
+ * - **DCQL** (OID4VP Final 1.0): `vp_token` is a JSON OBJECT keyed by
59
+ * the DCQL credential `id` (e.g. `{"pid": "<sd-jwt-vc>"}`). No
60
+ * `presentation_submission` is sent — the keys ARE the mapping.
61
+ *
62
+ * Verifiers should accept whichever shape the wallet sends; production EU
63
+ * wallets (eudi-lib-android-wallet-ui 0.26+) use DCQL exclusively.
64
+ */
51
65
  export interface AuthorizationResponse {
52
- /** The presentation(s) — a single SD-JWT-VC or an array for multi-credential. */
53
- vp_token: string | readonly string[];
54
- /** DIF Presentation Submission mapping descriptors → vp_token positions. */
55
- presentation_submission: Readonly<Record<string, unknown>>;
66
+ /** The presentation(s).
67
+ *
68
+ * - String / string[] form (PEX response).
69
+ * - Object form (DCQL response) — keys are the DCQL credential ids
70
+ * and values are the credential strings.
71
+ */
72
+ vp_token: string | readonly string[] | Readonly<Record<string, string>>;
73
+ /** DIF Presentation Submission mapping descriptors → vp_token positions.
74
+ * Required for PEX responses; absent for DCQL responses. */
75
+ presentation_submission?: Readonly<Record<string, unknown>>;
56
76
  /** Echoes the verifier's `state` from the request. */
57
77
  state?: string;
58
78
  /** OID4VP §6.4 — the wallet's identifier (e.g. issuer URL). */
59
79
  iss?: string;
60
80
  }
81
+ /**
82
+ * X.509 signing material for an OID4VP verifier.
83
+ *
84
+ * Bundles together the four artefacts a verifier needs to produce signed
85
+ * Authorization Requests (RFC 9101 JAR) and prove its identity to wallets
86
+ * via the `x509_san_dns` client_id_prefix:
87
+ *
88
+ * - `privateKeyPem` — PKCS#8 private key, used to sign the JAR
89
+ * - `certificatePem` — leaf cert, served when the wallet asks for proof
90
+ * - `x5c` — base64 DER cert(s) embedded in the JWS `x5c` header
91
+ * - `sanDns` — the SAN-DNS hostname the wallet matches against `client_id`
92
+ *
93
+ * Generation: see `generateSigningCert` for self-signed certs (local dev,
94
+ * pinned-trust-store deployments). Production typically uses an externally
95
+ * issued cert (ACME, corporate CA) — same shape, different origin.
96
+ */
97
+ export interface SigningCert {
98
+ /** PEM-encoded PKCS#8 private key. */
99
+ readonly privateKeyPem: string;
100
+ /** PEM-encoded leaf certificate. */
101
+ readonly certificatePem: string;
102
+ /** Base64-encoded DER certs in chain order, suitable for the JWS `x5c`
103
+ * header per RFC 7515 §4.1.6. Length 1 for self-signed leaves. */
104
+ readonly x5c: readonly string[];
105
+ /** DNS name in the cert's Subject Alternative Name. The wallet
106
+ * compares this against the OID4VP `client_id` value (with the
107
+ * `x509_san_dns:` prefix stripped). */
108
+ readonly sanDns: string;
109
+ }
61
110
  /** Stable codes for `Oid4vpError`. */
62
- export type Oid4vpErrorCode = "oid4vp.invalid_url" | "oid4vp.required_field_missing" | "oid4vp.unsupported_response_type" | "oid4vp.mutually_exclusive_fields" | "oid4vp.response_uri_required" | "oid4vp.invalid_json" | "oid4vp.invalid_value_type" | "oid4vp.malformed_body" | "oid4vp.malformed_submission";
63
- export declare class Oid4vpError extends Error {
64
- readonly name = "Oid4vpError";
111
+ export type Oid4vpErrorCode = "oid4vp.invalid_url" | "oid4vp.required_field_missing" | "oid4vp.unsupported_response_type" | "oid4vp.mutually_exclusive_fields" | "oid4vp.response_uri_required" | "oid4vp.invalid_json" | "oid4vp.invalid_value_type" | "oid4vp.malformed_body" | "oid4vp.malformed_submission" | "oid4vp.cert_generation_failed" | "oid4vp.jar_signing_failed";
112
+ export declare class Oid4vpError extends GramotaError {
65
113
  readonly code: Oid4vpErrorCode;
66
114
  constructor(code: Oid4vpErrorCode, message: string, options?: {
67
115
  cause?: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GACtB,gBAAgB,GAChB,cAAc,GACd,OAAO,GACP,KAAK,GACL,cAAc,GACd,cAAc,GACd,sBAAsB,GACtB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,oEAAoE;AACpE,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,iBAAiB,GACjB,UAAU,GACV,OAAO,CAAC;AAEZ;;;;2DAI2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,aAAa,EAAE,UAAU,CAAC;IAC1B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB;uEACmE;IACnE,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC,wEAAwE;IACxE,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;mCAC+B;IAC/B,uBAAuB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,gFAAgF;IAChF,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC;4DACwD;IACxD,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,8CAA8C;IAC9C,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD;+BAC2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kEAAkE;AAClE,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,sCAAsC;AACtC,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,+BAA+B,GAC/B,kCAAkC,GAClC,kCAAkC,GAClC,8BAA8B,GAC9B,qBAAqB,GACrB,2BAA2B,GAC3B,uBAAuB,GACvB,6BAA6B,CAAC;AAElC,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAkB,IAAI,iBAAiB;IACvC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;gBAG7B,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAQhC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;GAOG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GACtB,gBAAgB,GAChB,cAAc,GACd,OAAO,GACP,KAAK,GACL,cAAc,GACd,cAAc,GACd,sBAAsB,GACtB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,oEAAoE;AACpE,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,iBAAiB,GACjB,UAAU,GACV,OAAO,CAAC;AAEZ;;;;2DAI2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,aAAa,EAAE,UAAU,CAAC;IAC1B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB;uEACmE;IACnE,gBAAgB,CAAC,EAAE,cAAc,CAAC;IAClC,wEAAwE;IACxE,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;mCAC+B;IAC/B,uBAAuB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,gFAAgF;IAChF,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC;4DACwD;IACxD,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/C,8CAA8C;IAC9C,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD;+BAC2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxE;gEAC4D;IAC5D,uBAAuB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,oCAAoC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;sEACkE;IAClE,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;2CAEuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,sCAAsC;AACtC,MAAM,MAAM,eAAe,GACvB,oBAAoB,GACpB,+BAA+B,GAC/B,kCAAkC,GAClC,kCAAkC,GAClC,8BAA8B,GAC9B,qBAAqB,GACrB,2BAA2B,GAC3B,uBAAuB,GACvB,6BAA6B,GAC7B,+BAA+B,GAC/B,2BAA2B,CAAC;AAEhC,qBAAa,WAAY,SAAQ,YAAY;IAC3C,SAAkB,IAAI,EAAE,eAAe,CAAC;gBAGtC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC"}
package/dist/types.js CHANGED
@@ -1,20 +1,10 @@
1
- /**
2
- * OID4VP Final 1.0 wire-format types.
3
- * Spec: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html
4
- *
5
- * Scope of this package: §5 Authorization Request, §6 Authorization Response.
6
- * Presentation Definition (§5.4) is modelled but not yet matched/queried —
7
- * downstream packages will add DIF Presentation Exchange JSONPath support.
8
- */
9
- export class Oid4vpError extends Error {
10
- name = "Oid4vpError";
1
+ import { GramotaError } from "@gramota/core";
2
+ export class Oid4vpError extends GramotaError {
11
3
  code;
12
4
  constructor(code, message, options) {
13
- super(message);
5
+ super(message, code, options);
6
+ this.name = "Oid4vpError";
14
7
  this.code = code;
15
- if (options?.cause !== undefined) {
16
- this.cause = options.cause;
17
- }
18
8
  }
19
9
  }
20
10
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkFH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAClB,IAAI,GAAG,aAAa,CAAC;IAC9B,IAAI,CAAkB;IAE/B,YACE,IAAqB,EACrB,OAAe,EACf,OAA6B;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,IAA4B,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACtD,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AA8I7C,MAAM,OAAO,WAAY,SAAQ,YAAY;IACzB,IAAI,CAAkB;IAExC,YACE,IAAqB,EACrB,OAAe,EACf,OAA6B;QAE7B,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramota/oid4vp",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "OpenID for Verifiable Presentations transport — authorization request/response wire format for EUDIW.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -15,6 +15,16 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "dependencies": {
19
+ "@peculiar/x509": "^2.0.0",
20
+ "jose": "^5.9.6",
21
+ "reflect-metadata": "^0.2.0",
22
+ "@gramota/core": "0.2.0",
23
+ "@gramota/jose": "0.2.1"
24
+ },
25
+ "sideEffects": [
26
+ "./dist/cert.js"
27
+ ],
18
28
  "author": "Petromil Pavlov <petromilpavlov@gmail.com>",
19
29
  "homepage": "https://github.com/gramota-org/gramota#readme",
20
30
  "bugs": {
@@ -23,7 +33,6 @@
23
33
  "engines": {
24
34
  "node": ">=20"
25
35
  },
26
- "sideEffects": false,
27
36
  "publishConfig": {
28
37
  "access": "public",
29
38
  "registry": "https://registry.npmjs.org/"