@peac/mappings-ucp 0.9.31

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/util.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @peac/mappings-ucp - Shared utilities
3
+ *
4
+ * Crypto and encoding helpers used by both verification and evidence generation.
5
+ * Extracted to prevent architectural coupling between modules.
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { ErrorCodes, ucpError } from './errors.js';
9
+ /**
10
+ * SHA-256 hash as hex string.
11
+ */
12
+ export function sha256Hex(data) {
13
+ return createHash('sha256').update(data).digest('hex');
14
+ }
15
+ /**
16
+ * SHA-256 hash as Uint8Array.
17
+ */
18
+ export function sha256Bytes(data) {
19
+ return new Uint8Array(createHash('sha256').update(data).digest());
20
+ }
21
+ /**
22
+ * Convert base64 to base64url by replacing chars.
23
+ */
24
+ function base64ToBase64url(base64) {
25
+ let result = '';
26
+ for (let i = 0; i < base64.length; i++) {
27
+ const c = base64.charCodeAt(i);
28
+ if (c === 43) {
29
+ // '+' -> '-'
30
+ result += '-';
31
+ }
32
+ else if (c === 47) {
33
+ // '/' -> '_'
34
+ result += '_';
35
+ }
36
+ else if (c === 61) {
37
+ // '=' padding - skip
38
+ break;
39
+ }
40
+ else {
41
+ result += base64.charAt(i);
42
+ }
43
+ }
44
+ return result;
45
+ }
46
+ // Maximum size for base64url encoding (16MB - reasonable limit for webhook payloads)
47
+ const MAX_ENCODE_SIZE = 16 * 1024 * 1024;
48
+ /**
49
+ * Base64url encode without padding.
50
+ * Uses standard base64 + manual conversion for maximum portability.
51
+ * Works with Node.js 12+ (does not require 'base64url' encoding from Node 15.7.0+).
52
+ */
53
+ export function base64urlEncode(data) {
54
+ // Safety check for unreasonably large input
55
+ if (data.length > MAX_ENCODE_SIZE) {
56
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `Input too large for base64url encoding: ${data.length} bytes`);
57
+ }
58
+ // Use Buffer in Node.js, then convert base64 to base64url
59
+ if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
60
+ const base64 = Buffer.from(data).toString('base64');
61
+ return base64ToBase64url(base64);
62
+ }
63
+ // Fallback for non-Node environments (browsers)
64
+ // Build binary string with bounded iteration
65
+ const len = data.length;
66
+ let binary = '';
67
+ for (let i = 0; i < len; i++) {
68
+ binary += String.fromCharCode(data[i]);
69
+ }
70
+ const base64 = btoa(binary);
71
+ return base64ToBase64url(base64);
72
+ }
73
+ /**
74
+ * JCS canonicalize (RFC 8785) - synchronous.
75
+ * Produces deterministic JSON with sorted keys and no whitespace.
76
+ */
77
+ export function jcsCanonicalizeSync(obj) {
78
+ if (obj === null)
79
+ return 'null';
80
+ if (typeof obj === 'boolean')
81
+ return obj ? 'true' : 'false';
82
+ if (typeof obj === 'number') {
83
+ if (!Number.isFinite(obj)) {
84
+ throw ucpError(ErrorCodes.EVIDENCE_SERIALIZATION_FAILED, 'JCS does not support NaN or Infinity');
85
+ }
86
+ // Use ES6 number serialization (matches JSON.stringify for finite numbers)
87
+ return Object.is(obj, -0) ? '0' : String(obj);
88
+ }
89
+ if (typeof obj === 'string')
90
+ return JSON.stringify(obj);
91
+ if (Array.isArray(obj)) {
92
+ return '[' + obj.map(jcsCanonicalizeSync).join(',') + ']';
93
+ }
94
+ if (typeof obj === 'object') {
95
+ const keys = Object.keys(obj).sort();
96
+ const pairs = keys
97
+ .filter((k) => obj[k] !== undefined)
98
+ .map((k) => JSON.stringify(k) + ':' + jcsCanonicalizeSync(obj[k]));
99
+ return '{' + pairs.join(',') + '}';
100
+ }
101
+ throw ucpError(ErrorCodes.EVIDENCE_SERIALIZATION_FAILED, `Cannot canonicalize type: ${typeof obj}`);
102
+ }
103
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @peac/mappings-ucp - Webhook signature verification
3
+ *
4
+ * Verifies UCP webhook signatures using detached JWS (RFC 7797).
5
+ * Supports ES256, ES384, ES512 algorithms with ECDSA keys.
6
+ *
7
+ * Verification strategy (raw-first, JCS fallback):
8
+ * 1. Try verifying against raw request body bytes
9
+ * 2. If that fails AND body is valid JSON, try JCS-canonicalized body
10
+ * 3. Record all attempts for evidence
11
+ *
12
+ * The UCP spec is ambiguous on canonicalization for webhooks (unlike AP2 which
13
+ * explicitly requires JCS). This implementation tries raw bytes first because
14
+ * the spec says "detached JWT over the request body".
15
+ */
16
+ import type { UcpProfile, UcpSigningKey, ParsedDetachedJws, VerifyUcpWebhookOptions, VerifyUcpWebhookResult } from './types.js';
17
+ /**
18
+ * Parse a detached JWS from the Request-Signature header.
19
+ * Format: <protected>..<signature> (empty payload section)
20
+ */
21
+ export declare function parseDetachedJws(headerValue: string): ParsedDetachedJws;
22
+ /**
23
+ * Find the signing key by kid in the profile.
24
+ */
25
+ export declare function findSigningKey(profile: UcpProfile, kid: string): UcpSigningKey | undefined;
26
+ /**
27
+ * Verify a UCP webhook signature.
28
+ *
29
+ * Strategy: raw-first, JCS fallback
30
+ * 1. Parse the detached JWS from Request-Signature header
31
+ * 2. Find the signing key in the profile by kid
32
+ * 3. Try verifying against raw body bytes
33
+ * 4. If that fails and body is valid JSON, try JCS-canonicalized body
34
+ * 5. Return result with all verification attempts
35
+ *
36
+ * @param options - Verification options
37
+ * @returns Verification result with profile and key information for evidence capture
38
+ */
39
+ export declare function verifyUcpWebhookSignature(options: VerifyUcpWebhookOptions): Promise<VerifyUcpWebhookResult>;
40
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EAEb,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EAGvB,MAAM,YAAY,CAAC;AAmBpB;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,CAiIvE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE1F;AA4ED;;;;;;;;;;;;GAYG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,sBAAsB,CAAC,CAkKjC"}
package/dist/verify.js ADDED
@@ -0,0 +1,406 @@
1
+ /**
2
+ * @peac/mappings-ucp - Webhook signature verification
3
+ *
4
+ * Verifies UCP webhook signatures using detached JWS (RFC 7797).
5
+ * Supports ES256, ES384, ES512 algorithms with ECDSA keys.
6
+ *
7
+ * Verification strategy (raw-first, JCS fallback):
8
+ * 1. Try verifying against raw request body bytes
9
+ * 2. If that fails AND body is valid JSON, try JCS-canonicalized body
10
+ * 3. Record all attempts for evidence
11
+ *
12
+ * The UCP spec is ambiguous on canonicalization for webhooks (unlike AP2 which
13
+ * explicitly requires JCS). This implementation tries raw bytes first because
14
+ * the spec says "detached JWT over the request body".
15
+ */
16
+ import * as jose from 'jose';
17
+ import { ErrorCodes, ucpError, UcpError } from './errors.js';
18
+ import { jcsCanonicalizeSync, base64urlEncode } from './util.js';
19
+ /**
20
+ * Supported ECDSA algorithms and their curve mappings.
21
+ */
22
+ const ALG_TO_CURVE = {
23
+ ES256: 'P-256',
24
+ ES384: 'P-384',
25
+ ES512: 'P-521',
26
+ };
27
+ /**
28
+ * Known/understood critical header parameters.
29
+ * Per JOSE rules: if `crit` exists, every entry MUST be understood by the implementation.
30
+ */
31
+ const UNDERSTOOD_CRIT_PARAMS = new Set(['b64']);
32
+ /**
33
+ * Parse a detached JWS from the Request-Signature header.
34
+ * Format: <protected>..<signature> (empty payload section)
35
+ */
36
+ export function parseDetachedJws(headerValue) {
37
+ const trimmed = headerValue.trim();
38
+ // Detached JWS format: header..signature (double dot, empty payload)
39
+ const parts = trimmed.split('.');
40
+ if (parts.length !== 3) {
41
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Invalid detached JWS format, expected 3 parts', {
42
+ parts: parts.length,
43
+ });
44
+ }
45
+ const [protectedB64url, payloadPart, signatureB64url] = parts;
46
+ // Payload MUST be empty for detached JWS
47
+ if (payloadPart !== '') {
48
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Detached JWS must have empty payload section', {
49
+ payloadLength: payloadPart.length,
50
+ });
51
+ }
52
+ // Decode protected header
53
+ let header;
54
+ try {
55
+ const headerJson = base64urlDecode(protectedB64url);
56
+ header = JSON.parse(new TextDecoder('utf-8').decode(headerJson));
57
+ }
58
+ catch (err) {
59
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Failed to decode protected header', {
60
+ error: String(err),
61
+ });
62
+ }
63
+ // Validate algorithm
64
+ if (!header.alg || !['ES256', 'ES384', 'ES512'].includes(header.alg)) {
65
+ throw ucpError(ErrorCodes.SIGNATURE_ALGORITHM_UNSUPPORTED, `Unsupported algorithm: ${header.alg}`, {
66
+ alg: header.alg,
67
+ supported: ['ES256', 'ES384', 'ES512'],
68
+ });
69
+ }
70
+ // Validate kid
71
+ if (!header.kid) {
72
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'Missing kid in protected header');
73
+ }
74
+ // Validate b64 parameter type if present (must be boolean)
75
+ if ('b64' in header && typeof header.b64 !== 'boolean') {
76
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `b64 header must be a boolean, got ${typeof header.b64}`, { b64_type: typeof header.b64 });
77
+ }
78
+ // Validate crit parameter if present
79
+ if ('crit' in header) {
80
+ // crit must be an array
81
+ if (!Array.isArray(header.crit)) {
82
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `crit header must be an array, got ${typeof header.crit}`, { crit_type: typeof header.crit });
83
+ }
84
+ // crit must contain only strings
85
+ const nonStrings = header.crit.filter((param) => typeof param !== 'string');
86
+ if (nonStrings.length > 0) {
87
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, 'crit array must contain only strings', {
88
+ invalid_types: nonStrings.map((p) => typeof p),
89
+ });
90
+ }
91
+ // crit must not contain duplicates
92
+ const seen = new Set();
93
+ const duplicates = [];
94
+ for (const param of header.crit) {
95
+ if (seen.has(param)) {
96
+ duplicates.push(param);
97
+ }
98
+ seen.add(param);
99
+ }
100
+ if (duplicates.length > 0) {
101
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `crit array contains duplicates: ${duplicates.join(', ')}`, { duplicates });
102
+ }
103
+ // Enforce JOSE crit semantics: reject unknown critical parameters
104
+ const unknownCrit = header.crit.filter((param) => !UNDERSTOOD_CRIT_PARAMS.has(param));
105
+ if (unknownCrit.length > 0) {
106
+ throw ucpError(ErrorCodes.SIGNATURE_MALFORMED, `Unknown critical header parameters: ${unknownCrit.join(', ')}`, { unknown_crit: unknownCrit, understood: [...UNDERSTOOD_CRIT_PARAMS] });
107
+ }
108
+ }
109
+ // Handle b64 parameter
110
+ const isUnencodedPayload = header.b64 === false;
111
+ // If b64=false is used, it MUST be in crit (RFC 7797 requirement)
112
+ if (isUnencodedPayload) {
113
+ if (!header.crit || !header.crit.includes('b64')) {
114
+ throw ucpError(ErrorCodes.SIGNATURE_B64_INVALID, 'When b64=false, "b64" must be in the "crit" array');
115
+ }
116
+ }
117
+ return {
118
+ raw_header_value: trimmed,
119
+ header: header,
120
+ protected_b64url: protectedB64url,
121
+ signature_b64url: signatureB64url,
122
+ is_unencoded_payload: isUnencodedPayload,
123
+ };
124
+ }
125
+ /**
126
+ * Find the signing key by kid in the profile.
127
+ */
128
+ export function findSigningKey(profile, kid) {
129
+ return profile.signing_keys.find((k) => k.kid === kid);
130
+ }
131
+ /**
132
+ * Verify that a key's curve matches the algorithm.
133
+ */
134
+ function validateKeyForAlgorithm(key, alg) {
135
+ const expectedCurve = ALG_TO_CURVE[alg];
136
+ if (key.kty !== 'EC') {
137
+ throw ucpError(ErrorCodes.KEY_ALGORITHM_MISMATCH, `Expected EC key, got ${key.kty}`, {
138
+ kid: key.kid,
139
+ kty: key.kty,
140
+ });
141
+ }
142
+ if (key.crv !== expectedCurve) {
143
+ throw ucpError(ErrorCodes.KEY_CURVE_MISMATCH, `Algorithm ${alg} requires curve ${expectedCurve}, got ${key.crv}`, { kid: key.kid, alg, expected: expectedCurve, got: key.crv });
144
+ }
145
+ }
146
+ /**
147
+ * Verify a detached JWS signature against payload bytes.
148
+ *
149
+ * For b64=false (RFC 7797 unencoded payload):
150
+ * - jose.flattenedVerify accepts Uint8Array directly as payload
151
+ * - The signing input is: ASCII(BASE64URL(header)) || '.' || payload_bytes
152
+ *
153
+ * For standard JWS (b64=true or absent):
154
+ * - Payload is base64url-encoded in the signing input
155
+ *
156
+ * @param parsed - Parsed detached JWS
157
+ * @param payloadBytes - The payload bytes to verify against
158
+ * @param key - The EC public key (JWK)
159
+ * @returns true if signature is valid
160
+ */
161
+ async function verifyDetachedSignature(parsed, payloadBytes, key) {
162
+ // Import the public key
163
+ const publicKey = await jose.importJWK({
164
+ kty: key.kty,
165
+ crv: key.crv,
166
+ x: key.x,
167
+ y: key.y,
168
+ }, parsed.header.alg);
169
+ // Construct the JWS input for verification
170
+ // jose.flattenedVerify handles b64=false correctly when payload is Uint8Array
171
+ const jws = {
172
+ protected: parsed.protected_b64url,
173
+ // For b64=false: pass raw bytes directly (jose handles RFC 7797 semantics)
174
+ // For standard JWS: pass base64url-encoded string
175
+ payload: parsed.is_unencoded_payload ? payloadBytes : base64urlEncode(payloadBytes),
176
+ signature: parsed.signature_b64url,
177
+ };
178
+ try {
179
+ // jose.flattenedVerify handles b64=false correctly when it sees it in the protected header
180
+ await jose.flattenedVerify(jws, publicKey, {
181
+ algorithms: [parsed.header.alg],
182
+ });
183
+ return true;
184
+ }
185
+ catch {
186
+ return false;
187
+ }
188
+ }
189
+ /**
190
+ * Verify a UCP webhook signature.
191
+ *
192
+ * Strategy: raw-first, JCS fallback
193
+ * 1. Parse the detached JWS from Request-Signature header
194
+ * 2. Find the signing key in the profile by kid
195
+ * 3. Try verifying against raw body bytes
196
+ * 4. If that fails and body is valid JSON, try JCS-canonicalized body
197
+ * 5. Return result with all verification attempts
198
+ *
199
+ * @param options - Verification options
200
+ * @returns Verification result with profile and key information for evidence capture
201
+ */
202
+ export async function verifyUcpWebhookSignature(options) {
203
+ const { signature_header, body_bytes, profile_url, profile: providedProfile } = options;
204
+ // Track verification attempts
205
+ const attempts = [];
206
+ // Parse the detached JWS
207
+ let parsed;
208
+ try {
209
+ parsed = parseDetachedJws(signature_header);
210
+ }
211
+ catch (err) {
212
+ if (err instanceof UcpError) {
213
+ return {
214
+ valid: false,
215
+ header: { alg: 'ES256', kid: '' },
216
+ attempts,
217
+ error_code: err.code,
218
+ error_message: err.message,
219
+ };
220
+ }
221
+ throw err;
222
+ }
223
+ // Get the profile (use provided or fetch)
224
+ let profile;
225
+ let profileRaw;
226
+ if (providedProfile) {
227
+ profile = providedProfile;
228
+ }
229
+ else {
230
+ try {
231
+ const fetchResult = await fetchUcpProfile(profile_url);
232
+ profile = fetchResult.profile;
233
+ profileRaw = fetchResult.raw;
234
+ }
235
+ catch (err) {
236
+ return {
237
+ valid: false,
238
+ header: parsed.header,
239
+ attempts,
240
+ error_code: ErrorCodes.PROFILE_FETCH_FAILED,
241
+ error_message: `Failed to fetch UCP profile: ${err}`,
242
+ };
243
+ }
244
+ }
245
+ // Find the signing key
246
+ const key = findSigningKey(profile, parsed.header.kid);
247
+ if (!key) {
248
+ return {
249
+ valid: false,
250
+ header: parsed.header,
251
+ profile,
252
+ profile_raw: profileRaw,
253
+ attempts,
254
+ error_code: ErrorCodes.KEY_NOT_FOUND,
255
+ error_message: `Key not found in profile: ${parsed.header.kid}`,
256
+ };
257
+ }
258
+ // Validate key matches algorithm
259
+ try {
260
+ validateKeyForAlgorithm(key, parsed.header.alg);
261
+ }
262
+ catch (err) {
263
+ if (err instanceof UcpError) {
264
+ return {
265
+ valid: false,
266
+ header: parsed.header,
267
+ key,
268
+ profile,
269
+ profile_raw: profileRaw,
270
+ attempts,
271
+ error_code: err.code,
272
+ error_message: err.message,
273
+ };
274
+ }
275
+ throw err;
276
+ }
277
+ // Attempt 1: Verify against raw body bytes
278
+ try {
279
+ const rawValid = await verifyDetachedSignature(parsed, body_bytes, key);
280
+ attempts.push({
281
+ mode: 'raw',
282
+ success: rawValid,
283
+ ...(!rawValid && {
284
+ error_code: ErrorCodes.SIGNATURE_INVALID,
285
+ error_message: 'Signature verification failed',
286
+ }),
287
+ });
288
+ if (rawValid) {
289
+ return {
290
+ valid: true,
291
+ mode_used: 'raw',
292
+ header: parsed.header,
293
+ key,
294
+ profile,
295
+ profile_raw: profileRaw,
296
+ attempts,
297
+ };
298
+ }
299
+ }
300
+ catch (err) {
301
+ attempts.push({
302
+ mode: 'raw',
303
+ success: false,
304
+ error_code: ErrorCodes.VERIFICATION_FAILED,
305
+ error_message: String(err),
306
+ });
307
+ }
308
+ // Attempt 2: Try JCS-canonicalized body (only if JSON parseable)
309
+ try {
310
+ const bodyText = new TextDecoder('utf-8').decode(body_bytes);
311
+ const parsed_body = JSON.parse(bodyText);
312
+ const canonicalized = jcsCanonicalizeSync(parsed_body);
313
+ const canonicalizedBytes = new TextEncoder().encode(canonicalized);
314
+ const jcsValid = await verifyDetachedSignature(parsed, canonicalizedBytes, key);
315
+ attempts.push({
316
+ mode: 'jcs',
317
+ success: jcsValid,
318
+ ...(!jcsValid && {
319
+ error_code: ErrorCodes.SIGNATURE_INVALID,
320
+ error_message: 'JCS signature verification failed',
321
+ }),
322
+ });
323
+ if (jcsValid) {
324
+ return {
325
+ valid: true,
326
+ mode_used: 'jcs',
327
+ header: parsed.header,
328
+ key,
329
+ profile,
330
+ profile_raw: profileRaw,
331
+ attempts,
332
+ };
333
+ }
334
+ }
335
+ catch (err) {
336
+ // Body is not valid JSON or JCS canonicalization failed
337
+ // This is fine, we just don't try JCS verification
338
+ if (attempts.length === 1) {
339
+ // Only add JCS attempt if we haven't already
340
+ attempts.push({
341
+ mode: 'jcs',
342
+ success: false,
343
+ error_code: ErrorCodes.PAYLOAD_NOT_JSON,
344
+ error_message: 'Body is not valid JSON, cannot try JCS verification',
345
+ });
346
+ }
347
+ }
348
+ // Both attempts failed
349
+ return {
350
+ valid: false,
351
+ header: parsed.header,
352
+ key,
353
+ profile,
354
+ profile_raw: profileRaw,
355
+ attempts,
356
+ error_code: ErrorCodes.VERIFICATION_FAILED,
357
+ error_message: 'All verification attempts failed',
358
+ };
359
+ }
360
+ /**
361
+ * Fetch a UCP profile from a URL.
362
+ * Returns both the parsed profile and raw JSON for evidence capture.
363
+ */
364
+ async function fetchUcpProfile(url) {
365
+ const response = await fetch(url);
366
+ if (!response.ok) {
367
+ throw ucpError(ErrorCodes.PROFILE_FETCH_FAILED, `HTTP ${response.status} from ${url}`);
368
+ }
369
+ // Capture raw response text for evidence (deterministic)
370
+ const raw = await response.text();
371
+ let profile;
372
+ try {
373
+ profile = JSON.parse(raw);
374
+ }
375
+ catch {
376
+ throw ucpError(ErrorCodes.PROFILE_INVALID, 'Profile response is not valid JSON');
377
+ }
378
+ if (!profile.signing_keys || !Array.isArray(profile.signing_keys)) {
379
+ throw ucpError(ErrorCodes.PROFILE_NO_SIGNING_KEYS, 'Profile has no signing_keys array');
380
+ }
381
+ if (profile.signing_keys.length === 0) {
382
+ throw ucpError(ErrorCodes.PROFILE_NO_SIGNING_KEYS, 'Profile signing_keys array is empty');
383
+ }
384
+ return { profile, raw };
385
+ }
386
+ /**
387
+ * Base64url decode to Uint8Array.
388
+ * Uses Buffer for Node.js compatibility (avoids atob issues).
389
+ */
390
+ function base64urlDecode(s) {
391
+ // Use Buffer.from with 'base64url' encoding (Node.js 15.7.0+)
392
+ // Falls back to manual conversion for older environments
393
+ if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
394
+ return new Uint8Array(Buffer.from(s, 'base64url'));
395
+ }
396
+ // Fallback for non-Node environments
397
+ const padded = s + '==='.slice(0, (4 - (s.length % 4)) % 4);
398
+ const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
399
+ const binary = atob(base64);
400
+ const bytes = new Uint8Array(binary.length);
401
+ for (let i = 0; i < binary.length; i++) {
402
+ bytes[i] = binary.charCodeAt(i);
403
+ }
404
+ return bytes;
405
+ }
406
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@peac/mappings-ucp",
3
+ "version": "0.9.31",
4
+ "description": "Google Universal Commerce Protocol (UCP) mapping to PEAC receipts and dispute evidence",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "keywords": [
18
+ "ucp",
19
+ "google",
20
+ "commerce",
21
+ "webhooks",
22
+ "ap2",
23
+ "peac"
24
+ ],
25
+ "author": "PEAC Protocol Contributors",
26
+ "license": "Apache-2.0",
27
+ "bugs": {
28
+ "url": "https://github.com/peacprotocol/peac/issues"
29
+ },
30
+ "homepage": "https://peacprotocol.org",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/peacprotocol/peac.git",
34
+ "directory": "packages/mappings/ucp"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "jose": "^5.2.0",
41
+ "@peac/kernel": "0.9.31"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.3.0",
45
+ "vitest": "^2.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest"
51
+ }
52
+ }