@aamp/protocol 1.1.7 → 1.1.9

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.
@@ -33,6 +33,11 @@ export declare class AAMPPublisher {
33
33
  private checkPolicyStrict;
34
34
  private verifyRequestLogic;
35
35
  private verifyDnsBinding;
36
+ /**
37
+ * NEW: Verify a Broker-Issued Token (JWT)
38
+ * Checks if the request contains a valid "Visa" from the Broker.
39
+ */
40
+ private verifyBrokerCred;
36
41
  private isDomain;
37
42
  generateResponseHeaders(origin: ContentOrigin): Promise<Record<string, string>>;
38
43
  /**
package/dist/publisher.js CHANGED
@@ -6,6 +6,7 @@ import { HEADERS, MAX_CLOCK_SKEW_MS, WELL_KNOWN_AGENT_PATH } from './constants.j
6
6
  import { exportPublicKey, signData, verifySignature } from './crypto.js';
7
7
  import { AccessPurpose } from './types.js';
8
8
  import { verifyJwt } from './proof.js';
9
+ import { jwtVerify, createRemoteJWKSet } from 'jose';
9
10
  /**
10
11
  * Default In-Memory Cache (Fallback only)
11
12
  * NOT recommended for high-traffic Serverless production.
@@ -186,6 +187,19 @@ export class AAMPPublisher {
186
187
  if (requestHeader.purpose === AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
187
188
  return { allowed: false, status: 403, reason: 'POLICY_DENIED: Training not allowed.', visitorType: 'VERIFIED_AGENT' };
188
189
  }
190
+ // 2. BROKER CHECK (New v1.1)
191
+ if (this.policy.monetization?.brokerUrl) {
192
+ const brokerUrl = this.policy.monetization.brokerUrl;
193
+ if (!paymentCredential) {
194
+ return { allowed: false, status: 402, reason: "PAYMENT_REQUIRED: Missing Broker Credential", visitorType: 'UNIDENTIFIED_BOT' };
195
+ }
196
+ const isValid = await this.verifyBrokerCred(paymentCredential, brokerUrl);
197
+ if (!isValid) {
198
+ return { allowed: false, status: 403, reason: "PAYMENT_DENIED: Invalid Broker Token", visitorType: 'UNIDENTIFIED_BOT' };
199
+ }
200
+ // If valid, we record the "Proof Used" so we can settle later
201
+ return { allowed: true, status: 200, reason: "AAMP_PAID", visitorType: "VERIFIED_AGENT", proofUsed: `BROKER_JWT:${paymentCredential.slice(0, 10)}...` };
202
+ }
189
203
  if (requestHeader.purpose === AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
190
204
  return { allowed: false, status: 403, reason: 'POLICY_DENIED: RAG not allowed.', visitorType: 'VERIFIED_AGENT' };
191
205
  }
@@ -275,7 +289,7 @@ export class AAMPPublisher {
275
289
  return { allowed: false, reason: 'IDENTITY_FAIL: DNS Binding could not be verified.', identityVerified: false };
276
290
  }
277
291
  // Return verified status so handleAgentStrict can proceed to Policy Check
278
- return { allowed: true, reason: 'OK', identityVerified: true };
292
+ return { allowed: true, reason: 'OK', identityVerified: identityVerified };
279
293
  }
280
294
  async verifyDnsBinding(domain, requestKeySpki) {
281
295
  try {
@@ -312,6 +326,27 @@ export class AAMPPublisher {
312
326
  return false;
313
327
  }
314
328
  }
329
+ /**
330
+ * NEW: Verify a Broker-Issued Token (JWT)
331
+ * Checks if the request contains a valid "Visa" from the Broker.
332
+ */
333
+ async verifyBrokerCred(credential, brokerUrl) {
334
+ try {
335
+ // 1. Fetch Broker's Public Keys (JWKS)
336
+ const JWKS = createRemoteJWKSet(new URL(`${brokerUrl}/.well-known/jwks.json`));
337
+ // 2. Verify the Token Signature
338
+ const { payload } = await jwtVerify(credential, JWKS, {
339
+ issuer: brokerUrl, // Ensure it came from THE Broker
340
+ clockTolerance: 5 // Allow 5s clock skew
341
+ });
342
+ console.log(`[BROKER] 💰 Valid Payment Token from ${payload.iss} for amount ${payload.amount}`);
343
+ return true;
344
+ }
345
+ catch (e) {
346
+ console.warn(`[BROKER] ❌ Invalid Token:`, e.message);
347
+ return false;
348
+ }
349
+ }
315
350
  isDomain(s) {
316
351
  // Basic regex, allows localhost with ports
317
352
  return /^[a-zA-Z0-9.-]+(:\d+)?$/.test(s) || /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(s);
package/dist/types.d.ts CHANGED
@@ -49,6 +49,7 @@ export interface MonetizationConfig {
49
49
  jwksUrl: string;
50
50
  issuer: string;
51
51
  };
52
+ brokerUrl?: string;
52
53
  paymentConfig?: {
53
54
  jwksUrl: string;
54
55
  issuer: string;
package/package.json CHANGED
@@ -1,15 +1,22 @@
1
1
  {
2
2
  "name": "@aamp/protocol",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "TypeScript reference implementation of AAMP v1.1",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./dist/nextjs": "./dist/nextjs.js",
10
+ "./dist/agent": "./dist/agent.js",
11
+ "./dist/types": "./dist/types.js"
12
+ },
7
13
  "type": "module",
8
14
  "scripts": {
9
15
  "build": "tsc",
10
16
  "test": "node --loader ts-node/esm test/handshake.spec.ts"
11
17
  },
12
18
  "repository": {
19
+ "license": "Apache-2.0",
13
20
  "type": "git",
14
21
  "url": "https://github.com/aamp-protocol/aamp.git"
15
22
  },
package/src/publisher.ts CHANGED
@@ -6,6 +6,7 @@ import { HEADERS, MAX_CLOCK_SKEW_MS, WELL_KNOWN_AGENT_PATH } from './constants.j
6
6
  import { exportPublicKey, signData, verifySignature } from './crypto.js';
7
7
  import { AccessPolicy, AccessPurpose, AgentIdentityManifest, ContentOrigin, EvaluationResult, IdentityCache, SignedAccessRequest, UnauthenticatedStrategy } from './types.js';
8
8
  import { verifyJwt } from './proof.js';
9
+ import { jwtVerify, createRemoteJWKSet } from 'jose';
9
10
 
10
11
  interface VerificationResult {
11
12
  allowed: boolean;
@@ -248,6 +249,24 @@ export class AAMPPublisher {
248
249
  if (requestHeader.purpose === AccessPurpose.CRAWL_TRAINING && !this.policy.allowTraining) {
249
250
  return { allowed: false, status: 403, reason: 'POLICY_DENIED: Training not allowed.', visitorType: 'VERIFIED_AGENT' };
250
251
  }
252
+
253
+ // 2. BROKER CHECK (New v1.1)
254
+ if (this.policy.monetization?.brokerUrl) {
255
+ const brokerUrl = this.policy.monetization.brokerUrl;
256
+
257
+ if (!paymentCredential) {
258
+ return { allowed: false, status: 402, reason: "PAYMENT_REQUIRED: Missing Broker Credential", visitorType: 'UNIDENTIFIED_BOT' };
259
+ }
260
+
261
+ const isValid = await this.verifyBrokerCred(paymentCredential, brokerUrl);
262
+
263
+ if (!isValid) {
264
+ return { allowed: false, status: 403, reason: "PAYMENT_DENIED: Invalid Broker Token", visitorType: 'UNIDENTIFIED_BOT' };
265
+ }
266
+
267
+ // If valid, we record the "Proof Used" so we can settle later
268
+ return { allowed: true, status: 200, reason: "AAMP_PAID", visitorType: "VERIFIED_AGENT", proofUsed: `BROKER_JWT:${paymentCredential.slice(0, 10)}...` };
269
+ }
251
270
  if (requestHeader.purpose === AccessPurpose.RAG_RETRIEVAL && !this.policy.allowRAG) {
252
271
  return { allowed: false, status: 403, reason: 'POLICY_DENIED: RAG not allowed.', visitorType: 'VERIFIED_AGENT' };
253
272
  }
@@ -352,7 +371,7 @@ export class AAMPPublisher {
352
371
  }
353
372
 
354
373
  // Return verified status so handleAgentStrict can proceed to Policy Check
355
- return { allowed: true, reason: 'OK', identityVerified: true };
374
+ return { allowed: true, reason: 'OK', identityVerified: identityVerified };
356
375
  }
357
376
 
358
377
  private async verifyDnsBinding(domain: string, requestKeySpki: string): Promise<boolean> {
@@ -398,6 +417,30 @@ export class AAMPPublisher {
398
417
  }
399
418
  }
400
419
 
420
+ /**
421
+ * NEW: Verify a Broker-Issued Token (JWT)
422
+ * Checks if the request contains a valid "Visa" from the Broker.
423
+ */
424
+ private async verifyBrokerCred(credential: string, brokerUrl: string): Promise<boolean> {
425
+ try {
426
+ // 1. Fetch Broker's Public Keys (JWKS)
427
+ const JWKS = createRemoteJWKSet(new URL(`${brokerUrl}/.well-known/jwks.json`));
428
+
429
+ // 2. Verify the Token Signature
430
+ const { payload } = await jwtVerify(credential, JWKS, {
431
+ issuer: brokerUrl, // Ensure it came from THE Broker
432
+ clockTolerance: 5 // Allow 5s clock skew
433
+ });
434
+
435
+ console.log(`[BROKER] 💰 Valid Payment Token from ${payload.iss} for amount ${payload.amount}`);
436
+ return true;
437
+
438
+ } catch (e: any) {
439
+ console.warn(`[BROKER] ❌ Invalid Token:`, e.message);
440
+ return false;
441
+ }
442
+ }
443
+
401
444
  private isDomain(s: string): boolean {
402
445
  // Basic regex, allows localhost with ports
403
446
  return /^[a-zA-Z0-9.-]+(:\d+)?$/.test(s) || /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(s);
package/src/types.ts CHANGED
@@ -62,7 +62,11 @@ export interface MonetizationConfig {
62
62
  issuer: string; // e.g. "https://accounts.google.com"
63
63
  };
64
64
 
65
- // Method 3: Payment Credentials (Unified JWT)
65
+ // Method 3: Broker Integration (NEW)
66
+ // For third-party clearing houses (AdSense for Data)
67
+ brokerUrl?: string; // e.g. "https://broker.aamp.network"
68
+
69
+ // Method 4: Payment Credentials (Unified JWT)
66
70
  // Verifies "x-aamp-credential" for Broker or Direct payments.
67
71
  paymentConfig?: {
68
72
  jwksUrl: string; // e.g. "https://my-site.com/.well-known/jwks.json"
Binary file