@aamp/protocol 1.1.4 → 1.1.6
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/aamp-protocol-1.1.5.tgz +0 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +9 -13
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +12 -9
- package/dist/crypto.js +9 -13
- package/dist/express.d.ts +1 -1
- package/dist/express.js +9 -12
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -25
- package/dist/nextjs.d.ts +1 -1
- package/dist/nextjs.js +9 -12
- package/dist/proof.d.ts +9 -0
- package/dist/proof.js +27 -0
- package/dist/publisher.d.ts +6 -1
- package/dist/publisher.js +124 -39
- package/dist/types.d.ts +21 -2
- package/dist/types.js +6 -9
- package/package.json +10 -5
- package/src/agent.ts +8 -8
- package/src/constants.ts +13 -4
- package/src/crypto.ts +6 -1
- package/src/express.ts +8 -7
- package/src/index.ts +7 -7
- package/src/nextjs.ts +11 -10
- package/src/proof.ts +36 -0
- package/src/publisher.ts +157 -47
- package/src/types.ts +42 -9
- package/test/handshake.spec.ts +6 -6
- package/tsconfig.json +9 -3
|
Binary file
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Layer 2: Agent SDK
|
|
3
3
|
*/
|
|
4
|
-
import { AccessPurpose, SignedAccessRequest, FeedbackSignal, QualityFlag, AgentIdentityManifest } from './types';
|
|
4
|
+
import { AccessPurpose, SignedAccessRequest, FeedbackSignal, QualityFlag, AgentIdentityManifest } from './types.js';
|
|
5
5
|
export interface AccessOptions {
|
|
6
6
|
adsDisplayed?: boolean;
|
|
7
7
|
}
|
package/dist/agent.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const crypto_1 = require("./crypto");
|
|
5
|
-
const constants_1 = require("./constants");
|
|
6
|
-
class AAMPAgent {
|
|
1
|
+
import { generateKeyPair, signData, exportPublicKey } from './crypto.js';
|
|
2
|
+
import { AAMP_VERSION } from './constants.js';
|
|
3
|
+
export class AAMPAgent {
|
|
7
4
|
constructor() {
|
|
8
5
|
this.keyPair = null;
|
|
9
6
|
this.agentId = "pending";
|
|
@@ -13,7 +10,7 @@ class AAMPAgent {
|
|
|
13
10
|
* @param customAgentId For PRODUCTION, this should be your domain (e.g., "bot.openai.com")
|
|
14
11
|
*/
|
|
15
12
|
async initialize(customAgentId) {
|
|
16
|
-
this.keyPair = await
|
|
13
|
+
this.keyPair = await generateKeyPair();
|
|
17
14
|
// Use the provided ID (authentic) or generate a session ID (ephemeral)
|
|
18
15
|
this.agentId = customAgentId || "agent_" + Math.random().toString(36).substring(7);
|
|
19
16
|
}
|
|
@@ -21,7 +18,7 @@ class AAMPAgent {
|
|
|
21
18
|
if (!this.keyPair)
|
|
22
19
|
throw new Error("Agent not initialized. Call initialize() first.");
|
|
23
20
|
const header = {
|
|
24
|
-
v:
|
|
21
|
+
v: AAMP_VERSION,
|
|
25
22
|
ts: new Date().toISOString(),
|
|
26
23
|
agent_id: this.agentId,
|
|
27
24
|
resource,
|
|
@@ -30,8 +27,8 @@ class AAMPAgent {
|
|
|
30
27
|
ads_displayed: options.adsDisplayed || false
|
|
31
28
|
}
|
|
32
29
|
};
|
|
33
|
-
const signature = await
|
|
34
|
-
const publicKeyExport = await
|
|
30
|
+
const signature = await signData(this.keyPair.privateKey, JSON.stringify(header));
|
|
31
|
+
const publicKeyExport = await exportPublicKey(this.keyPair.publicKey);
|
|
35
32
|
return { header, signature, publicKey: publicKeyExport };
|
|
36
33
|
}
|
|
37
34
|
/**
|
|
@@ -41,7 +38,7 @@ class AAMPAgent {
|
|
|
41
38
|
async getIdentityManifest(contactEmail) {
|
|
42
39
|
if (!this.keyPair)
|
|
43
40
|
throw new Error("Agent not initialized.");
|
|
44
|
-
const publicKey = await
|
|
41
|
+
const publicKey = await exportPublicKey(this.keyPair.publicKey);
|
|
45
42
|
return {
|
|
46
43
|
agent_id: this.agentId,
|
|
47
44
|
public_key: publicKey,
|
|
@@ -62,8 +59,7 @@ class AAMPAgent {
|
|
|
62
59
|
flags,
|
|
63
60
|
timestamp: new Date().toISOString()
|
|
64
61
|
};
|
|
65
|
-
const signature = await
|
|
62
|
+
const signature = await signData(this.keyPair.privateKey, JSON.stringify(signal));
|
|
66
63
|
return { signal, signature };
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
|
-
exports.AAMPAgent = AAMPAgent;
|
package/dist/constants.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export declare const HEADERS: {
|
|
|
13
13
|
readonly ALGORITHM: "x-aamp-alg";
|
|
14
14
|
readonly CONTENT_ORIGIN: "x-aamp-content-origin";
|
|
15
15
|
readonly PROVENANCE_SIG: "x-aamp-provenance-sig";
|
|
16
|
+
readonly PROOF_TOKEN: "x-aamp-proof";
|
|
17
|
+
readonly PAYMENT_CREDENTIAL: "x-aamp-credential";
|
|
18
|
+
readonly FEEDBACK: "x-aamp-feedback";
|
|
16
19
|
};
|
|
17
20
|
export declare const CRYPTO_CONFIG: {
|
|
18
21
|
readonly ALGORITHM_NAME: "ECDSA";
|
package/dist/constants.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Layer 1: Protocol Constants
|
|
4
3
|
* These values are immutable and defined by the AAMP Specification.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
exports.MAX_CLOCK_SKEW_MS = exports.CRYPTO_CONFIG = exports.HEADERS = exports.WELL_KNOWN_AGENT_PATH = exports.AAMP_VERSION = void 0;
|
|
8
|
-
exports.AAMP_VERSION = '1.1';
|
|
5
|
+
export const AAMP_VERSION = '1.1';
|
|
9
6
|
// The path where Agents MUST host their public key to prove identity.
|
|
10
7
|
// Example: https://bot.openai.com/.well-known/aamp-agent.json
|
|
11
|
-
|
|
8
|
+
export const WELL_KNOWN_AGENT_PATH = '/.well-known/aamp-agent.json';
|
|
12
9
|
// HTTP Headers used for the handshake
|
|
13
|
-
|
|
10
|
+
export const HEADERS = {
|
|
14
11
|
// Transport: The signed payload (Base64 encoded JSON of ProtocolHeader)
|
|
15
12
|
PAYLOAD: 'x-aamp-payload',
|
|
16
13
|
// Transport: The cryptographic signature (Hex)
|
|
@@ -23,13 +20,19 @@ exports.HEADERS = {
|
|
|
23
20
|
ALGORITHM: 'x-aamp-alg',
|
|
24
21
|
// v1.1 Addition: Provenance (Server to Agent)
|
|
25
22
|
CONTENT_ORIGIN: 'x-aamp-content-origin',
|
|
26
|
-
PROVENANCE_SIG: 'x-aamp-provenance-sig'
|
|
23
|
+
PROVENANCE_SIG: 'x-aamp-provenance-sig',
|
|
24
|
+
// v1.2 Proof of Value
|
|
25
|
+
PROOF_TOKEN: 'x-aamp-proof',
|
|
26
|
+
// v1.2 Payment Credential (The "Digital Receipt")
|
|
27
|
+
PAYMENT_CREDENTIAL: 'x-aamp-credential',
|
|
28
|
+
// v1.2 Quality Feedback (The "Dispute Token")
|
|
29
|
+
FEEDBACK: 'x-aamp-feedback'
|
|
27
30
|
};
|
|
28
31
|
// Cryptographic Settings
|
|
29
|
-
|
|
32
|
+
export const CRYPTO_CONFIG = {
|
|
30
33
|
ALGORITHM_NAME: 'ECDSA',
|
|
31
34
|
CURVE: 'P-256',
|
|
32
35
|
HASH: 'SHA-256',
|
|
33
36
|
};
|
|
34
37
|
// Tolerance
|
|
35
|
-
|
|
38
|
+
export const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000; // 5 minutes
|
package/dist/crypto.js
CHANGED
|
@@ -1,35 +1,31 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Layer 1: Cryptographic Primitives
|
|
4
3
|
* Implementation of ECDSA P-256 signing/verification.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
exports.generateKeyPair = generateKeyPair;
|
|
8
|
-
exports.signData = signData;
|
|
9
|
-
exports.verifySignature = verifySignature;
|
|
10
|
-
exports.exportPublicKey = exportPublicKey;
|
|
11
|
-
exports.importPublicKey = importPublicKey;
|
|
12
|
-
async function generateKeyPair() {
|
|
5
|
+
export async function generateKeyPair() {
|
|
13
6
|
// Uses standard Web Crypto API (Node 19+ compatible)
|
|
14
7
|
return await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
15
8
|
}
|
|
16
|
-
async function signData(privateKey, data) {
|
|
9
|
+
export async function signData(privateKey, data) {
|
|
17
10
|
const encoder = new TextEncoder();
|
|
18
11
|
const encoded = encoder.encode(data);
|
|
19
12
|
const signature = await crypto.subtle.sign({ name: "ECDSA", hash: { name: "SHA-256" } }, privateKey, encoded);
|
|
20
13
|
return bufToHex(signature);
|
|
21
14
|
}
|
|
22
|
-
async function verifySignature(publicKey, data, signatureHex) {
|
|
15
|
+
export async function verifySignature(publicKey, data, signatureHex) {
|
|
23
16
|
const encoder = new TextEncoder();
|
|
24
17
|
const encodedData = encoder.encode(data);
|
|
25
18
|
const signatureBytes = hexToBuf(signatureHex);
|
|
26
|
-
|
|
19
|
+
console.log(" 🔐 [AAMP Crypto] Verifying ECDSA P-256 Signature...");
|
|
20
|
+
const isValid = await crypto.subtle.verify({ name: "ECDSA", hash: { name: "SHA-256" } }, publicKey, signatureBytes, encodedData);
|
|
21
|
+
console.log(` ${isValid ? "✅" : "❌"} [AAMP Crypto] Signature Result: ${isValid ? "VALID" : "INVALID"}`);
|
|
22
|
+
return isValid;
|
|
27
23
|
}
|
|
28
|
-
async function exportPublicKey(key) {
|
|
24
|
+
export async function exportPublicKey(key) {
|
|
29
25
|
const exported = await crypto.subtle.exportKey("spki", key);
|
|
30
26
|
return btoa(String.fromCharCode(...new Uint8Array(exported)));
|
|
31
27
|
}
|
|
32
|
-
async function importPublicKey(keyData) {
|
|
28
|
+
export async function importPublicKey(keyData) {
|
|
33
29
|
const binaryString = atob(keyData);
|
|
34
30
|
const bytes = new Uint8Array(binaryString.length);
|
|
35
31
|
for (let i = 0; i < binaryString.length; i++) {
|
package/dist/express.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
|
|
1
|
+
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types.js';
|
|
2
2
|
export interface AAMPConfig {
|
|
3
3
|
policy: Omit<AccessPolicy, 'version'>;
|
|
4
4
|
meta: {
|
package/dist/express.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AAMP = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Layer 3: Framework Adapters
|
|
6
3
|
* Zero-friction integration for Express/Node.js.
|
|
7
4
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class AAMP {
|
|
5
|
+
import { AAMPPublisher } from './publisher.js';
|
|
6
|
+
import { ContentOrigin } from './types.js';
|
|
7
|
+
import { generateKeyPair } from './crypto.js';
|
|
8
|
+
export class AAMP {
|
|
12
9
|
constructor(config) {
|
|
13
|
-
this.publisher = new
|
|
14
|
-
this.origin =
|
|
15
|
-
this.ready =
|
|
10
|
+
this.publisher = new AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
|
|
11
|
+
this.origin = ContentOrigin[config.meta.origin];
|
|
12
|
+
this.ready = generateKeyPair().then(keys => {
|
|
16
13
|
return this.publisher.initialize(keys);
|
|
17
14
|
});
|
|
18
15
|
}
|
|
@@ -39,7 +36,8 @@ class AAMP {
|
|
|
39
36
|
if (!result.allowed) {
|
|
40
37
|
res.status(result.status).json({
|
|
41
38
|
error: result.reason,
|
|
42
|
-
visitor_type: result.visitorType
|
|
39
|
+
visitor_type: result.visitorType,
|
|
40
|
+
proof_used: result.proofUsed
|
|
43
41
|
});
|
|
44
42
|
return;
|
|
45
43
|
}
|
|
@@ -64,4 +62,3 @@ class AAMP {
|
|
|
64
62
|
};
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
|
-
exports.AAMP = AAMP;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This is the main entry point for the library.
|
|
5
5
|
*/
|
|
6
|
-
export * from './types';
|
|
7
|
-
export * from './constants';
|
|
8
|
-
export * from './agent';
|
|
9
|
-
export * from './publisher';
|
|
10
|
-
export * from './crypto';
|
|
11
|
-
export * from './express';
|
|
12
|
-
export { AAMPNext } from './nextjs';
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './constants.js';
|
|
8
|
+
export * from './agent.js';
|
|
9
|
+
export * from './publisher.js';
|
|
10
|
+
export * from './crypto.js';
|
|
11
|
+
export * from './express.js';
|
|
12
|
+
export { AAMPNext } from './nextjs.js';
|
package/dist/index.js
CHANGED
|
@@ -1,30 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* AAMP SDK Public API
|
|
4
3
|
*
|
|
5
4
|
* This is the main entry point for the library.
|
|
6
5
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}) : (function(o, m, k, k2) {
|
|
15
|
-
if (k2 === undefined) k2 = k;
|
|
16
|
-
o[k2] = m[k];
|
|
17
|
-
}));
|
|
18
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
-
};
|
|
21
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.AAMPNext = void 0;
|
|
23
|
-
__exportStar(require("./types"), exports);
|
|
24
|
-
__exportStar(require("./constants"), exports);
|
|
25
|
-
__exportStar(require("./agent"), exports);
|
|
26
|
-
__exportStar(require("./publisher"), exports);
|
|
27
|
-
__exportStar(require("./crypto"), exports);
|
|
28
|
-
__exportStar(require("./express"), exports); // Node.js / Express Adapter
|
|
29
|
-
var nextjs_1 = require("./nextjs"); // Serverless / Next.js Adapter
|
|
30
|
-
Object.defineProperty(exports, "AAMPNext", { enumerable: true, get: function () { return nextjs_1.AAMPNext; } });
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './constants.js';
|
|
8
|
+
export * from './agent.js';
|
|
9
|
+
export * from './publisher.js';
|
|
10
|
+
export * from './crypto.js';
|
|
11
|
+
export * from './express.js'; // Node.js / Express Adapter
|
|
12
|
+
export { AAMPNext } from './nextjs.js'; // Serverless / Next.js Adapter
|
package/dist/nextjs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
|
|
1
|
+
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types.js';
|
|
2
2
|
type NextRequest = any;
|
|
3
3
|
type NextResponse = any;
|
|
4
4
|
export interface AAMPConfig {
|
package/dist/nextjs.js
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AAMPNext = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Layer 3: Framework Adapters
|
|
6
3
|
* Serverless integration for Next.js (App Router & API Routes).
|
|
7
4
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { AAMPPublisher } from './publisher.js';
|
|
6
|
+
import { ContentOrigin } from './types.js';
|
|
7
|
+
import { generateKeyPair } from './crypto.js';
|
|
11
8
|
const createJsonResponse = (body, status = 200) => {
|
|
12
9
|
return new Response(JSON.stringify(body), {
|
|
13
10
|
status,
|
|
14
11
|
headers: { 'Content-Type': 'application/json' }
|
|
15
12
|
});
|
|
16
13
|
};
|
|
17
|
-
class AAMPNext {
|
|
14
|
+
export class AAMPNext {
|
|
18
15
|
constructor(config) {
|
|
19
|
-
this.publisher = new
|
|
20
|
-
this.origin =
|
|
21
|
-
this.ready =
|
|
16
|
+
this.publisher = new AAMPPublisher({ version: '1.1', ...config.policy }, config.strategy || 'PASSIVE', config.cache);
|
|
17
|
+
this.origin = ContentOrigin[config.meta.origin];
|
|
18
|
+
this.ready = generateKeyPair().then(keys => this.publisher.initialize(keys));
|
|
22
19
|
}
|
|
23
20
|
static init(config) {
|
|
24
21
|
return new AAMPNext(config);
|
|
@@ -39,7 +36,8 @@ class AAMPNext {
|
|
|
39
36
|
if (!result.allowed) {
|
|
40
37
|
return createJsonResponse({
|
|
41
38
|
error: result.reason,
|
|
42
|
-
visitor_type: result.visitorType
|
|
39
|
+
visitor_type: result.visitorType,
|
|
40
|
+
proof_used: result.proofUsed
|
|
43
41
|
}, result.status);
|
|
44
42
|
}
|
|
45
43
|
// Execute Handler
|
|
@@ -60,4 +58,3 @@ class AAMPNext {
|
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
|
-
exports.AAMPNext = AAMPNext;
|
package/dist/proof.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies a JWT (Proof Token or Payment Credential) using JWKS.
|
|
3
|
+
*
|
|
4
|
+
* @param token The JWT string
|
|
5
|
+
* @param jwksUrl The URL to fetch Public Keys
|
|
6
|
+
* @param issuer The expected issuer
|
|
7
|
+
* @param audience The expected audience range
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifyJwt(token: string, jwksUrl: string, issuer: string, audience?: string): Promise<boolean>;
|
package/dist/proof.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
2
|
+
// In-memory cache for JWKS to avoid repeated fetches
|
|
3
|
+
// Jose's createRemoteJWKSet handles caching/cooldowns internally.
|
|
4
|
+
/**
|
|
5
|
+
* Verifies a JWT (Proof Token or Payment Credential) using JWKS.
|
|
6
|
+
*
|
|
7
|
+
* @param token The JWT string
|
|
8
|
+
* @param jwksUrl The URL to fetch Public Keys
|
|
9
|
+
* @param issuer The expected issuer
|
|
10
|
+
* @param audience The expected audience range
|
|
11
|
+
*/
|
|
12
|
+
export async function verifyJwt(token, jwksUrl, issuer, audience) {
|
|
13
|
+
try {
|
|
14
|
+
const JWKS = createRemoteJWKSet(new URL(jwksUrl));
|
|
15
|
+
const { payload } = await jwtVerify(token, JWKS, {
|
|
16
|
+
issuer: issuer,
|
|
17
|
+
audience: audience // specific audience check if provided
|
|
18
|
+
});
|
|
19
|
+
// Check specific AAMP claims if we standardize them
|
|
20
|
+
// if (payload.type !== 'AD_IMPRESSION') return false;
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
// console.error("Ad Proof Verification Failed:", error);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/publisher.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AccessPolicy, ContentOrigin, EvaluationResult, IdentityCache, UnauthenticatedStrategy } from './types';
|
|
1
|
+
import { AccessPolicy, ContentOrigin, EvaluationResult, IdentityCache, UnauthenticatedStrategy } from './types.js';
|
|
2
2
|
export declare class AAMPPublisher {
|
|
3
3
|
private policy;
|
|
4
4
|
private keyPair;
|
|
@@ -27,4 +27,9 @@ export declare class AAMPPublisher {
|
|
|
27
27
|
private verifyDnsBinding;
|
|
28
28
|
private isDomain;
|
|
29
29
|
generateResponseHeaders(origin: ContentOrigin): Promise<Record<string, string>>;
|
|
30
|
+
/**
|
|
31
|
+
* Handling Quality Feedback (The "Dispute" Layer)
|
|
32
|
+
* This runs when an Agent sends 'x-aamp-feedback'.
|
|
33
|
+
*/
|
|
34
|
+
private handleFeedback;
|
|
30
35
|
}
|