@dupecom/botcha-cloudflare 0.15.0 → 0.18.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.
Files changed (65) hide show
  1. package/dist/dashboard/landing.d.ts.map +1 -1
  2. package/dist/dashboard/landing.js +2 -9
  3. package/dist/dashboard/layout.d.ts +12 -0
  4. package/dist/dashboard/layout.d.ts.map +1 -1
  5. package/dist/dashboard/layout.js +12 -5
  6. package/dist/dashboard/showcase.d.ts +1 -0
  7. package/dist/dashboard/showcase.d.ts.map +1 -1
  8. package/dist/dashboard/showcase.js +3 -2
  9. package/dist/dashboard/whitepaper.d.ts +14 -0
  10. package/dist/dashboard/whitepaper.d.ts.map +1 -0
  11. package/dist/dashboard/whitepaper.js +418 -0
  12. package/dist/email.d.ts.map +1 -1
  13. package/dist/email.js +5 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +148 -18
  17. package/dist/og-image.d.ts +2 -0
  18. package/dist/og-image.d.ts.map +1 -0
  19. package/dist/og-image.js +2 -0
  20. package/dist/static.d.ts +871 -2
  21. package/dist/static.d.ts.map +1 -1
  22. package/dist/static.js +812 -4
  23. package/dist/tap-agents.d.ts +3 -2
  24. package/dist/tap-agents.d.ts.map +1 -1
  25. package/dist/tap-agents.js +19 -6
  26. package/dist/tap-attestation-routes.d.ts +204 -0
  27. package/dist/tap-attestation-routes.d.ts.map +1 -0
  28. package/dist/tap-attestation-routes.js +396 -0
  29. package/dist/tap-attestation.d.ts +178 -0
  30. package/dist/tap-attestation.d.ts.map +1 -0
  31. package/dist/tap-attestation.js +416 -0
  32. package/dist/tap-consumer.d.ts +151 -0
  33. package/dist/tap-consumer.d.ts.map +1 -0
  34. package/dist/tap-consumer.js +346 -0
  35. package/dist/tap-delegation-routes.d.ts +236 -0
  36. package/dist/tap-delegation-routes.d.ts.map +1 -0
  37. package/dist/tap-delegation-routes.js +378 -0
  38. package/dist/tap-delegation.d.ts +127 -0
  39. package/dist/tap-delegation.d.ts.map +1 -0
  40. package/dist/tap-delegation.js +490 -0
  41. package/dist/tap-edge.d.ts +106 -0
  42. package/dist/tap-edge.d.ts.map +1 -0
  43. package/dist/tap-edge.js +487 -0
  44. package/dist/tap-federation.d.ts +89 -0
  45. package/dist/tap-federation.d.ts.map +1 -0
  46. package/dist/tap-federation.js +237 -0
  47. package/dist/tap-jwks.d.ts +64 -0
  48. package/dist/tap-jwks.d.ts.map +1 -0
  49. package/dist/tap-jwks.js +279 -0
  50. package/dist/tap-payment.d.ts +172 -0
  51. package/dist/tap-payment.d.ts.map +1 -0
  52. package/dist/tap-payment.js +425 -0
  53. package/dist/tap-reputation-routes.d.ts +154 -0
  54. package/dist/tap-reputation-routes.d.ts.map +1 -0
  55. package/dist/tap-reputation-routes.js +341 -0
  56. package/dist/tap-reputation.d.ts +136 -0
  57. package/dist/tap-reputation.d.ts.map +1 -0
  58. package/dist/tap-reputation.js +346 -0
  59. package/dist/tap-routes.d.ts +239 -2
  60. package/dist/tap-routes.d.ts.map +1 -1
  61. package/dist/tap-routes.js +279 -4
  62. package/dist/tap-verify.d.ts +43 -1
  63. package/dist/tap-verify.d.ts.map +1 -1
  64. package/dist/tap-verify.js +215 -30
  65. package/package.json +1 -1
@@ -0,0 +1,151 @@
1
+ /**
2
+ * TAP Consumer Recognition — Agentic Consumer Recognition Object
3
+ * Visa TAP Layer 2: Consumer identity verification linked to message signature
4
+ *
5
+ * Implements nonce-linked signature chains where:
6
+ * 1. HTTP message signature (Layer 1) contains a nonce
7
+ * 2. Consumer object contains the same nonce + consumer data
8
+ * 3. Consumer object is signed with same key
9
+ *
10
+ * This proves the agent had authorization from the consumer at signature time.
11
+ */
12
+ /**
13
+ * Agentic Consumer Recognition Object
14
+ * JSON structure in request body proving consumer authorization
15
+ */
16
+ export interface AgenticConsumer {
17
+ nonce: string;
18
+ idToken?: IDTokenJWT | string;
19
+ contextualData?: ContextualData;
20
+ kid: string;
21
+ alg: string;
22
+ signature: string;
23
+ }
24
+ /**
25
+ * Parsed ID Token (OIDC-compatible JWT)
26
+ */
27
+ export interface IDTokenJWT {
28
+ header: {
29
+ typ?: string;
30
+ alg: string;
31
+ kid?: string;
32
+ };
33
+ payload: IDTokenClaims;
34
+ signature: string;
35
+ }
36
+ /**
37
+ * ID Token claims (OIDC-compatible)
38
+ */
39
+ export interface IDTokenClaims {
40
+ iss: string;
41
+ sub: string;
42
+ aud: string | string[];
43
+ exp: number;
44
+ iat: number;
45
+ jti?: string;
46
+ auth_time?: number;
47
+ amr?: string[];
48
+ phone_number?: string;
49
+ phone_number_verified?: boolean;
50
+ email?: string;
51
+ email_verified?: boolean;
52
+ phone_number_mask?: string;
53
+ email_mask?: string;
54
+ [key: string]: any;
55
+ }
56
+ /**
57
+ * Contextual data about consumer device/location
58
+ */
59
+ export interface ContextualData {
60
+ countryCode?: string;
61
+ zip?: string;
62
+ ipAddress?: string;
63
+ deviceData?: DeviceData;
64
+ }
65
+ /**
66
+ * Device fingerprint data
67
+ */
68
+ export interface DeviceData {
69
+ userAgent?: string;
70
+ screenResolution?: string;
71
+ language?: string;
72
+ timezone?: string;
73
+ [key: string]: any;
74
+ }
75
+ /**
76
+ * Result of consumer verification
77
+ */
78
+ export interface ConsumerVerificationResult {
79
+ verified: boolean;
80
+ nonceLinked: boolean;
81
+ signatureValid: boolean;
82
+ idTokenValid?: boolean;
83
+ idTokenClaims?: IDTokenClaims;
84
+ contextualData?: ContextualData;
85
+ error?: string;
86
+ }
87
+ /**
88
+ * JWKS (JSON Web Key Set) structure
89
+ */
90
+ export interface JWKS {
91
+ keys: JWK[];
92
+ }
93
+ /**
94
+ * JSON Web Key
95
+ */
96
+ export interface JWK {
97
+ kid: string;
98
+ kty: string;
99
+ alg?: string;
100
+ use?: string;
101
+ n?: string;
102
+ e?: string;
103
+ crv?: string;
104
+ x?: string;
105
+ y?: string;
106
+ [key: string]: any;
107
+ }
108
+ /**
109
+ * Parse AgenticConsumer from request body
110
+ * Handles both nested { agenticConsumer: {...} } and top-level formats
111
+ */
112
+ export declare function parseAgenticConsumer(body: any): AgenticConsumer | null;
113
+ /**
114
+ * Parse ID Token JWT string into structured format
115
+ * Does NOT verify signature - just parses and validates structure
116
+ */
117
+ export declare function parseIDToken(tokenString: string): IDTokenClaims | null;
118
+ /**
119
+ * Verify ID Token signature by fetching JWKS from issuer
120
+ * Full cryptographic verification of the JWT
121
+ */
122
+ export declare function verifyIDTokenSignature(tokenString: string, jwksUrl?: string): Promise<{
123
+ valid: boolean;
124
+ claims?: IDTokenClaims;
125
+ error?: string;
126
+ }>;
127
+ /**
128
+ * Build canonical signature base string for consumer object
129
+ * Per TAP spec: all fields in order except 'signature' itself
130
+ */
131
+ export declare function buildConsumerSignatureBase(consumer: AgenticConsumer): string;
132
+ /**
133
+ * Verify AgenticConsumer object signature and nonce linkage
134
+ * Main verification function for TAP Layer 2
135
+ */
136
+ export declare function verifyAgenticConsumer(consumer: AgenticConsumer, headerNonce: string, publicKey: string, algorithm?: string): Promise<ConsumerVerificationResult>;
137
+ /**
138
+ * Hash-match obfuscated identity with cleartext
139
+ * Merchants maintain mapping tables with hashed values
140
+ */
141
+ export declare function hashMatchIdentity(obfuscated: string, cleartext: string, method?: 'sha256'): Promise<boolean>;
142
+ declare const _default: {
143
+ parseAgenticConsumer: typeof parseAgenticConsumer;
144
+ verifyAgenticConsumer: typeof verifyAgenticConsumer;
145
+ parseIDToken: typeof parseIDToken;
146
+ verifyIDTokenSignature: typeof verifyIDTokenSignature;
147
+ hashMatchIdentity: typeof hashMatchIdentity;
148
+ buildConsumerSignatureBase: typeof buildConsumerSignatureBase;
149
+ };
150
+ export default _default;
151
+ //# sourceMappingURL=tap-consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tap-consumer.d.ts","sourceRoot":"","sources":["../src/tap-consumer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,346 @@
1
+ /**
2
+ * TAP Consumer Recognition — Agentic Consumer Recognition Object
3
+ * Visa TAP Layer 2: Consumer identity verification linked to message signature
4
+ *
5
+ * Implements nonce-linked signature chains where:
6
+ * 1. HTTP message signature (Layer 1) contains a nonce
7
+ * 2. Consumer object contains the same nonce + consumer data
8
+ * 3. Consumer object is signed with same key
9
+ *
10
+ * This proves the agent had authorization from the consumer at signature time.
11
+ */
12
+ // ============ PARSING ============
13
+ /**
14
+ * Parse AgenticConsumer from request body
15
+ * Handles both nested { agenticConsumer: {...} } and top-level formats
16
+ */
17
+ export function parseAgenticConsumer(body) {
18
+ try {
19
+ // Try nested format first
20
+ let consumer = body?.agenticConsumer;
21
+ // If not nested, check if body itself is the consumer object
22
+ if (!consumer && body?.nonce && body?.kid && body?.alg && body?.signature) {
23
+ consumer = body;
24
+ }
25
+ if (!consumer) {
26
+ return null;
27
+ }
28
+ // Validate required fields
29
+ if (typeof consumer.nonce !== 'string' ||
30
+ typeof consumer.kid !== 'string' ||
31
+ typeof consumer.alg !== 'string' ||
32
+ typeof consumer.signature !== 'string') {
33
+ return null;
34
+ }
35
+ return {
36
+ nonce: consumer.nonce,
37
+ idToken: consumer.idToken,
38
+ contextualData: consumer.contextualData,
39
+ kid: consumer.kid,
40
+ alg: consumer.alg,
41
+ signature: consumer.signature
42
+ };
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ /**
49
+ * Parse ID Token JWT string into structured format
50
+ * Does NOT verify signature - just parses and validates structure
51
+ */
52
+ export function parseIDToken(tokenString) {
53
+ try {
54
+ const parts = tokenString.split('.');
55
+ if (parts.length !== 3) {
56
+ return null;
57
+ }
58
+ // Base64url decode header
59
+ const headerJson = atob(parts[0].replace(/-/g, '+').replace(/_/g, '/'));
60
+ const header = JSON.parse(headerJson);
61
+ // Base64url decode payload
62
+ const payloadJson = atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'));
63
+ const claims = JSON.parse(payloadJson);
64
+ // Validate required claims
65
+ if (typeof claims.iss !== 'string' ||
66
+ typeof claims.sub !== 'string' ||
67
+ !claims.aud ||
68
+ typeof claims.exp !== 'number' ||
69
+ typeof claims.iat !== 'number') {
70
+ return null;
71
+ }
72
+ // Check expiration
73
+ const now = Math.floor(Date.now() / 1000);
74
+ if (claims.exp <= now) {
75
+ return null; // Token expired
76
+ }
77
+ return claims;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ /**
84
+ * Verify ID Token signature by fetching JWKS from issuer
85
+ * Full cryptographic verification of the JWT
86
+ */
87
+ export async function verifyIDTokenSignature(tokenString, jwksUrl) {
88
+ try {
89
+ const parts = tokenString.split('.');
90
+ if (parts.length !== 3) {
91
+ return { valid: false, error: 'Invalid JWT format' };
92
+ }
93
+ // Parse header and payload
94
+ const headerJson = atob(parts[0].replace(/-/g, '+').replace(/_/g, '/'));
95
+ const header = JSON.parse(headerJson);
96
+ const payloadJson = atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'));
97
+ const claims = JSON.parse(payloadJson);
98
+ // If no JWKS URL provided, just parse without verification
99
+ if (!jwksUrl) {
100
+ // Check expiration at least
101
+ const now = Math.floor(Date.now() / 1000);
102
+ if (claims.exp && claims.exp <= now) {
103
+ return { valid: false, error: 'Token expired', claims };
104
+ }
105
+ return { valid: true, claims }; // Unverified but parsed
106
+ }
107
+ // Fetch JWKS
108
+ const jwksResponse = await fetch(jwksUrl);
109
+ if (!jwksResponse.ok) {
110
+ return { valid: false, error: 'Failed to fetch JWKS' };
111
+ }
112
+ const jwks = await jwksResponse.json();
113
+ // Find matching key by kid
114
+ const key = jwks.keys.find(k => k.kid === header.kid);
115
+ if (!key) {
116
+ return { valid: false, error: 'Key not found in JWKS' };
117
+ }
118
+ // Build signature base (header.payload)
119
+ const signatureBase = `${parts[0]}.${parts[1]}`;
120
+ const signatureBytes = base64UrlToArrayBuffer(parts[2]);
121
+ // Import public key from JWK
122
+ const cryptoKey = await jwkToCryptoKey(key, header.alg);
123
+ // Verify signature
124
+ const encoder = new TextEncoder();
125
+ const data = encoder.encode(signatureBase);
126
+ const { importParams, verifyParams } = jwkAlgToWebCrypto(header.alg);
127
+ const isValid = await crypto.subtle.verify(verifyParams, cryptoKey, signatureBytes, data);
128
+ if (!isValid) {
129
+ return { valid: false, error: 'Signature verification failed' };
130
+ }
131
+ // Check expiration
132
+ const now = Math.floor(Date.now() / 1000);
133
+ if (claims.exp && claims.exp <= now) {
134
+ return { valid: false, error: 'Token expired', claims };
135
+ }
136
+ return { valid: true, claims };
137
+ }
138
+ catch (error) {
139
+ return {
140
+ valid: false,
141
+ error: `Verification error: ${error instanceof Error ? error.message : 'Unknown'}`
142
+ };
143
+ }
144
+ }
145
+ // ============ SIGNATURE VERIFICATION ============
146
+ /**
147
+ * Build canonical signature base string for consumer object
148
+ * Per TAP spec: all fields in order except 'signature' itself
149
+ */
150
+ export function buildConsumerSignatureBase(consumer) {
151
+ const fields = [];
152
+ // Add fields IN ORDER, excluding 'signature'
153
+ if (consumer.nonce) {
154
+ fields.push(`"nonce": "${consumer.nonce}"`);
155
+ }
156
+ if (consumer.idToken) {
157
+ const idTokenStr = typeof consumer.idToken === 'string'
158
+ ? `"${consumer.idToken}"`
159
+ : JSON.stringify(consumer.idToken);
160
+ fields.push(`"idToken": ${idTokenStr}`);
161
+ }
162
+ if (consumer.contextualData) {
163
+ fields.push(`"contextualData": ${JSON.stringify(consumer.contextualData)}`);
164
+ }
165
+ if (consumer.kid) {
166
+ fields.push(`"kid": "${consumer.kid}"`);
167
+ }
168
+ if (consumer.alg) {
169
+ fields.push(`"alg": "${consumer.alg}"`);
170
+ }
171
+ return fields.join('\n');
172
+ }
173
+ /**
174
+ * Verify AgenticConsumer object signature and nonce linkage
175
+ * Main verification function for TAP Layer 2
176
+ */
177
+ export async function verifyAgenticConsumer(consumer, headerNonce, publicKey, algorithm) {
178
+ try {
179
+ // Step 1: Check nonce linkage
180
+ const nonceLinked = consumer.nonce === headerNonce;
181
+ // Step 2: Build signature base
182
+ const signatureBase = buildConsumerSignatureBase(consumer);
183
+ // Step 3: Verify signature
184
+ const alg = algorithm || consumer.alg;
185
+ const signatureValid = await verifyCryptoSignature(signatureBase, consumer.signature, publicKey, alg);
186
+ // Step 4: Parse and validate ID token if present
187
+ let idTokenValid;
188
+ let idTokenClaims;
189
+ if (consumer.idToken) {
190
+ const tokenString = typeof consumer.idToken === 'string'
191
+ ? consumer.idToken
192
+ : consumer.idToken.signature; // If already parsed
193
+ if (tokenString) {
194
+ const parsedClaims = parseIDToken(tokenString);
195
+ idTokenClaims = parsedClaims ?? undefined;
196
+ idTokenValid = parsedClaims !== null;
197
+ }
198
+ }
199
+ const verified = nonceLinked && signatureValid;
200
+ return {
201
+ verified,
202
+ nonceLinked,
203
+ signatureValid,
204
+ idTokenValid,
205
+ idTokenClaims,
206
+ contextualData: consumer.contextualData,
207
+ error: verified ? undefined : 'Verification failed'
208
+ };
209
+ }
210
+ catch (error) {
211
+ return {
212
+ verified: false,
213
+ nonceLinked: false,
214
+ signatureValid: false,
215
+ error: `Verification error: ${error instanceof Error ? error.message : 'Unknown'}`
216
+ };
217
+ }
218
+ }
219
+ /**
220
+ * Verify cryptographic signature using Web Crypto API
221
+ */
222
+ async function verifyCryptoSignature(signatureBase, signature, publicKeyPem, algorithm) {
223
+ try {
224
+ // Decode signature from base64
225
+ const signatureBytes = Uint8Array.from(atob(signature), c => c.charCodeAt(0));
226
+ // Import public key
227
+ const keyData = pemToArrayBuffer(publicKeyPem);
228
+ const { importParams, verifyParams } = jwkAlgToWebCrypto(algorithm);
229
+ const cryptoKey = await crypto.subtle.importKey('spki', keyData, importParams, false, ['verify']);
230
+ // Verify signature
231
+ const encoder = new TextEncoder();
232
+ const data = encoder.encode(signatureBase);
233
+ return await crypto.subtle.verify(verifyParams, cryptoKey, signatureBytes, data);
234
+ }
235
+ catch (error) {
236
+ console.error('Consumer signature verification error:', error);
237
+ return false;
238
+ }
239
+ }
240
+ // ============ IDENTITY MATCHING ============
241
+ /**
242
+ * Hash-match obfuscated identity with cleartext
243
+ * Merchants maintain mapping tables with hashed values
244
+ */
245
+ export async function hashMatchIdentity(obfuscated, cleartext, method = 'sha256') {
246
+ try {
247
+ const encoder = new TextEncoder();
248
+ const data = encoder.encode(cleartext);
249
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
250
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
251
+ const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
252
+ // Compare with obfuscated value (assume it's hex-encoded hash)
253
+ return hashHex === obfuscated.toLowerCase();
254
+ }
255
+ catch {
256
+ return false;
257
+ }
258
+ }
259
+ // ============ CRYPTO HELPERS ============
260
+ /**
261
+ * Convert PEM public key to ArrayBuffer
262
+ */
263
+ function pemToArrayBuffer(pem) {
264
+ const base64 = pem
265
+ .replace(/-----BEGIN PUBLIC KEY-----/, '')
266
+ .replace(/-----END PUBLIC KEY-----/, '')
267
+ .replace(/\s/g, '');
268
+ const binary = atob(base64);
269
+ const bytes = new Uint8Array(binary.length);
270
+ for (let i = 0; i < binary.length; i++) {
271
+ bytes[i] = binary.charCodeAt(i);
272
+ }
273
+ return bytes.buffer;
274
+ }
275
+ /**
276
+ * Convert base64url string to ArrayBuffer
277
+ */
278
+ function base64UrlToArrayBuffer(base64url) {
279
+ const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
280
+ const binary = atob(base64);
281
+ const bytes = new Uint8Array(binary.length);
282
+ for (let i = 0; i < binary.length; i++) {
283
+ bytes[i] = binary.charCodeAt(i);
284
+ }
285
+ return bytes.buffer;
286
+ }
287
+ /**
288
+ * Convert JWK algorithm name to Web Crypto API parameters
289
+ */
290
+ function jwkAlgToWebCrypto(alg) {
291
+ switch (alg) {
292
+ case 'PS256':
293
+ return {
294
+ importParams: { name: 'RSA-PSS', hash: 'SHA-256' },
295
+ verifyParams: { name: 'RSA-PSS', saltLength: 32 }
296
+ };
297
+ case 'ES256':
298
+ return {
299
+ importParams: { name: 'ECDSA', namedCurve: 'P-256' },
300
+ verifyParams: { name: 'ECDSA', hash: 'SHA-256' }
301
+ };
302
+ case 'EdDSA':
303
+ return {
304
+ importParams: { name: 'Ed25519' },
305
+ verifyParams: { name: 'Ed25519' }
306
+ };
307
+ case 'RS256':
308
+ return {
309
+ importParams: { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
310
+ verifyParams: { name: 'RSASSA-PKCS1-v1_5' }
311
+ };
312
+ default:
313
+ throw new Error(`Unsupported algorithm: ${alg}`);
314
+ }
315
+ }
316
+ /**
317
+ * Convert JWK to CryptoKey
318
+ */
319
+ async function jwkToCryptoKey(jwk, alg) {
320
+ const { importParams } = jwkAlgToWebCrypto(alg);
321
+ // Build JWK for import
322
+ const keyData = {
323
+ kty: jwk.kty,
324
+ alg: alg,
325
+ ext: true
326
+ };
327
+ if (jwk.kty === 'RSA') {
328
+ keyData.n = jwk.n;
329
+ keyData.e = jwk.e;
330
+ }
331
+ else if (jwk.kty === 'EC') {
332
+ keyData.crv = jwk.crv;
333
+ keyData.x = jwk.x;
334
+ keyData.y = jwk.y;
335
+ }
336
+ return await crypto.subtle.importKey('jwk', keyData, importParams, true, ['verify']);
337
+ }
338
+ // ============ EXPORTS ============
339
+ export default {
340
+ parseAgenticConsumer,
341
+ verifyAgenticConsumer,
342
+ parseIDToken,
343
+ verifyIDTokenSignature,
344
+ hashMatchIdentity,
345
+ buildConsumerSignatureBase
346
+ };