@owf/eudi-wrprc 0.0.0

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/dist/index.mjs ADDED
@@ -0,0 +1,892 @@
1
+ import { z } from "zod";
2
+ import { IdentityException, base64urlEncode, decodeJwt } from "@owf/identity-common";
3
+ import { pemToDer } from "@owf/crypto";
4
+ //#region src/schemas.ts
5
+ /**
6
+ * WRPRC Zod Schemas
7
+ *
8
+ * Zod schemas for ETSI TS 119 475 (Wallet-Relying Party Registration Certificates).
9
+ * Types are derived from these schemas via z.infer<>.
10
+ *
11
+ * @see https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf
12
+ */
13
+ /**
14
+ * Multilingual string schema (B.2.6 Class MultiLangString)
15
+ */
16
+ const MultiLangStringSchema = z.object({
17
+ lang: z.string().min(2),
18
+ value: z.string().min(1)
19
+ });
20
+ /**
21
+ * Supervisory Authority schema for Data Protection Authority
22
+ */
23
+ const SupervisoryAuthoritySchema = z.object({
24
+ email: z.string().email().optional(),
25
+ phone: z.string().optional(),
26
+ uri: z.string().url().optional()
27
+ });
28
+ /**
29
+ * Claim schema for credential attribute specification (B.2.10 Class Claim)
30
+ */
31
+ const ClaimSchema = z.object({
32
+ path: z.array(z.string()).min(1),
33
+ values: z.array(z.union([
34
+ z.string(),
35
+ z.number(),
36
+ z.boolean()
37
+ ])).optional()
38
+ });
39
+ /**
40
+ * Credential schema for attestations (B.2.9 Class Credential)
41
+ */
42
+ const CredentialSchema = z.object({
43
+ format: z.string().min(1),
44
+ meta: z.record(z.unknown()),
45
+ claim: z.array(ClaimSchema).optional()
46
+ });
47
+ /**
48
+ * Status list reference for certificate validity
49
+ */
50
+ const StatusListSchema = z.object({
51
+ idx: z.number().int().nonnegative(),
52
+ uri: z.string().url()
53
+ });
54
+ /**
55
+ * Status schema for WRPRC validity
56
+ */
57
+ const StatusSchema = z.object({ status_list: StatusListSchema });
58
+ /**
59
+ * Intermediary information when WRP acts through an intermediary
60
+ */
61
+ const IntermediarySchema = z.object({
62
+ sub: z.string().min(1),
63
+ name: z.string().min(1)
64
+ });
65
+ /**
66
+ * WRPRC Payload schema according to ETSI TS 119 475 clause 5.2.4
67
+ */
68
+ const WRPRCPayloadSchema = z.object({
69
+ name: z.string().min(1),
70
+ sub_ln: z.string().optional(),
71
+ sub_gn: z.string().optional(),
72
+ sub_fn: z.string().optional(),
73
+ sub: z.string().min(1),
74
+ country: z.string().length(2),
75
+ registry_uri: z.string().url(),
76
+ srv_description: z.array(z.array(MultiLangStringSchema)).optional(),
77
+ entitlements: z.array(z.string().url()).min(1),
78
+ privacy_policy: z.string().url().optional(),
79
+ info_uri: z.string().url().optional(),
80
+ support_uri: z.string().url().optional(),
81
+ supervisory_authority: SupervisoryAuthoritySchema.optional(),
82
+ policy_id: z.array(z.string()).optional(),
83
+ certificate_policy: z.string().url().optional(),
84
+ iat: z.number().int().positive(),
85
+ status: StatusSchema.optional(),
86
+ purpose: z.array(MultiLangStringSchema).optional(),
87
+ credentials: z.array(CredentialSchema).optional(),
88
+ provides_attestations: z.array(CredentialSchema).optional(),
89
+ intermediary: IntermediarySchema.optional()
90
+ });
91
+ /**
92
+ * JWT Header schema according to ETSI TS 119 475 clause 5.2.2
93
+ */
94
+ const WRPRCJWTHeaderSchema = z.object({
95
+ typ: z.literal("rc-wrp+jwt"),
96
+ alg: z.enum([
97
+ "ES256",
98
+ "ES384",
99
+ "ES512",
100
+ "RS256",
101
+ "RS384",
102
+ "RS512"
103
+ ]),
104
+ x5c: z.array(z.string()).min(1),
105
+ kid: z.string().optional()
106
+ });
107
+ /**
108
+ * CWT Header schema according to ETSI TS 119 475 clause 5.2.3
109
+ */
110
+ const WRPRCCWTHeaderSchema = z.object({
111
+ typ: z.literal("rc-wrp+cwt"),
112
+ alg: z.number().int(),
113
+ x5chain: z.array(z.instanceof(Uint8Array)).min(1)
114
+ });
115
+ z.object({
116
+ header: WRPRCJWTHeaderSchema,
117
+ payload: WRPRCPayloadSchema
118
+ });
119
+ z.object({
120
+ header: WRPRCCWTHeaderSchema,
121
+ payload: WRPRCPayloadSchema
122
+ });
123
+ /**
124
+ * Schema for legal person WRPRC subject
125
+ */
126
+ const LegalPersonSubjectSchema = WRPRCPayloadSchema.extend({ sub_ln: z.string().min(1) }).omit({
127
+ sub_gn: true,
128
+ sub_fn: true
129
+ });
130
+ /**
131
+ * Schema for natural person WRPRC subject
132
+ */
133
+ const NaturalPersonSubjectSchema = WRPRCPayloadSchema.extend({
134
+ sub_gn: z.string().min(1),
135
+ sub_fn: z.string().min(1)
136
+ }).omit({ sub_ln: true });
137
+ //#endregion
138
+ //#region src/entitlements.ts
139
+ /**
140
+ * WRPRC Entitlement Constants
141
+ *
142
+ * Entitlement identifiers as defined in ETSI TS 119 475 Annex A.
143
+ * These identifiers define the roles and capabilities of Wallet-Relying Parties.
144
+ *
145
+ * @see https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf
146
+ */
147
+ /**
148
+ * Base OID for ETSI WRPA entitlements: 0.4.0.19475
149
+ * id-etsi-wrpa OBJECT IDENTIFIER ::= { itu-t(0) identified-organization(4) etsi(0) 19475 }
150
+ */
151
+ const ETSI_WRPA_BASE_OID = "0.4.0.19475";
152
+ `${ETSI_WRPA_BASE_OID}`;
153
+ /**
154
+ * Service_Provider - General service provider (A.2.1)
155
+ * OID: id-etsi-wrpa-entitlement 1
156
+ */
157
+ const ENTITLEMENT_SERVICE_PROVIDER = "https://uri.etsi.org/19475/Entitlement/Service_Provider";
158
+ /**
159
+ * QEAA_Provider - Qualified trust service provider issuing qualified electronic attestations of attributes (A.2.2)
160
+ * OID: id-etsi-wrpa-entitlement 2
161
+ */
162
+ const ENTITLEMENT_QEAA_PROVIDER = "https://uri.etsi.org/19475/Entitlement/QEAA_Provider";
163
+ /**
164
+ * Non_Q_EAA_Provider - Trust service provider issuing non-qualified electronic attestations of attributes (A.2.3)
165
+ * OID: id-etsi-wrpa-entitlement 3
166
+ */
167
+ const ENTITLEMENT_NON_Q_EAA_PROVIDER = "https://uri.etsi.org/19475/Entitlement/Non_Q_EAA_Provider";
168
+ /**
169
+ * PUB_EAA_Provider - Public sector body or its agent issuing electronic attestations of attributes from authentic sources (A.2.4)
170
+ * OID: id-etsi-wrpa-entitlement 4
171
+ */
172
+ const ENTITLEMENT_PUB_EAA_PROVIDER = "https://uri.etsi.org/19475/Entitlement/PUB_EAA_Provider";
173
+ /**
174
+ * PID_Provider - Provider of person identification data (A.2.5)
175
+ * OID: id-etsi-wrpa-entitlement 5
176
+ */
177
+ const ENTITLEMENT_PID_PROVIDER = "https://uri.etsi.org/19475/Entitlement/PID_Provider";
178
+ /**
179
+ * All standard WRP entitlements defined in ETSI TS 119 475
180
+ */
181
+ const WRP_ENTITLEMENTS = {
182
+ SERVICE_PROVIDER: ENTITLEMENT_SERVICE_PROVIDER,
183
+ QEAA_PROVIDER: ENTITLEMENT_QEAA_PROVIDER,
184
+ NON_Q_EAA_PROVIDER: ENTITLEMENT_NON_Q_EAA_PROVIDER,
185
+ PUB_EAA_PROVIDER: ENTITLEMENT_PUB_EAA_PROVIDER,
186
+ PID_PROVIDER: ENTITLEMENT_PID_PROVIDER,
187
+ QCERT_FOR_ESEAL_PROVIDER: "https://uri.etsi.org/19475/Entitlement/QCert_for_ESeal_Provider",
188
+ QCERT_FOR_ESIG_PROVIDER: "https://uri.etsi.org/19475/Entitlement/QCert_for_ESig_Provider",
189
+ RQSEALCDS_PROVIDER: "https://uri.etsi.org/19475/Entitlement/rQSealCDs_Provider",
190
+ RQSIGCDS_PROVIDER: "https://uri.etsi.org/19475/Entitlement/rQSigCDs_Provider",
191
+ ESIG_ESEAL_CREATION_PROVIDER: "https://uri.etsi.org/19475/Entitlement/ESig_ESeal_Creation_Provider"
192
+ };
193
+ /**
194
+ * List of all standard entitlement URIs
195
+ */
196
+ const ALL_ENTITLEMENTS = Object.values(WRP_ENTITLEMENTS);
197
+ /**
198
+ * Entitlements for attestation providers (QEAA, Non-Q EAA, PUB EAA, PID)
199
+ */
200
+ const ATTESTATION_PROVIDER_ENTITLEMENTS = [
201
+ ENTITLEMENT_QEAA_PROVIDER,
202
+ ENTITLEMENT_NON_Q_EAA_PROVIDER,
203
+ ENTITLEMENT_PUB_EAA_PROVIDER,
204
+ ENTITLEMENT_PID_PROVIDER
205
+ ];
206
+ /**
207
+ * Payment Service Provider Sub-entitlements (A.3.1)
208
+ * As defined in ETSI TS 119 495
209
+ */
210
+ const PSP_SUB_ENTITLEMENTS = {
211
+ ACCOUNT_SERVICING: "https://uri.etsi.org/19475/SubEntitlement/psp/psp-as",
212
+ PAYMENT_INITIATION: "https://uri.etsi.org/19475/SubEntitlement/psp/psp-pi",
213
+ ACCOUNT_INFORMATION: "https://uri.etsi.org/19475/SubEntitlement/psp/psp-ai",
214
+ CARD_BASED: "https://uri.etsi.org/19475/SubEntitlement/psp/psp-ic",
215
+ UNSPECIFIED: "https://uri.etsi.org/19475/SubEntitlement/psp/unspecified"
216
+ };
217
+ /**
218
+ * All PSP sub-entitlement URIs
219
+ */
220
+ const ALL_PSP_SUB_ENTITLEMENTS = Object.values(PSP_SUB_ENTITLEMENTS);
221
+ /**
222
+ * Identifier type URIs for legal person semantic identifiers
223
+ */
224
+ const IDENTIFIER_TYPES = {
225
+ EORI: "http://data.europa.eu/eudi/id/EORI-No",
226
+ LEI: "http://data.europa.eu/eudi/id/LEI",
227
+ EUID: "http://data.europa.eu/eudi/id/EUID",
228
+ VATIN: "http://data.europa.eu/eudi/id/VATIN",
229
+ TIN: "http://data.europa.eu/eudi/id/TIN",
230
+ EXCISE: "http://data.europa.eu/eudi/id/Excise"
231
+ };
232
+ IDENTIFIER_TYPES.EORI, IDENTIFIER_TYPES.LEI, IDENTIFIER_TYPES.EUID, IDENTIFIER_TYPES.VATIN, IDENTIFIER_TYPES.TIN, IDENTIFIER_TYPES.EXCISE;
233
+ `${ETSI_WRPA_BASE_OID}`;
234
+ /**
235
+ * Check if a given URI is a valid WRP entitlement
236
+ */
237
+ function isValidEntitlement(uri) {
238
+ return ALL_ENTITLEMENTS.includes(uri);
239
+ }
240
+ /**
241
+ * Check if a given URI is a PSP sub-entitlement
242
+ */
243
+ function isPSPSubEntitlement(uri) {
244
+ return ALL_PSP_SUB_ENTITLEMENTS.includes(uri);
245
+ }
246
+ /**
247
+ * Check if entitlements include an attestation provider role
248
+ */
249
+ function hasAttestationProviderEntitlement(entitlements) {
250
+ return entitlements.some((e) => ATTESTATION_PROVIDER_ENTITLEMENTS.includes(e));
251
+ }
252
+ //#endregion
253
+ //#region src/wrprc-exception.ts
254
+ /**
255
+ * WRPRCException is a custom error class for WRPRC-related exceptions.
256
+ */
257
+ var WRPRCException = class WRPRCException extends IdentityException {
258
+ constructor(message, details) {
259
+ super(message, details);
260
+ Object.setPrototypeOf(this, WRPRCException.prototype);
261
+ this.name = "WRPRCException";
262
+ }
263
+ };
264
+ //#endregion
265
+ //#region src/validator.ts
266
+ /**
267
+ * WRPRC Validator
268
+ *
269
+ * Validation functions for ETSI TS 119 475 Wallet-Relying Party Registration Certificates.
270
+ *
271
+ * @see https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf
272
+ */
273
+ /**
274
+ * Validate a WRPRC payload against the schema
275
+ */
276
+ function validateWRPRCPayload(payload) {
277
+ const result = WRPRCPayloadSchema.safeParse(payload);
278
+ const errors = [];
279
+ const warnings = [];
280
+ if (!result.success) {
281
+ for (const issue of result.error.issues) errors.push({
282
+ path: issue.path.map(String),
283
+ message: issue.message,
284
+ code: issue.code
285
+ });
286
+ return {
287
+ valid: false,
288
+ errors,
289
+ warnings
290
+ };
291
+ }
292
+ const validPayload = result.data;
293
+ for (const entitlement of validPayload.entitlements) if (!ALL_ENTITLEMENTS.includes(entitlement)) {
294
+ if (!ALL_PSP_SUB_ENTITLEMENTS.includes(entitlement)) warnings.push({
295
+ path: ["entitlements"],
296
+ message: `Unknown entitlement URI: ${entitlement}. This may be a national or EU-defined extension.`,
297
+ code: "unknown_entitlement"
298
+ });
299
+ }
300
+ if (validPayload.entitlements.length === 0) errors.push({
301
+ path: ["entitlements"],
302
+ message: "At least one entitlement must be specified (GEN-5.2.4-03)",
303
+ code: "missing_entitlement"
304
+ });
305
+ const hasSubEntitlement = validPayload.entitlements.some((e) => ALL_PSP_SUB_ENTITLEMENTS.includes(e));
306
+ const hasServiceProvider = validPayload.entitlements.includes("https://uri.etsi.org/19475/Entitlement/Service_Provider");
307
+ if (hasSubEntitlement && !hasServiceProvider) errors.push({
308
+ path: ["entitlements"],
309
+ message: "Service provider sub-entitlements require Service_Provider entitlement (GEN-5.2.4-04)",
310
+ code: "missing_base_entitlement"
311
+ });
312
+ if (validPayload.entitlements.some((e) => ATTESTATION_PROVIDER_ENTITLEMENTS.includes(e)) && !validPayload.provides_attestations) warnings.push({
313
+ path: ["provides_attestations"],
314
+ message: "Attestation providers should include provides_attestations field (GEN-5.2.4-05). This is recommended but not required.",
315
+ code: "missing_provides_attestations"
316
+ });
317
+ const subResult = validateSemanticIdentifier(validPayload.sub);
318
+ if (!subResult.valid) errors.push({
319
+ path: ["sub"],
320
+ message: subResult.message,
321
+ code: "invalid_semantic_identifier"
322
+ });
323
+ return {
324
+ valid: errors.length === 0,
325
+ errors,
326
+ warnings
327
+ };
328
+ }
329
+ /**
330
+ * Validate a JWT header for WRPRC
331
+ */
332
+ function validateWRPRCJWTHeader(header) {
333
+ const result = WRPRCJWTHeaderSchema.safeParse(header);
334
+ const errors = [];
335
+ const warnings = [];
336
+ if (!result.success) for (const issue of result.error.issues) errors.push({
337
+ path: issue.path.map(String),
338
+ message: issue.message,
339
+ code: issue.code
340
+ });
341
+ return {
342
+ valid: errors.length === 0,
343
+ errors,
344
+ warnings
345
+ };
346
+ }
347
+ /**
348
+ * Validate a complete WRPRC (header + payload)
349
+ */
350
+ function validateWRPRC(header, payload) {
351
+ const headerResult = validateWRPRCJWTHeader(header);
352
+ const payloadResult = validateWRPRCPayload(payload);
353
+ const errors = [...headerResult.errors.map((e) => ({
354
+ ...e,
355
+ path: ["header", ...e.path]
356
+ })), ...payloadResult.errors.map((e) => ({
357
+ ...e,
358
+ path: ["payload", ...e.path]
359
+ }))];
360
+ const warnings = [...headerResult.warnings.map((w) => ({
361
+ ...w,
362
+ path: ["header", ...w.path]
363
+ })), ...payloadResult.warnings.map((w) => ({
364
+ ...w,
365
+ path: ["payload", ...w.path]
366
+ }))];
367
+ return {
368
+ valid: errors.length === 0,
369
+ errors,
370
+ warnings
371
+ };
372
+ }
373
+ /**
374
+ * Check if payload represents a legal person
375
+ */
376
+ function isLegalPersonWRPRC(payload) {
377
+ return typeof payload.sub_ln === "string" && payload.sub_ln.length > 0;
378
+ }
379
+ /**
380
+ * Check if payload represents a natural person
381
+ */
382
+ function isNaturalPersonWRPRC(payload) {
383
+ return typeof payload.sub_gn === "string" && payload.sub_gn.length > 0 && typeof payload.sub_fn === "string" && payload.sub_fn.length > 0;
384
+ }
385
+ /**
386
+ * Validate as legal person WRPRC
387
+ */
388
+ function validateLegalPersonWRPRC(payload) {
389
+ const result = LegalPersonSubjectSchema.safeParse(payload);
390
+ const errors = [];
391
+ const warnings = [];
392
+ if (!result.success) for (const issue of result.error.issues) errors.push({
393
+ path: issue.path.map(String),
394
+ message: issue.message,
395
+ code: issue.code
396
+ });
397
+ if (result.success) {
398
+ const sub = result.data.sub;
399
+ if (!/^[A-Z]{3}[A-Z]{2}-.+$/.test(sub)) warnings.push({
400
+ path: ["sub"],
401
+ message: "Legal person identifier should follow format: PREFIX + COUNTRY + \"-\" + ID (e.g., \"LEIXG-529900T8BM49AURSDO55\")",
402
+ code: "identifier_format_warning"
403
+ });
404
+ }
405
+ return {
406
+ valid: errors.length === 0,
407
+ errors,
408
+ warnings
409
+ };
410
+ }
411
+ /**
412
+ * Validate as natural person WRPRC
413
+ */
414
+ function validateNaturalPersonWRPRC(payload) {
415
+ const result = NaturalPersonSubjectSchema.safeParse(payload);
416
+ const errors = [];
417
+ const warnings = [];
418
+ if (!result.success) for (const issue of result.error.issues) errors.push({
419
+ path: issue.path.map(String),
420
+ message: issue.message,
421
+ code: issue.code
422
+ });
423
+ if (result.success) {
424
+ const sub = result.data.sub;
425
+ if (!/^[A-Z]{3}[A-Z]{2}-.+$/.test(sub)) warnings.push({
426
+ path: ["sub"],
427
+ message: "Natural person identifier should follow format: PREFIX + COUNTRY + \"-\" + ID (e.g., \"TINIT-RSSMRA85T10A562S\")",
428
+ code: "identifier_format_warning"
429
+ });
430
+ }
431
+ return {
432
+ valid: errors.length === 0,
433
+ errors,
434
+ warnings
435
+ };
436
+ }
437
+ /**
438
+ * Valid semantic identifier prefixes for legal persons (ETSI EN 319 412-1 clause 5.1.4)
439
+ */
440
+ const LEGAL_PERSON_PREFIXES = [
441
+ "EOR",
442
+ "LEI",
443
+ "NTR",
444
+ "VAT",
445
+ "TIN",
446
+ "EXC"
447
+ ];
448
+ /**
449
+ * Valid semantic identifier prefixes for natural persons (ETSI EN 319 412-1 clause 5.1.3)
450
+ */
451
+ const NATURAL_PERSON_PREFIXES = [
452
+ "TIN",
453
+ "PAS",
454
+ "IDC",
455
+ "PNO",
456
+ "TAX"
457
+ ];
458
+ /**
459
+ * Validate a semantic identifier format
460
+ */
461
+ function validateSemanticIdentifier(identifier) {
462
+ const match = identifier.match(/^([A-Z]{3})([A-Z]{2})-(.+)$/);
463
+ if (!match) return {
464
+ valid: false,
465
+ message: "Semantic identifier must follow format: PREFIX (3 chars) + COUNTRY (2 chars) + \"-\" + ID"
466
+ };
467
+ const [, prefix, , id] = match;
468
+ if (![...LEGAL_PERSON_PREFIXES, ...NATURAL_PERSON_PREFIXES].includes(prefix)) {}
469
+ if (!id || id.length === 0) return {
470
+ valid: false,
471
+ message: "Semantic identifier must have a non-empty ID part after the hyphen"
472
+ };
473
+ return {
474
+ valid: true,
475
+ message: ""
476
+ };
477
+ }
478
+ /**
479
+ * Assert that a WRPRC payload is valid, throws WRPRCException if not
480
+ */
481
+ function assertValidWRPRCPayload(payload) {
482
+ const result = validateWRPRCPayload(payload);
483
+ if (!result.valid) throw new WRPRCException(`Invalid WRPRC payload:\n${result.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n")}`, result.errors);
484
+ }
485
+ /**
486
+ * Assert that a WRPRC is valid (header + payload), throws WRPRCException if not
487
+ */
488
+ function assertValidWRPRC(header, payload) {
489
+ const result = validateWRPRC(header, payload);
490
+ if (!result.valid) throw new WRPRCException(`Invalid WRPRC:\n${result.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("\n")}`, result.errors);
491
+ }
492
+ //#endregion
493
+ //#region src/builders.ts
494
+ /**
495
+ * WRPRC Builders
496
+ *
497
+ * Fluent builders for creating ETSI TS 119 475 Wallet-Relying Party Registration Certificates.
498
+ *
499
+ * @see https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf
500
+ */
501
+ /**
502
+ * Builder for creating WRPRC payloads with a fluent API
503
+ */
504
+ var WRPRCBuilder = class {
505
+ constructor() {
506
+ this.payload = {};
507
+ }
508
+ /**
509
+ * Set the trade name (display name) of the WRP
510
+ */
511
+ name(value) {
512
+ this.payload.name = value;
513
+ return this;
514
+ }
515
+ /**
516
+ * Set the legal name for a legal person WRP
517
+ */
518
+ legalName(value) {
519
+ this.payload.sub_ln = value;
520
+ return this;
521
+ }
522
+ /**
523
+ * Set the given name for a natural person WRP
524
+ */
525
+ givenName(value) {
526
+ this.payload.sub_gn = value;
527
+ return this;
528
+ }
529
+ /**
530
+ * Set the family name for a natural person WRP
531
+ */
532
+ familyName(value) {
533
+ this.payload.sub_fn = value;
534
+ return this;
535
+ }
536
+ /**
537
+ * Set the WRP identifier (semantic identifier)
538
+ *
539
+ * @param identifier - The semantic identifier following ETSI EN 319 412-1
540
+ * Format: PREFIX (3 chars) + COUNTRY (2 chars) + "-" + ID
541
+ * Examples: "LEIXG-529900T8BM49AURSDO55", "TINIT-RSSMRA85T10A562S"
542
+ */
543
+ identifier(identifier) {
544
+ this.payload.sub = identifier;
545
+ return this;
546
+ }
547
+ /**
548
+ * Set the country code (ISO 3166-1 Alpha-2)
549
+ */
550
+ country(code) {
551
+ this.payload.country = code;
552
+ return this;
553
+ }
554
+ /**
555
+ * Set the URL to the national registry API endpoint
556
+ */
557
+ registryUri(uri) {
558
+ this.payload.registry_uri = uri;
559
+ return this;
560
+ }
561
+ /**
562
+ * Add a service description in a specific language
563
+ */
564
+ serviceDescription(description, lang = "en") {
565
+ this.payload.srv_description = this.payload.srv_description ?? [];
566
+ const existing = this.payload.srv_description.find((group) => group.some((d) => d.lang === lang));
567
+ if (existing) existing.push({
568
+ lang,
569
+ value: description
570
+ });
571
+ else this.payload.srv_description.push([{
572
+ lang,
573
+ value: description
574
+ }]);
575
+ return this;
576
+ }
577
+ /**
578
+ * Add multiple service descriptions (one array per service, with multiple languages)
579
+ */
580
+ addServiceDescriptions(descriptions) {
581
+ this.payload.srv_description = this.payload.srv_description ?? [];
582
+ this.payload.srv_description.push(descriptions);
583
+ return this;
584
+ }
585
+ /**
586
+ * Add an entitlement
587
+ *
588
+ * @param entitlement - The entitlement URI or key from WRP_ENTITLEMENTS
589
+ */
590
+ addEntitlement(entitlement) {
591
+ this.payload.entitlements = this.payload.entitlements ?? [];
592
+ if (!this.payload.entitlements.includes(entitlement)) this.payload.entitlements.push(entitlement);
593
+ return this;
594
+ }
595
+ /**
596
+ * Set all entitlements (replaces existing)
597
+ */
598
+ entitlements(entitlements) {
599
+ this.payload.entitlements = [...entitlements];
600
+ return this;
601
+ }
602
+ /**
603
+ * Set the privacy policy URL
604
+ */
605
+ privacyPolicy(uri) {
606
+ this.payload.privacy_policy = uri;
607
+ return this;
608
+ }
609
+ /**
610
+ * Set the info URI (general-purpose web address)
611
+ */
612
+ infoUri(uri) {
613
+ this.payload.info_uri = uri;
614
+ return this;
615
+ }
616
+ /**
617
+ * Set the support URI for data requests
618
+ */
619
+ supportUri(uri) {
620
+ this.payload.support_uri = uri;
621
+ return this;
622
+ }
623
+ /**
624
+ * Set the supervisory authority (Data Protection Authority)
625
+ */
626
+ supervisoryAuthority(authority) {
627
+ this.payload.supervisory_authority = authority;
628
+ return this;
629
+ }
630
+ /**
631
+ * Set the policy ID(s)
632
+ */
633
+ policyId(ids) {
634
+ this.payload.policy_id = ids;
635
+ return this;
636
+ }
637
+ /**
638
+ * Set the certificate policy URL
639
+ */
640
+ certificatePolicy(uri) {
641
+ this.payload.certificate_policy = uri;
642
+ return this;
643
+ }
644
+ /**
645
+ * Set the issued-at timestamp (Unix timestamp)
646
+ * If not set, will default to current time when building
647
+ */
648
+ issuedAt(timestamp) {
649
+ this.payload.iat = typeof timestamp === "number" ? timestamp : Math.floor(timestamp.getTime() / 1e3);
650
+ return this;
651
+ }
652
+ /**
653
+ * Set the status reference for certificate validity
654
+ */
655
+ status(status) {
656
+ this.payload.status = status;
657
+ return this;
658
+ }
659
+ /**
660
+ * Add a purpose description
661
+ */
662
+ addPurpose(description, lang = "en") {
663
+ this.payload.purpose = this.payload.purpose ?? [];
664
+ this.payload.purpose.push({
665
+ lang,
666
+ value: description
667
+ });
668
+ return this;
669
+ }
670
+ /**
671
+ * Set all purposes (replaces existing)
672
+ */
673
+ purposes(purposes) {
674
+ this.payload.purpose = purposes;
675
+ return this;
676
+ }
677
+ /**
678
+ * Add a credential that the WRP intends to request
679
+ */
680
+ addCredential(credential) {
681
+ this.payload.credentials = this.payload.credentials ?? [];
682
+ this.payload.credentials.push(credential);
683
+ return this;
684
+ }
685
+ /**
686
+ * Add a credential the WRP provides (for attestation providers)
687
+ */
688
+ addProvidedAttestation(credential) {
689
+ this.payload.provides_attestations = this.payload.provides_attestations ?? [];
690
+ this.payload.provides_attestations.push(credential);
691
+ return this;
692
+ }
693
+ /**
694
+ * Set intermediary information
695
+ */
696
+ intermediary(intermediary) {
697
+ this.payload.intermediary = intermediary;
698
+ return this;
699
+ }
700
+ /**
701
+ * Build the WRPRC payload
702
+ *
703
+ * @throws WRPRCException if the payload is invalid
704
+ */
705
+ build() {
706
+ if (!this.payload.iat) this.payload.iat = Math.floor(Date.now() / 1e3);
707
+ const result = WRPRCPayloadSchema.safeParse(this.payload);
708
+ if (!result.success) throw new WRPRCException(`Invalid WRPRC payload:\n${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("\n")}`, result.error.issues);
709
+ return result.data;
710
+ }
711
+ };
712
+ /**
713
+ * Builder for creating Credential objects
714
+ */
715
+ var CredentialBuilder = class {
716
+ constructor() {
717
+ this.credential = {};
718
+ }
719
+ /**
720
+ * Set the credential format
721
+ *
722
+ * @param format - The format identifier (e.g., "dc+sd-jwt", "mso_mdoc")
723
+ */
724
+ format(format) {
725
+ this.credential.format = format;
726
+ return this;
727
+ }
728
+ /**
729
+ * Set the credential metadata
730
+ *
731
+ * @param meta - Metadata object per credential format specification
732
+ */
733
+ meta(meta) {
734
+ this.credential.meta = meta;
735
+ return this;
736
+ }
737
+ /**
738
+ * Set SD-JWT credential metadata (vct_values)
739
+ */
740
+ sdJwtMeta(vctValues) {
741
+ this.credential.format = "dc+sd-jwt";
742
+ this.credential.meta = { vct_values: vctValues };
743
+ return this;
744
+ }
745
+ /**
746
+ * Set mDL/mDoc credential metadata (doctype_value)
747
+ */
748
+ mdocMeta(doctypeValue) {
749
+ this.credential.format = "mso_mdoc";
750
+ this.credential.meta = { doctype_value: doctypeValue };
751
+ return this;
752
+ }
753
+ /**
754
+ * Add a claim to request
755
+ */
756
+ addClaim(claim) {
757
+ this.credential.claim = this.credential.claim ?? [];
758
+ this.credential.claim.push(claim);
759
+ return this;
760
+ }
761
+ /**
762
+ * Add a simple path claim
763
+ */
764
+ addPathClaim(...path) {
765
+ this.credential.claim = this.credential.claim ?? [];
766
+ this.credential.claim.push({ path });
767
+ return this;
768
+ }
769
+ /**
770
+ * Build the Credential object
771
+ */
772
+ build() {
773
+ if (!this.credential.format) throw new WRPRCException("Credential format is required");
774
+ if (!this.credential.meta) throw new WRPRCException("Credential meta is required");
775
+ return this.credential;
776
+ }
777
+ };
778
+ /**
779
+ * Create a new WRPRCBuilder
780
+ */
781
+ function wrprc() {
782
+ return new WRPRCBuilder();
783
+ }
784
+ /**
785
+ * Create a new CredentialBuilder
786
+ */
787
+ function credential() {
788
+ return new CredentialBuilder();
789
+ }
790
+ /**
791
+ * Create a WRPRC payload for a legal person
792
+ */
793
+ function createLegalPersonWRPRC(input) {
794
+ return wrprc().name(input.name).legalName(input.legalName).identifier(input.identifier).country(input.country).registryUri(input.registryUri).entitlements(input.entitlements).build();
795
+ }
796
+ /**
797
+ * Create a WRPRC payload for a natural person
798
+ */
799
+ function createNaturalPersonWRPRC(input) {
800
+ return wrprc().name(input.name).givenName(input.givenName).familyName(input.familyName).identifier(input.identifier).country(input.country).registryUri(input.registryUri).entitlements(input.entitlements).build();
801
+ }
802
+ /**
803
+ * Create a simple service provider WRPRC
804
+ */
805
+ function createServiceProviderWRPRC(name, legalName, identifier, country, registryUri) {
806
+ return wrprc().name(name).legalName(legalName).identifier(identifier).country(country).registryUri(registryUri).addEntitlement(ENTITLEMENT_SERVICE_PROVIDER).build();
807
+ }
808
+ //#endregion
809
+ //#region src/signer.ts
810
+ /**
811
+ * WRPRC Signer
812
+ *
813
+ * Functions for creating and signing ETSI TS 119 475 Wallet-Relying Party Registration Certificates.
814
+ *
815
+ * @see https://www.etsi.org/deliver/etsi_ts/119400_119499/119475/01.02.01_60/ts_119475v010201p.pdf
816
+ */
817
+ /**
818
+ * Sign a WRPRC payload to create a JWT
819
+ *
820
+ * @param options - Signing options including payload, algorithm, certificates, and signer
821
+ * @returns Signed WRPRC with JWS string and decoded parts
822
+ */
823
+ async function signWRPRC(options) {
824
+ const { payload, algorithm = "ES256", certificates, keyId, signer } = options;
825
+ assertValidWRPRCPayload(payload);
826
+ if (!certificates || certificates.length === 0) throw new WRPRCException("At least one certificate is required for x5c header");
827
+ const header = {
828
+ typ: "rc-wrp+jwt",
829
+ alg: algorithm,
830
+ x5c: certificates.map((cert) => {
831
+ const content = pemToDer(cert);
832
+ if (!content) throw new WRPRCException("Invalid PEM certificate format");
833
+ return content;
834
+ }),
835
+ ...keyId && { kid: keyId }
836
+ };
837
+ const signingInput = `${base64urlEncode(JSON.stringify(header))}.${base64urlEncode(JSON.stringify(payload))}`;
838
+ return {
839
+ jws: `${signingInput}.${await signer(signingInput)}`,
840
+ header,
841
+ payload
842
+ };
843
+ }
844
+ /**
845
+ * Decode a signed WRPRC JWT (without verification)
846
+ *
847
+ * @param jws - The compact JWS string
848
+ * @returns Decoded WRPRC with header and payload
849
+ */
850
+ function decodeWRPRC(jws) {
851
+ const decoded = decodeJwt(jws);
852
+ if (decoded.header.typ !== "rc-wrp+jwt") throw new WRPRCException(`Invalid WRPRC type: expected "rc-wrp+jwt", got "${decoded.header.typ}"`);
853
+ assertValidWRPRCPayload(decoded.payload);
854
+ return {
855
+ jws,
856
+ header: decoded.header,
857
+ payload: decoded.payload
858
+ };
859
+ }
860
+ /**
861
+ * Parse a WRPRC JWT without validation (for inspection purposes)
862
+ *
863
+ * @param jws - The compact JWS string
864
+ * @returns Decoded parts without validation
865
+ */
866
+ function parseWRPRC(jws) {
867
+ const decoded = decodeJwt(jws);
868
+ const parts = jws.split(".");
869
+ return {
870
+ header: decoded.header,
871
+ payload: decoded.payload,
872
+ signature: parts[2]
873
+ };
874
+ }
875
+ /**
876
+ * Create a WRPRC with automatic timestamp
877
+ *
878
+ * @param payload - Partial payload (iat will be set automatically if not provided)
879
+ * @returns Complete payload with timestamp
880
+ */
881
+ function createWRPRCPayload(payload) {
882
+ const completePayload = {
883
+ ...payload,
884
+ iat: payload.iat ?? Math.floor(Date.now() / 1e3)
885
+ };
886
+ assertValidWRPRCPayload(completePayload);
887
+ return completePayload;
888
+ }
889
+ //#endregion
890
+ export { ClaimSchema, CredentialBuilder, CredentialSchema, IDENTIFIER_TYPES, IntermediarySchema, LegalPersonSubjectSchema, MultiLangStringSchema, NaturalPersonSubjectSchema, PSP_SUB_ENTITLEMENTS, StatusSchema, SupervisoryAuthoritySchema, WRPRCBuilder, WRPRCCWTHeaderSchema, WRPRCException, WRPRCJWTHeaderSchema, WRPRCPayloadSchema, WRP_ENTITLEMENTS, assertValidWRPRC, assertValidWRPRCPayload, createLegalPersonWRPRC, createNaturalPersonWRPRC, createServiceProviderWRPRC, createWRPRCPayload, credential, decodeWRPRC, hasAttestationProviderEntitlement, isLegalPersonWRPRC, isNaturalPersonWRPRC, isPSPSubEntitlement, isValidEntitlement, parseWRPRC, signWRPRC, validateLegalPersonWRPRC, validateNaturalPersonWRPRC, validateWRPRC, validateWRPRCJWTHeader, validateWRPRCPayload, wrprc };
891
+
892
+ //# sourceMappingURL=index.mjs.map