@aamp/protocol 1.1.2 → 1.1.3
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/agent.d.ts +7 -2
- package/dist/agent.js +15 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +4 -1
- package/dist/express.d.ts +3 -1
- package/dist/express.js +28 -36
- package/dist/nextjs.d.ts +3 -1
- package/dist/nextjs.js +15 -30
- package/dist/publisher.d.ts +20 -17
- package/dist/publisher.js +205 -39
- package/dist/types.d.ts +30 -17
- package/package.json +23 -23
- package/src/agent.ts +87 -71
- package/src/constants.ts +38 -34
- package/src/crypto.ts +68 -68
- package/src/express.ts +94 -103
- package/src/index.ts +12 -12
- package/src/nextjs.ts +91 -108
- package/src/publisher.ts +302 -106
- package/src/types.ts +112 -97
- package/test/handshake.spec.ts +62 -66
- package/tsconfig.json +14 -14
package/src/express.ts
CHANGED
|
@@ -1,104 +1,95 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 3: Framework Adapters
|
|
3
|
-
* Zero-friction integration for Express/Node.js.
|
|
4
|
-
*/
|
|
5
|
-
import { AAMPPublisher } from './publisher';
|
|
6
|
-
import { AccessPolicy, ContentOrigin,
|
|
7
|
-
import { generateKeyPair
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
discoveryHandler() {
|
|
99
|
-
return (req: any, res: any) => {
|
|
100
|
-
res.setHeader('Content-Type', 'application/json');
|
|
101
|
-
res.send(JSON.stringify(this.publisher.getPolicy(), null, 2));
|
|
102
|
-
};
|
|
103
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Layer 3: Framework Adapters
|
|
3
|
+
* Zero-friction integration for Express/Node.js.
|
|
4
|
+
*/
|
|
5
|
+
import { AAMPPublisher } from './publisher';
|
|
6
|
+
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
|
|
7
|
+
import { generateKeyPair } from './crypto';
|
|
8
|
+
|
|
9
|
+
export interface AAMPConfig {
|
|
10
|
+
policy: Omit<AccessPolicy, 'version'>;
|
|
11
|
+
meta: {
|
|
12
|
+
origin: keyof typeof ContentOrigin;
|
|
13
|
+
paymentPointer?: string;
|
|
14
|
+
};
|
|
15
|
+
strategy?: UnauthenticatedStrategy;
|
|
16
|
+
// Optional: Provide a Redis/Memcached adapter here for production
|
|
17
|
+
cache?: IdentityCache;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class AAMP {
|
|
21
|
+
private publisher: AAMPPublisher;
|
|
22
|
+
private origin: ContentOrigin;
|
|
23
|
+
private ready: Promise<void>;
|
|
24
|
+
|
|
25
|
+
private constructor(config: AAMPConfig) {
|
|
26
|
+
this.publisher = new AAMPPublisher(
|
|
27
|
+
{ version: '1.1', ...config.policy } as AccessPolicy,
|
|
28
|
+
config.strategy || 'PASSIVE',
|
|
29
|
+
config.cache
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
this.origin = ContentOrigin[config.meta.origin];
|
|
33
|
+
|
|
34
|
+
this.ready = generateKeyPair().then(keys => {
|
|
35
|
+
return this.publisher.initialize(keys);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static init(config: AAMPConfig): AAMP {
|
|
40
|
+
return new AAMP(config);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Express Middleware
|
|
45
|
+
*/
|
|
46
|
+
middleware() {
|
|
47
|
+
return async (req: any, res: any, next: any) => {
|
|
48
|
+
await this.ready;
|
|
49
|
+
|
|
50
|
+
// Normalize headers to lowercase dictionary
|
|
51
|
+
const headers: Record<string, string> = {};
|
|
52
|
+
Object.keys(req.headers).forEach(key => {
|
|
53
|
+
headers[key.toLowerCase()] = req.headers[key] as string;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Retrieve Raw Payload if available (optional but good for crypto)
|
|
57
|
+
// Note: Express body parsing might interfere, so we usually rely on the header content.
|
|
58
|
+
const rawPayload = headers['x-aamp-payload'];
|
|
59
|
+
|
|
60
|
+
// Evaluate Visitor
|
|
61
|
+
const result = await this.publisher.evaluateVisitor(headers, rawPayload);
|
|
62
|
+
|
|
63
|
+
// Enforce Decision
|
|
64
|
+
if (!result.allowed) {
|
|
65
|
+
res.status(result.status).json({
|
|
66
|
+
error: result.reason,
|
|
67
|
+
visitor_type: result.visitorType
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Inject Provenance Headers (For the humans/agents that got through)
|
|
73
|
+
const respHeaders = await this.publisher.generateResponseHeaders(this.origin);
|
|
74
|
+
Object.entries(respHeaders).forEach(([k, v]) => {
|
|
75
|
+
res.setHeader(k, v);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Attach metadata to request for downstream use
|
|
79
|
+
req.aamp = {
|
|
80
|
+
verified: result.visitorType === 'VERIFIED_AGENT',
|
|
81
|
+
type: result.visitorType,
|
|
82
|
+
...result.metadata
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
next();
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
discoveryHandler() {
|
|
90
|
+
return (req: any, res: any) => {
|
|
91
|
+
res.setHeader('Content-Type', 'application/json');
|
|
92
|
+
res.send(JSON.stringify(this.publisher.getPolicy(), null, 2));
|
|
93
|
+
};
|
|
94
|
+
}
|
|
104
95
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AAMP SDK Public API
|
|
3
|
-
*
|
|
4
|
-
* This is the main entry point for the library.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export * from './types';
|
|
8
|
-
export * from './constants';
|
|
9
|
-
export * from './agent';
|
|
10
|
-
export * from './publisher';
|
|
11
|
-
export * from './crypto';
|
|
12
|
-
export * from './express'; // Node.js / Express Adapter
|
|
1
|
+
/**
|
|
2
|
+
* AAMP SDK Public API
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for the library.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './types';
|
|
8
|
+
export * from './constants';
|
|
9
|
+
export * from './agent';
|
|
10
|
+
export * from './publisher';
|
|
11
|
+
export * from './crypto';
|
|
12
|
+
export * from './express'; // Node.js / Express Adapter
|
|
13
13
|
export { AAMPNext } from './nextjs'; // Serverless / Next.js Adapter
|
package/src/nextjs.ts
CHANGED
|
@@ -1,109 +1,92 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 3: Framework Adapters
|
|
3
|
-
* Serverless integration for Next.js (App Router & API Routes).
|
|
4
|
-
*/
|
|
5
|
-
import { AAMPPublisher } from './publisher';
|
|
6
|
-
import { AccessPolicy, ContentOrigin,
|
|
7
|
-
import { generateKeyPair } from './crypto';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// 3. Inject Provenance Headers (Passive)
|
|
93
|
-
const aampHeaders = await this.publisher.generateResponseHeaders(this.origin);
|
|
94
|
-
if (response && response.headers) {
|
|
95
|
-
Object.entries(aampHeaders).forEach(([k, v]) => {
|
|
96
|
-
response.headers.set(k, v);
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return response;
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
discoveryHandler() {
|
|
105
|
-
return async () => {
|
|
106
|
-
return createJsonResponse(this.publisher.getPolicy());
|
|
107
|
-
};
|
|
108
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Layer 3: Framework Adapters
|
|
3
|
+
* Serverless integration for Next.js (App Router & API Routes).
|
|
4
|
+
*/
|
|
5
|
+
import { AAMPPublisher } from './publisher';
|
|
6
|
+
import { AccessPolicy, ContentOrigin, UnauthenticatedStrategy, IdentityCache } from './types';
|
|
7
|
+
import { generateKeyPair } from './crypto';
|
|
8
|
+
|
|
9
|
+
type NextRequest = any;
|
|
10
|
+
type NextResponse = any;
|
|
11
|
+
|
|
12
|
+
const createJsonResponse = (body: any, status = 200) => {
|
|
13
|
+
return new Response(JSON.stringify(body), {
|
|
14
|
+
status,
|
|
15
|
+
headers: { 'Content-Type': 'application/json' }
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export interface AAMPConfig {
|
|
20
|
+
policy: Omit<AccessPolicy, 'version'>;
|
|
21
|
+
meta: {
|
|
22
|
+
origin: keyof typeof ContentOrigin;
|
|
23
|
+
paymentPointer?: string;
|
|
24
|
+
};
|
|
25
|
+
strategy?: UnauthenticatedStrategy;
|
|
26
|
+
// Optional: Provide a KV/Redis adapter here for production
|
|
27
|
+
cache?: IdentityCache;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class AAMPNext {
|
|
31
|
+
private publisher: AAMPPublisher;
|
|
32
|
+
private origin: ContentOrigin;
|
|
33
|
+
private ready: Promise<void>;
|
|
34
|
+
|
|
35
|
+
private constructor(config: AAMPConfig) {
|
|
36
|
+
this.publisher = new AAMPPublisher(
|
|
37
|
+
{ version: '1.1', ...config.policy } as AccessPolicy,
|
|
38
|
+
config.strategy || 'PASSIVE',
|
|
39
|
+
config.cache
|
|
40
|
+
);
|
|
41
|
+
this.origin = ContentOrigin[config.meta.origin];
|
|
42
|
+
this.ready = generateKeyPair().then(keys => this.publisher.initialize(keys));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static init(config: AAMPConfig): AAMPNext {
|
|
46
|
+
return new AAMPNext(config);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Serverless Route Wrapper
|
|
51
|
+
*/
|
|
52
|
+
withProtection(handler: (req: NextRequest) => Promise<NextResponse>) {
|
|
53
|
+
return async (req: NextRequest) => {
|
|
54
|
+
await this.ready;
|
|
55
|
+
|
|
56
|
+
// Extract Headers map
|
|
57
|
+
const headers: Record<string, string> = {};
|
|
58
|
+
req.headers.forEach((value: string, key: string) => {
|
|
59
|
+
headers[key.toLowerCase()] = value;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Evaluate
|
|
63
|
+
const result = await this.publisher.evaluateVisitor(headers, headers['x-aamp-payload']);
|
|
64
|
+
|
|
65
|
+
if (!result.allowed) {
|
|
66
|
+
return createJsonResponse({
|
|
67
|
+
error: result.reason,
|
|
68
|
+
visitor_type: result.visitorType
|
|
69
|
+
}, result.status);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Execute Handler
|
|
73
|
+
const response = await handler(req);
|
|
74
|
+
|
|
75
|
+
// Inject Provenance
|
|
76
|
+
const aampHeaders = await this.publisher.generateResponseHeaders(this.origin);
|
|
77
|
+
if (response && response.headers) {
|
|
78
|
+
Object.entries(aampHeaders).forEach(([k, v]) => {
|
|
79
|
+
response.headers.set(k, v);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return response;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
discoveryHandler() {
|
|
88
|
+
return async () => {
|
|
89
|
+
return createJsonResponse(this.publisher.getPolicy());
|
|
90
|
+
};
|
|
91
|
+
}
|
|
109
92
|
}
|