@hathbanger/tap-core 1.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/README.md +280 -0
- package/dist/agent.d.ts +13 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +32 -0
- package/dist/agent.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/jwks.d.ts +14 -0
- package/dist/jwks.d.ts.map +1 -0
- package/dist/jwks.js +31 -0
- package/dist/jwks.js.map +1 -0
- package/dist/keys.d.ts +12 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +65 -0
- package/dist/keys.js.map +1 -0
- package/dist/nonce.d.ts +10 -0
- package/dist/nonce.d.ts.map +1 -0
- package/dist/nonce.js +26 -0
- package/dist/nonce.js.map +1 -0
- package/dist/payment.d.ts +19 -0
- package/dist/payment.d.ts.map +1 -0
- package/dist/payment.js +89 -0
- package/dist/payment.js.map +1 -0
- package/dist/sign.d.ts +35 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +83 -0
- package/dist/sign.js.map +1 -0
- package/dist/tokens.d.ts +22 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +55 -0
- package/dist/tokens.js.map +1 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +54 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +189 -0
- package/dist/verify.js.map +1 -0
- package/package.json +54 -0
package/dist/payment.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { webcrypto } from "node:crypto";
|
|
2
|
+
import { signPayload } from "./sign.js";
|
|
3
|
+
import { base64url } from "./keys.js";
|
|
4
|
+
import { generateNonce, nowSeconds } from "./nonce.js";
|
|
5
|
+
/**
|
|
6
|
+
* Builds a PaymentContainerObject for checkout requests.
|
|
7
|
+
*
|
|
8
|
+
* In production this would contain an encrypted Visa payment token.
|
|
9
|
+
* In demo mode it contains a mock token with realistic structure.
|
|
10
|
+
*/
|
|
11
|
+
export async function buildPaymentContainer(keyPair, nonce, payload, cardMetadata) {
|
|
12
|
+
const unsigned = {
|
|
13
|
+
nonce,
|
|
14
|
+
payload,
|
|
15
|
+
cardMetadata,
|
|
16
|
+
kid: keyPair.keyId,
|
|
17
|
+
alg: "ed25519",
|
|
18
|
+
};
|
|
19
|
+
const signature = await signPayload(keyPair, unsigned);
|
|
20
|
+
return { ...unsigned, signature };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a mock payment token for demo purposes.
|
|
24
|
+
* Simulates the encrypted token Visa would issue in production.
|
|
25
|
+
*/
|
|
26
|
+
export async function createMockPaymentToken(lastFour, cardholderName, billingAddress, shippingAddress, email) {
|
|
27
|
+
const raw = JSON.stringify({
|
|
28
|
+
pan_hash: await hashPan(lastFour),
|
|
29
|
+
cardholder: cardholderName,
|
|
30
|
+
billing: billingAddress,
|
|
31
|
+
shipping: shippingAddress,
|
|
32
|
+
contact: email,
|
|
33
|
+
issued_at: Date.now(),
|
|
34
|
+
});
|
|
35
|
+
const encoded = new TextEncoder().encode(raw);
|
|
36
|
+
const token = base64url(encoded.buffer);
|
|
37
|
+
return {
|
|
38
|
+
payload: {
|
|
39
|
+
token,
|
|
40
|
+
cardholderName,
|
|
41
|
+
billingAddress,
|
|
42
|
+
shippingAddress,
|
|
43
|
+
email,
|
|
44
|
+
},
|
|
45
|
+
cardMetadata: {
|
|
46
|
+
lastFour,
|
|
47
|
+
paymentAccountReference: generatePar(),
|
|
48
|
+
brand: "Visa",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function buildTapPaymentRequest(keyPair, amount, currency, description) {
|
|
53
|
+
return {
|
|
54
|
+
payeeKeyId: keyPair.keyId,
|
|
55
|
+
amount,
|
|
56
|
+
currency,
|
|
57
|
+
description,
|
|
58
|
+
nonce: generateNonce(),
|
|
59
|
+
expiresAt: nowSeconds() + 300,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export async function buildAgentPayeeObject(keyPair, paymentRequest) {
|
|
63
|
+
const payerKeyId = keyPair.keyId;
|
|
64
|
+
const timestamp = new Date().toISOString();
|
|
65
|
+
const nonce = generateNonce();
|
|
66
|
+
const unsigned = {
|
|
67
|
+
paymentRequest,
|
|
68
|
+
payerKeyId,
|
|
69
|
+
timestamp,
|
|
70
|
+
nonce,
|
|
71
|
+
kid: payerKeyId,
|
|
72
|
+
alg: "ed25519",
|
|
73
|
+
};
|
|
74
|
+
const signature = await signPayload(keyPair, unsigned);
|
|
75
|
+
return { ...unsigned, signature };
|
|
76
|
+
}
|
|
77
|
+
async function hashPan(lastFour) {
|
|
78
|
+
const encoded = new TextEncoder().encode(`DEMO-CARD-${lastFour}`);
|
|
79
|
+
const hash = await webcrypto.subtle.digest("SHA-256", encoded);
|
|
80
|
+
return Buffer.from(hash).toString("hex");
|
|
81
|
+
}
|
|
82
|
+
function generatePar() {
|
|
83
|
+
const bytes = webcrypto.getRandomValues(new Uint8Array(16));
|
|
84
|
+
return Array.from(bytes)
|
|
85
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
86
|
+
.join("")
|
|
87
|
+
.toUpperCase();
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=payment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.js","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"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"}
|
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { SignatureInputParams, SignatureTag, TapKeyPair, TapSignedHeaders } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* RFC 9421 — Signature Base construction.
|
|
4
|
+
*
|
|
5
|
+
* Accepts an ordered list of [componentName, componentValue] tuples covering
|
|
6
|
+
* any mix of derived components (@method, @authority, @path, @target-uri, etc.)
|
|
7
|
+
* and HTTP header fields (content-type, content-digest, authorization, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Each line is "<component-name>": <value>, joined by \n.
|
|
10
|
+
* The final line is always @signature-params with the serialized Inner List.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildSignatureBase(components: Array<[string, string]>, params: SignatureInputParams): string;
|
|
13
|
+
/**
|
|
14
|
+
* Serializes an ordered component name list + SignatureInputParams into the
|
|
15
|
+
* RFC 9421 Inner List format.
|
|
16
|
+
* e.g. ("@method" "@authority" "@path");created=1735689600;expires=1735693200;...
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildSignatureInputParams(componentNames: string[], params: SignatureInputParams): string;
|
|
19
|
+
/**
|
|
20
|
+
* Signs an HTTP request using Ed25519 (RFC 9421).
|
|
21
|
+
*
|
|
22
|
+
* Pass an ordered list of [componentName, componentValue] tuples to control
|
|
23
|
+
* exactly which components are covered. Common derived components: @method,
|
|
24
|
+
* @authority, @path, @target-uri, @scheme. HTTP fields: content-type,
|
|
25
|
+
* content-digest, authorization, etc.
|
|
26
|
+
*
|
|
27
|
+
* Returns the Signature-Input and Signature header values ready to attach.
|
|
28
|
+
*/
|
|
29
|
+
export declare function signRequest(keyPair: TapKeyPair, components: Array<[string, string]>, tag: SignatureTag): Promise<TapSignedHeaders>;
|
|
30
|
+
/**
|
|
31
|
+
* Signs an arbitrary payload (for Consumer Recognition Object and Payment Container).
|
|
32
|
+
* Uses the same key as the request signature, tied via nonce.
|
|
33
|
+
*/
|
|
34
|
+
export declare function signPayload(keyPair: TapKeyPair, payload: Record<string, unknown>): Promise<string>;
|
|
35
|
+
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlG;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACnC,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CAUR;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAYxG;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,EACnB,UAAU,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACnC,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAwB3B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,MAAM,CAAC,CAKjB"}
|
package/dist/sign.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { webcrypto } from "node:crypto";
|
|
2
|
+
import { base64url } from "./keys.js";
|
|
3
|
+
import { generateNonce, makeTimestamps } from "./nonce.js";
|
|
4
|
+
const subtle = webcrypto.subtle;
|
|
5
|
+
/**
|
|
6
|
+
* RFC 9421 — Signature Base construction.
|
|
7
|
+
*
|
|
8
|
+
* Accepts an ordered list of [componentName, componentValue] tuples covering
|
|
9
|
+
* any mix of derived components (@method, @authority, @path, @target-uri, etc.)
|
|
10
|
+
* and HTTP header fields (content-type, content-digest, authorization, etc.).
|
|
11
|
+
*
|
|
12
|
+
* Each line is "<component-name>": <value>, joined by \n.
|
|
13
|
+
* The final line is always @signature-params with the serialized Inner List.
|
|
14
|
+
*/
|
|
15
|
+
export function buildSignatureBase(components, params) {
|
|
16
|
+
const componentNames = components.map(([name]) => name);
|
|
17
|
+
const paramsString = buildSignatureInputParams(componentNames, params);
|
|
18
|
+
const lines = [
|
|
19
|
+
...components.map(([name, value]) => `"${name}": ${value}`),
|
|
20
|
+
`"@signature-params": ${paramsString}`,
|
|
21
|
+
];
|
|
22
|
+
return lines.join("\n");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Serializes an ordered component name list + SignatureInputParams into the
|
|
26
|
+
* RFC 9421 Inner List format.
|
|
27
|
+
* e.g. ("@method" "@authority" "@path");created=1735689600;expires=1735693200;...
|
|
28
|
+
*/
|
|
29
|
+
export function buildSignatureInputParams(componentNames, params) {
|
|
30
|
+
const components = `(${componentNames.map((n) => `"${n}"`).join(" ")})`;
|
|
31
|
+
const attrs = [
|
|
32
|
+
`created=${params.created}`,
|
|
33
|
+
`expires=${params.expires}`,
|
|
34
|
+
`nonce="${params.nonce}"`,
|
|
35
|
+
`keyid="${params.keyId}"`,
|
|
36
|
+
`alg="${params.alg}"`,
|
|
37
|
+
`tag="${params.tag}"`,
|
|
38
|
+
].join(";");
|
|
39
|
+
return `${components};${attrs}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Signs an HTTP request using Ed25519 (RFC 9421).
|
|
43
|
+
*
|
|
44
|
+
* Pass an ordered list of [componentName, componentValue] tuples to control
|
|
45
|
+
* exactly which components are covered. Common derived components: @method,
|
|
46
|
+
* @authority, @path, @target-uri, @scheme. HTTP fields: content-type,
|
|
47
|
+
* content-digest, authorization, etc.
|
|
48
|
+
*
|
|
49
|
+
* Returns the Signature-Input and Signature header values ready to attach.
|
|
50
|
+
*/
|
|
51
|
+
export async function signRequest(keyPair, components, tag) {
|
|
52
|
+
const { created, expires } = makeTimestamps();
|
|
53
|
+
const nonce = generateNonce();
|
|
54
|
+
const params = {
|
|
55
|
+
tag,
|
|
56
|
+
keyId: keyPair.keyId,
|
|
57
|
+
alg: "ed25519",
|
|
58
|
+
created,
|
|
59
|
+
expires,
|
|
60
|
+
nonce,
|
|
61
|
+
};
|
|
62
|
+
const componentNames = components.map(([name]) => name);
|
|
63
|
+
const paramsString = buildSignatureInputParams(componentNames, params);
|
|
64
|
+
const signatureBase = buildSignatureBase(components, params);
|
|
65
|
+
const encoded = new TextEncoder().encode(signatureBase);
|
|
66
|
+
const signatureBuffer = await subtle.sign("Ed25519", keyPair.privateKey, encoded);
|
|
67
|
+
const signatureB64 = base64url(signatureBuffer);
|
|
68
|
+
return {
|
|
69
|
+
"signature-input": `sig1=${paramsString}`,
|
|
70
|
+
signature: `sig1=:${signatureB64}:`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Signs an arbitrary payload (for Consumer Recognition Object and Payment Container).
|
|
75
|
+
* Uses the same key as the request signature, tied via nonce.
|
|
76
|
+
*/
|
|
77
|
+
export async function signPayload(keyPair, payload) {
|
|
78
|
+
const canonical = JSON.stringify(payload, Object.keys(payload).sort());
|
|
79
|
+
const encoded = new TextEncoder().encode(canonical);
|
|
80
|
+
const signatureBuffer = await subtle.sign("Ed25519", keyPair.privateKey, encoded);
|
|
81
|
+
return base64url(signatureBuffer);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=sign.js.map
|
package/dist/sign.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IdTokenClaims, TapJwk, TapKeyPair } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Visa-style ID Token (JWT) for the Consumer Recognition Object.
|
|
4
|
+
* In prod this would be issued by Visa; in dev/demo the agent issues its own.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createIdToken(keyPair: TapKeyPair, claims: Partial<IdTokenClaims> & {
|
|
7
|
+
sub: string;
|
|
8
|
+
aud: string;
|
|
9
|
+
}): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Verifies an ID Token using the agent's public key (dev mode).
|
|
12
|
+
* In prod, uses Visa's published JWKS.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifyIdToken(token: string, publicJwk: TapJwk, audience: string): Promise<IdTokenClaims>;
|
|
15
|
+
/**
|
|
16
|
+
* Hashes a PII value (email/phone) for inclusion in Consumer Recognition Object.
|
|
17
|
+
* Uses SHA-256 so the merchant can match against their own hashed records.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hashPii(value: string): Promise<string>;
|
|
20
|
+
export declare function maskEmail(email: string): string;
|
|
21
|
+
export declare function maskPhone(phone: string): string;
|
|
22
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAKnE;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC5D,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAIxB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK5D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { SignJWT, jwtVerify, importJWK } from "jose";
|
|
2
|
+
const ISSUER = "tap-stack-dev";
|
|
3
|
+
const TOKEN_TTL_SECONDS = 300;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a Visa-style ID Token (JWT) for the Consumer Recognition Object.
|
|
6
|
+
* In prod this would be issued by Visa; in dev/demo the agent issues its own.
|
|
7
|
+
*/
|
|
8
|
+
export async function createIdToken(keyPair, claims) {
|
|
9
|
+
const now = Math.floor(Date.now() / 1000);
|
|
10
|
+
return new SignJWT({
|
|
11
|
+
phone_number: claims.phone_number,
|
|
12
|
+
email: claims.email,
|
|
13
|
+
phone_number_verified: claims.phone_number_verified ?? false,
|
|
14
|
+
email_verified: claims.email_verified ?? false,
|
|
15
|
+
phone_number_mask: claims.phone_number_mask,
|
|
16
|
+
email_mask: claims.email_mask,
|
|
17
|
+
})
|
|
18
|
+
.setProtectedHeader({ alg: "EdDSA", kid: keyPair.keyId })
|
|
19
|
+
.setIssuer(claims.iss ?? ISSUER)
|
|
20
|
+
.setSubject(claims.sub)
|
|
21
|
+
.setAudience(claims.aud)
|
|
22
|
+
.setIssuedAt(now)
|
|
23
|
+
.setExpirationTime(now + TOKEN_TTL_SECONDS)
|
|
24
|
+
.sign(keyPair.privateKey);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Verifies an ID Token using the agent's public key (dev mode).
|
|
28
|
+
* In prod, uses Visa's published JWKS.
|
|
29
|
+
*/
|
|
30
|
+
export async function verifyIdToken(token, publicJwk, audience) {
|
|
31
|
+
const key = await importJWK(publicJwk, "EdDSA");
|
|
32
|
+
const { payload } = await jwtVerify(token, key, { audience });
|
|
33
|
+
return payload;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Hashes a PII value (email/phone) for inclusion in Consumer Recognition Object.
|
|
37
|
+
* Uses SHA-256 so the merchant can match against their own hashed records.
|
|
38
|
+
*/
|
|
39
|
+
export async function hashPii(value) {
|
|
40
|
+
const { webcrypto } = await import("node:crypto");
|
|
41
|
+
const encoded = new TextEncoder().encode(value.toLowerCase().trim());
|
|
42
|
+
const hash = await webcrypto.subtle.digest("SHA-256", encoded);
|
|
43
|
+
return Buffer.from(hash).toString("hex");
|
|
44
|
+
}
|
|
45
|
+
export function maskEmail(email) {
|
|
46
|
+
const [local, domain] = email.split("@");
|
|
47
|
+
if (!local || !domain)
|
|
48
|
+
return "***@***";
|
|
49
|
+
return `${local[0]}***@${domain}`;
|
|
50
|
+
}
|
|
51
|
+
export function maskPhone(phone) {
|
|
52
|
+
const digits = phone.replace(/\D/g, "");
|
|
53
|
+
return `***-***-${digits.slice(-4)}`;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAIpD,MAAM,MAAM,GAAG,eAAe,CAAA;AAC9B,MAAM,iBAAiB,GAAG,GAAG,CAAA;AAE7B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAmB,EACnB,MAA6D;IAE7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IAEzC,OAAO,IAAI,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,IAAI,KAAK;QAC5D,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,KAAK;QAC9C,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC;SACC,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;SACxD,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;SAC/B,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC;SACtB,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC;SACvB,WAAW,CAAC,GAAG,CAAC;SAChB,iBAAiB,CAAC,GAAG,GAAG,iBAAiB,CAAC;SAC1C,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAa,EACb,SAAiB,EACjB,QAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAgB,EAAE,OAAO,CAAC,CAAA;IACtD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC7D,OAAO,OAAmC,CAAA;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAa;IACzC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;IACjD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IACpE,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAC9D,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IACvC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACvC,OAAO,WAAW,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACtC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export interface TapJwk {
|
|
2
|
+
kty: string;
|
|
3
|
+
crv?: string;
|
|
4
|
+
x?: string;
|
|
5
|
+
d?: string;
|
|
6
|
+
n?: string;
|
|
7
|
+
e?: string;
|
|
8
|
+
kid?: string;
|
|
9
|
+
use?: string;
|
|
10
|
+
alg?: string;
|
|
11
|
+
key_ops?: string[];
|
|
12
|
+
}
|
|
13
|
+
export type SignatureTag = "agent-browser-auth" | "agent-payer-auth" | "agent-payee-auth";
|
|
14
|
+
export type SignatureAlgorithm = "ed25519" | "rsa-pss-sha512";
|
|
15
|
+
export interface TapKeyPair {
|
|
16
|
+
keyId: string;
|
|
17
|
+
privateKey: CryptoKey;
|
|
18
|
+
publicKey: CryptoKey;
|
|
19
|
+
jwk: TapJwk;
|
|
20
|
+
}
|
|
21
|
+
export interface SignatureInputParams {
|
|
22
|
+
tag: SignatureTag;
|
|
23
|
+
keyId: string;
|
|
24
|
+
alg: SignatureAlgorithm;
|
|
25
|
+
created: number;
|
|
26
|
+
expires: number;
|
|
27
|
+
nonce: string;
|
|
28
|
+
}
|
|
29
|
+
export interface TapSignedHeaders {
|
|
30
|
+
"signature-input": string;
|
|
31
|
+
signature: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ConsumerRecognitionObject {
|
|
34
|
+
nonce: string;
|
|
35
|
+
idToken: string;
|
|
36
|
+
contextualData: ContextualData;
|
|
37
|
+
kid: string;
|
|
38
|
+
alg: SignatureAlgorithm;
|
|
39
|
+
signature: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ContextualData {
|
|
42
|
+
deviceId?: string;
|
|
43
|
+
ipAddress?: string;
|
|
44
|
+
countryCode?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PaymentContainerObject {
|
|
47
|
+
nonce: string;
|
|
48
|
+
payload: EncryptedPaymentPayload;
|
|
49
|
+
cardMetadata: CardMetadata;
|
|
50
|
+
kid: string;
|
|
51
|
+
alg: SignatureAlgorithm;
|
|
52
|
+
signature: string;
|
|
53
|
+
}
|
|
54
|
+
export interface EncryptedPaymentPayload {
|
|
55
|
+
token: string;
|
|
56
|
+
cardholderName: string;
|
|
57
|
+
billingAddress: Address;
|
|
58
|
+
shippingAddress: Address;
|
|
59
|
+
email: string;
|
|
60
|
+
}
|
|
61
|
+
export interface CardMetadata {
|
|
62
|
+
lastFour: string;
|
|
63
|
+
paymentAccountReference: string;
|
|
64
|
+
brand: string;
|
|
65
|
+
}
|
|
66
|
+
export interface Address {
|
|
67
|
+
line1: string;
|
|
68
|
+
line2?: string;
|
|
69
|
+
city: string;
|
|
70
|
+
state: string;
|
|
71
|
+
postalCode: string;
|
|
72
|
+
country: string;
|
|
73
|
+
}
|
|
74
|
+
export interface IdTokenClaims {
|
|
75
|
+
iss: string;
|
|
76
|
+
sub: string;
|
|
77
|
+
aud: string | string[];
|
|
78
|
+
exp: number;
|
|
79
|
+
iat: number;
|
|
80
|
+
phone_number?: string;
|
|
81
|
+
email?: string;
|
|
82
|
+
phone_number_verified?: boolean;
|
|
83
|
+
email_verified?: boolean;
|
|
84
|
+
phone_number_mask?: string;
|
|
85
|
+
email_mask?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface VerificationResult {
|
|
88
|
+
valid: boolean;
|
|
89
|
+
agentKeyId?: string;
|
|
90
|
+
tag?: SignatureTag;
|
|
91
|
+
consumerId?: string;
|
|
92
|
+
failureReason?: string;
|
|
93
|
+
}
|
|
94
|
+
export interface JwksResponse {
|
|
95
|
+
keys: TapJwk[];
|
|
96
|
+
}
|
|
97
|
+
export interface TapPaymentRequest {
|
|
98
|
+
payeeKeyId: string;
|
|
99
|
+
amount: number;
|
|
100
|
+
currency: string;
|
|
101
|
+
description: string;
|
|
102
|
+
nonce: string;
|
|
103
|
+
expiresAt: number;
|
|
104
|
+
}
|
|
105
|
+
export interface AgentPayeeObject {
|
|
106
|
+
paymentRequest: TapPaymentRequest;
|
|
107
|
+
payerKeyId: string;
|
|
108
|
+
timestamp: string;
|
|
109
|
+
nonce: string;
|
|
110
|
+
kid: string;
|
|
111
|
+
alg: "ed25519";
|
|
112
|
+
signature: string;
|
|
113
|
+
}
|
|
114
|
+
export interface PaymentVerificationResult {
|
|
115
|
+
valid: boolean;
|
|
116
|
+
payerKeyId?: string;
|
|
117
|
+
failureReason?: string;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AgentPayeeObject, ConsumerRecognitionObject, PaymentContainerObject, PaymentVerificationResult, SignatureInputParams, TapJwk, VerificationResult } from "./types.js";
|
|
2
|
+
export interface ParsedSignatureInput {
|
|
3
|
+
params: SignatureInputParams;
|
|
4
|
+
components: string[];
|
|
5
|
+
raw: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Parses a Signature-Input header value into structured params + covered component list.
|
|
9
|
+
* Expected format: sig1=("@method" "@authority" "@path");created=...;expires=...;nonce=...;keyid=...;alg=...;tag=...
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseSignatureInput(header: string): ParsedSignatureInput;
|
|
12
|
+
/**
|
|
13
|
+
* Extracts the raw base64url signature value from the Signature header.
|
|
14
|
+
* Expected format: sig1=:[base64url]:
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseSignatureHeader(header: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Core TAP agent recognition signature verification (RFC 9421).
|
|
19
|
+
*
|
|
20
|
+
* Pass a map of all available component values from the incoming request
|
|
21
|
+
* (e.g. { '@method': 'POST', '@authority': 'api.example.com', '@path': '/v1/pay' }).
|
|
22
|
+
* The verifier reads the covered component list from the Signature-Input header
|
|
23
|
+
* and reconstructs the signature base using only those components in order.
|
|
24
|
+
*
|
|
25
|
+
* Steps per spec:
|
|
26
|
+
* 1. Parse Signature-Input and extract covered component list
|
|
27
|
+
* 2. Validate timestamps (created < now < expires, gap ≤ 8min)
|
|
28
|
+
* 3. Confirm tag is valid
|
|
29
|
+
* 4. Fetch public key by keyid
|
|
30
|
+
* 5. Reconstruct signature base from parsed component list + provided values
|
|
31
|
+
* 6. Verify Ed25519 signature
|
|
32
|
+
*/
|
|
33
|
+
export declare function verifyAgentSignature(componentValues: Record<string, string>, signatureInputHeader: string, signatureHeader: string, fetchPublicKey: (keyId: string) => Promise<TapJwk | null>): Promise<VerificationResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Verifies an AgentPayeeObject — the proposed TAP primitive for A2A payments.
|
|
36
|
+
*
|
|
37
|
+
* Steps:
|
|
38
|
+
* 1. Confirm payment request is not expired
|
|
39
|
+
* 2. Fetch the payer's public key by kid
|
|
40
|
+
* 3. Reconstruct canonical payload (sans signature)
|
|
41
|
+
* 4. Verify Ed25519 signature
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyAgentPayeeObject(payment: AgentPayeeObject, fetchPublicKey: (keyId: string) => Promise<TapJwk | null>): Promise<PaymentVerificationResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Verifies the Consumer Recognition Object signature.
|
|
46
|
+
* The nonce must match the agent signature nonce.
|
|
47
|
+
*/
|
|
48
|
+
export declare function verifyConsumerRecognition(obj: ConsumerRecognitionObject, expectedNonce: string, fetchPublicKey: (keyId: string) => Promise<TapJwk | null>): Promise<VerificationResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Verifies the Payment Container Object signature.
|
|
51
|
+
* The nonce must match the agent signature nonce if present.
|
|
52
|
+
*/
|
|
53
|
+
export declare function verifyPaymentContainer(obj: PaymentContainerObject, expectedNonce: string, fetchPublicKey: (keyId: string) => Promise<TapJwk | null>): Promise<VerificationResult>;
|
|
54
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAChB,yBAAyB,EACzB,sBAAsB,EACtB,yBAAyB,EACzB,oBAAoB,EAEpB,MAAM,EACN,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAInB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,oBAAoB,CAAA;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CA6BxE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CACxC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,oBAAoB,EAAE,MAAM,EAC5B,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACxD,OAAO,CAAC,kBAAkB,CAAC,CA6D7B;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,gBAAgB,EACzB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACxD,OAAO,CAAC,yBAAyB,CAAC,CA8BpC;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,yBAAyB,EAC9B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACxD,OAAO,CAAC,kBAAkB,CAAC,CAmB7B;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,sBAAsB,EAC3B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GACxD,OAAO,CAAC,kBAAkB,CAAC,CAmB7B"}
|