@agentic-passport/next 0.3.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/LICENSE +202 -0
- package/dist/clerk.d.ts +137 -0
- package/dist/clerk.d.ts.map +1 -0
- package/dist/clerk.js +110 -0
- package/dist/clerk.js.map +1 -0
- package/dist/handler.d.ts +71 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +516 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/is-agent-request.d.ts +17 -0
- package/dist/is-agent-request.d.ts.map +1 -0
- package/dist/is-agent-request.js +28 -0
- package/dist/is-agent-request.js.map +1 -0
- package/dist/next-adapter.d.ts +230 -0
- package/dist/next-adapter.d.ts.map +1 -0
- package/dist/next-adapter.js +270 -0
- package/dist/next-adapter.js.map +1 -0
- package/dist/stores.d.ts +35 -0
- package/dist/stores.d.ts.map +1 -0
- package/dist/stores.js +175 -0
- package/dist/stores.js.map +1 -0
- package/dist/types.d.ts +159 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js adapter for AgentPassport (works on Next 14, 15, and 16).
|
|
3
|
+
*
|
|
4
|
+
* Architecture: middleware NEVER imports crypto. It only sniffs headers and
|
|
5
|
+
* decides whether to bypass Clerk's redirect-to-signin. The actual protocol
|
|
6
|
+
* work (verifying signatures, minting tokens, etc.) happens in dedicated
|
|
7
|
+
* route handlers under `app/agent/*` and `app/.well-known/agent-passport`, which
|
|
8
|
+
* the vendor wires up with the factories below. Each of those routes
|
|
9
|
+
* declares `export const runtime = 'nodejs'` so node:crypto works freely.
|
|
10
|
+
*
|
|
11
|
+
* Usage in middleware.ts:
|
|
12
|
+
*
|
|
13
|
+
* import { isAgentRequest } from '@agentic-passport/next';
|
|
14
|
+
* import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
15
|
+
* import { NextResponse } from 'next/server';
|
|
16
|
+
*
|
|
17
|
+
* const clerk = clerkMiddleware();
|
|
18
|
+
* export default async function middleware(req, evt) {
|
|
19
|
+
* const headers = Object.fromEntries(req.headers);
|
|
20
|
+
* if (isAgentRequest({ headers })) return NextResponse.next();
|
|
21
|
+
* return clerk(req, evt);
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* That's the whole middleware. No imports from this package's handler.ts.
|
|
25
|
+
* Edge-safe.
|
|
26
|
+
*/
|
|
27
|
+
import type { AgentPassportInstance } from './handler.js';
|
|
28
|
+
/** @deprecated Use the global `Request` type. Kept as an alias for back-compat with 0.0.2. */
|
|
29
|
+
export type MinimalNextRequest = Request;
|
|
30
|
+
/**
|
|
31
|
+
* Canonical agent identity, derived from the verified handshake. Use this
|
|
32
|
+
* in `onAgentSignup` to provision the agent into your auth provider —
|
|
33
|
+
* vendors shouldn't be inventing emails, names, or metadata shapes for
|
|
34
|
+
* agents. The SDK returns the right fields.
|
|
35
|
+
*
|
|
36
|
+
* onAgentSignup: async (ctx) => {
|
|
37
|
+
* const identity = agentIdentity(ctx);
|
|
38
|
+
* const user = await clerkClient.users.createUser({
|
|
39
|
+
* ...identity, // externalId, emailAddress, firstName, lastName, publicMetadata
|
|
40
|
+
* skipPasswordRequirement: true,
|
|
41
|
+
* });
|
|
42
|
+
* return { allow: true, principal: { userId: user.id, grantedScopes: identity.grantedScopes } };
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* Email format: `agent-<safe-id>@agents.invalid` — RFC 6761-reserved TLD,
|
|
46
|
+
* never deliverable, no domain owner can claim it. When AgentPassport has a
|
|
47
|
+
* canonical brand domain this default will move; the shape stays the same.
|
|
48
|
+
*
|
|
49
|
+
* The `publicMetadata` shape is canonical across vendors so that a tool
|
|
50
|
+
* looking up an "agent user" in Clerk (or any auth provider) finds the
|
|
51
|
+
* same fields regardless of which vendor provisioned it. Filterable on
|
|
52
|
+
* `publicMetadata.agent === true`.
|
|
53
|
+
*/
|
|
54
|
+
import type { AgentSignupContext } from './types.js';
|
|
55
|
+
export type CanonicalAgentIdentity = {
|
|
56
|
+
externalId: string;
|
|
57
|
+
emailAddress: [string];
|
|
58
|
+
firstName: string;
|
|
59
|
+
lastName: string;
|
|
60
|
+
publicMetadata: {
|
|
61
|
+
agentpassport: {
|
|
62
|
+
schema: 'agentpassport/v1';
|
|
63
|
+
agentId: string;
|
|
64
|
+
actor: string | null;
|
|
65
|
+
task: string;
|
|
66
|
+
mandate: {
|
|
67
|
+
jti: string;
|
|
68
|
+
issuer: string;
|
|
69
|
+
expiresAt: number;
|
|
70
|
+
};
|
|
71
|
+
scopes: readonly string[];
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
/** Convenience: scopes the SDK has verified the agent can use. */
|
|
75
|
+
grantedScopes: string[];
|
|
76
|
+
};
|
|
77
|
+
export declare function agentIdentity(ctx: AgentSignupContext): CanonicalAgentIdentity;
|
|
78
|
+
/**
|
|
79
|
+
* Discovery doc handler.
|
|
80
|
+
*
|
|
81
|
+
* // app/.well-known/agent-passport/route.ts
|
|
82
|
+
* export const runtime = 'nodejs';
|
|
83
|
+
* export const dynamic = 'force-dynamic';
|
|
84
|
+
* export const GET = createDiscoveryHandler(getGate);
|
|
85
|
+
*
|
|
86
|
+
* Accepts either an AgentPassportInstance or a Promise/factory for one.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createDiscoveryHandler(gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>)): () => Promise<Response>;
|
|
89
|
+
/**
|
|
90
|
+
* Register handler.
|
|
91
|
+
*
|
|
92
|
+
* // app/agent/register/route.ts
|
|
93
|
+
* export const runtime = 'nodejs';
|
|
94
|
+
* export const dynamic = 'force-dynamic';
|
|
95
|
+
* export const POST = createRegisterHandler(getGate);
|
|
96
|
+
*/
|
|
97
|
+
export declare function createRegisterHandler(gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>)): (req: Request) => Promise<Response>;
|
|
98
|
+
/**
|
|
99
|
+
* Authorize handler.
|
|
100
|
+
*
|
|
101
|
+
* // app/agent/authorize/route.ts
|
|
102
|
+
* export const runtime = 'nodejs';
|
|
103
|
+
* export const dynamic = 'force-dynamic';
|
|
104
|
+
* export const POST = createAuthorizeHandler(getGate);
|
|
105
|
+
*/
|
|
106
|
+
export declare function createAuthorizeHandler(gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>)): (req: Request) => Promise<Response>;
|
|
107
|
+
/**
|
|
108
|
+
* Audit log handler. Gate behind your own auth in production (Clerk session,
|
|
109
|
+
* API key, etc.) — audit data is sensitive.
|
|
110
|
+
*
|
|
111
|
+
* // app/agent/audit/route.ts
|
|
112
|
+
* export const runtime = 'nodejs';
|
|
113
|
+
* export const dynamic = 'force-dynamic';
|
|
114
|
+
* export const GET = createAuditHandler(getGate);
|
|
115
|
+
*/
|
|
116
|
+
export declare function createAuditHandler(gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>)): () => Promise<Response>;
|
|
117
|
+
/**
|
|
118
|
+
* Catch-all dispatcher for all AgentPassport protocol endpoints.
|
|
119
|
+
*
|
|
120
|
+
* Mount this once and you don't need separate route files for register,
|
|
121
|
+
* authorize, audit, or the discovery doc. The gate routes internally based
|
|
122
|
+
* on the request URL.
|
|
123
|
+
*
|
|
124
|
+
* Two route files cover the four protocol surfaces:
|
|
125
|
+
*
|
|
126
|
+
* // app/agent/[...agentauth]/route.ts
|
|
127
|
+
* import { handleAgentPassport } from '@agentic-passport/next';
|
|
128
|
+
* import { getGate } from '@/lib/agent-gate';
|
|
129
|
+
* export const runtime = 'nodejs';
|
|
130
|
+
* export const dynamic = 'force-dynamic';
|
|
131
|
+
* export const GET = (r: Request) => handleAgentPassport(r, getGate);
|
|
132
|
+
* export const POST = (r: Request) => handleAgentPassport(r, getGate);
|
|
133
|
+
*
|
|
134
|
+
* // app/.well-known/agent-passport/route.ts
|
|
135
|
+
* export const runtime = 'nodejs';
|
|
136
|
+
* export const dynamic = 'force-dynamic';
|
|
137
|
+
* export const GET = (r: Request) => handleAgentPassport(r, getGate);
|
|
138
|
+
*
|
|
139
|
+
* (Next.js does not allow a single catch-all to cover both `/agent/*` and
|
|
140
|
+
* `/.well-known/agent-passport` — the well-known route lives outside `/agent`.
|
|
141
|
+
* Two files; one line each.)
|
|
142
|
+
*/
|
|
143
|
+
export declare function handleAgentPassport(req: Request, gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>)): Promise<Response>;
|
|
144
|
+
/**
|
|
145
|
+
* Wrap a vendor API route handler so it verifies the agent's signed request +
|
|
146
|
+
* DPoP-bound access token + required scope before running. The wrapped handler
|
|
147
|
+
* receives the verified principal as its second argument.
|
|
148
|
+
*
|
|
149
|
+
* // app/api/keyword-search/route.ts (or whatever the vendor's endpoint is)
|
|
150
|
+
* export const runtime = 'nodejs';
|
|
151
|
+
* export const dynamic = 'force-dynamic';
|
|
152
|
+
*
|
|
153
|
+
* export async function GET(req: NextRequest) {
|
|
154
|
+
* if (isAgentRequest({ headers: Object.fromEntries(req.headers) })) {
|
|
155
|
+
* return withAgent(getGate, 'keyword.search', async (r, principal) => {
|
|
156
|
+
* return Response.json(await searchKeywords(r, principal.agentId));
|
|
157
|
+
* })(req);
|
|
158
|
+
* }
|
|
159
|
+
* // Existing human flow stays exactly as-is:
|
|
160
|
+
* const { userId } = await auth();
|
|
161
|
+
* if (!userId) return new Response('unauthorized', { status: 401 });
|
|
162
|
+
* return Response.json(await searchKeywords(req, userId));
|
|
163
|
+
* }
|
|
164
|
+
*/
|
|
165
|
+
export declare function withAgent<T>(gateOrFactory: AgentPassportInstance | (() => Promise<AgentPassportInstance>), requiredScope: string, handler: (req: Request, principal: {
|
|
166
|
+
agentId: string;
|
|
167
|
+
scopes: readonly string[];
|
|
168
|
+
mandateJti: string;
|
|
169
|
+
}) => Promise<T>): (req: Request) => Promise<Response>;
|
|
170
|
+
/**
|
|
171
|
+
* Discriminated principal: agent (verified via AgentPassport) or human
|
|
172
|
+
* (verified via the vendor's own auth provider). Both carry a `userId`
|
|
173
|
+
* the vendor's business logic can branch on.
|
|
174
|
+
*
|
|
175
|
+
* The `mandateJti` and `scopes` fields only exist for agents — humans
|
|
176
|
+
* don't carry a mandate. Vendor route handlers should narrow on `type`
|
|
177
|
+
* before reading them.
|
|
178
|
+
*/
|
|
179
|
+
export type AgentSessionPrincipal = {
|
|
180
|
+
type: 'agent';
|
|
181
|
+
userId: string;
|
|
182
|
+
agentId: string;
|
|
183
|
+
scopes: readonly string[];
|
|
184
|
+
mandateJti: string;
|
|
185
|
+
};
|
|
186
|
+
export type HumanSessionPrincipal = {
|
|
187
|
+
type: 'human';
|
|
188
|
+
userId: string;
|
|
189
|
+
};
|
|
190
|
+
export type SessionPrincipal = AgentSessionPrincipal | HumanSessionPrincipal;
|
|
191
|
+
/**
|
|
192
|
+
* Resolve the request's principal — agent OR human OR none — without
|
|
193
|
+
* bridging cookies. This is the recommended way to write a vendor route
|
|
194
|
+
* that should be reachable by both.
|
|
195
|
+
*
|
|
196
|
+
* Behavior:
|
|
197
|
+
* 1. Agent path. If the request carries AgentPassport headers (signature
|
|
198
|
+
* + DPoP-bound token), verify them via `gate.authorizeApiCall` and
|
|
199
|
+
* return an `AgentSessionPrincipal`. Scope is verified iff
|
|
200
|
+
* `opts.requireScope` is provided.
|
|
201
|
+
* 2. Human path. If not an agent, and `opts.humanAuth` is provided,
|
|
202
|
+
* call it. If it returns `{ userId }`, return a `HumanSessionPrincipal`.
|
|
203
|
+
* 3. Otherwise return null.
|
|
204
|
+
*
|
|
205
|
+
* // app/api/account/route.ts
|
|
206
|
+
* import { getSession } from '@agentic-passport/next';
|
|
207
|
+
* import { auth } from '@clerk/nextjs/server';
|
|
208
|
+
*
|
|
209
|
+
* export async function GET(req: Request) {
|
|
210
|
+
* const principal = await getSession(req, {
|
|
211
|
+
* gate: getGate,
|
|
212
|
+
* humanAuth: async () => {
|
|
213
|
+
* const { userId } = await auth();
|
|
214
|
+
* return userId ? { userId } : null;
|
|
215
|
+
* },
|
|
216
|
+
* });
|
|
217
|
+
* if (!principal) return new Response('unauthorized', { status: 401 });
|
|
218
|
+
* // Same code path for agent and human. Branch only if you need to:
|
|
219
|
+
* // if (principal.type === 'agent') ...
|
|
220
|
+
* return Response.json({ user: principal.userId });
|
|
221
|
+
* }
|
|
222
|
+
*/
|
|
223
|
+
export declare function getSession(req: Request, opts: {
|
|
224
|
+
gate: AgentPassportInstance | (() => Promise<AgentPassportInstance>);
|
|
225
|
+
humanAuth?: () => Promise<{
|
|
226
|
+
userId: string;
|
|
227
|
+
} | null>;
|
|
228
|
+
requireScope?: string;
|
|
229
|
+
}): Promise<SessionPrincipal | null>;
|
|
230
|
+
//# sourceMappingURL=next-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-adapter.d.ts","sourceRoot":"","sources":["../src/next-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js adapter for AgentPassport (works on Next 14, 15, and 16).
|
|
3
|
+
*
|
|
4
|
+
* Architecture: middleware NEVER imports crypto. It only sniffs headers and
|
|
5
|
+
* decides whether to bypass Clerk's redirect-to-signin. The actual protocol
|
|
6
|
+
* work (verifying signatures, minting tokens, etc.) happens in dedicated
|
|
7
|
+
* route handlers under `app/agent/*` and `app/.well-known/agent-passport`, which
|
|
8
|
+
* the vendor wires up with the factories below. Each of those routes
|
|
9
|
+
* declares `export const runtime = 'nodejs'` so node:crypto works freely.
|
|
10
|
+
*
|
|
11
|
+
* Usage in middleware.ts:
|
|
12
|
+
*
|
|
13
|
+
* import { isAgentRequest } from '@agentic-passport/next';
|
|
14
|
+
* import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
15
|
+
* import { NextResponse } from 'next/server';
|
|
16
|
+
*
|
|
17
|
+
* const clerk = clerkMiddleware();
|
|
18
|
+
* export default async function middleware(req, evt) {
|
|
19
|
+
* const headers = Object.fromEntries(req.headers);
|
|
20
|
+
* if (isAgentRequest({ headers })) return NextResponse.next();
|
|
21
|
+
* return clerk(req, evt);
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* That's the whole middleware. No imports from this package's handler.ts.
|
|
25
|
+
* Edge-safe.
|
|
26
|
+
*/
|
|
27
|
+
async function toNormalized(req) {
|
|
28
|
+
const headers = {};
|
|
29
|
+
req.headers.forEach((v, k) => { headers[k.toLowerCase()] = v; });
|
|
30
|
+
const ab = await req.arrayBuffer();
|
|
31
|
+
// Buffer.from on a Uint8Array is fine on Node; on Edge there's no Buffer
|
|
32
|
+
// but this code path is never reached on Edge (only Node-runtime routes
|
|
33
|
+
// import this module).
|
|
34
|
+
return { method: req.method, url: req.url, headers, body: Buffer.from(ab) };
|
|
35
|
+
}
|
|
36
|
+
function toResponse(res) {
|
|
37
|
+
return new Response(new Uint8Array(res.body), { status: res.status, headers: res.headers });
|
|
38
|
+
}
|
|
39
|
+
// example.com is RFC 2606 reserved (never deliverable), AND passes
|
|
40
|
+
// auth providers' email-format validators (Clerk accepts; agents.invalid
|
|
41
|
+
// is rejected because .invalid is too obviously synthetic). When the SDK
|
|
42
|
+
// has its own canonical domain (agents.<brand>.dev or similar), this
|
|
43
|
+
// constant moves; the canonical shape doesn't.
|
|
44
|
+
const AGENTS_EMAIL_DOMAIN = 'example.com';
|
|
45
|
+
export function agentIdentity(ctx) {
|
|
46
|
+
const { agentId, mandate } = ctx;
|
|
47
|
+
const safe = agentId.replace(/[^a-zA-Z0-9._-]/g, '-').toLowerCase();
|
|
48
|
+
return {
|
|
49
|
+
externalId: `agent:${agentId}`,
|
|
50
|
+
emailAddress: [`agent-${safe}@${AGENTS_EMAIL_DOMAIN}`],
|
|
51
|
+
firstName: 'Agent',
|
|
52
|
+
lastName: agentId,
|
|
53
|
+
publicMetadata: {
|
|
54
|
+
agentpassport: {
|
|
55
|
+
schema: 'agentpassport/v1',
|
|
56
|
+
agentId,
|
|
57
|
+
actor: mandate.actor ?? null,
|
|
58
|
+
task: mandate.task,
|
|
59
|
+
mandate: {
|
|
60
|
+
jti: mandate.jti,
|
|
61
|
+
issuer: mandate.iss,
|
|
62
|
+
expiresAt: mandate.exp,
|
|
63
|
+
},
|
|
64
|
+
scopes: mandate.scope_hints,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
grantedScopes: [...mandate.scope_hints],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Discovery doc handler.
|
|
72
|
+
*
|
|
73
|
+
* // app/.well-known/agent-passport/route.ts
|
|
74
|
+
* export const runtime = 'nodejs';
|
|
75
|
+
* export const dynamic = 'force-dynamic';
|
|
76
|
+
* export const GET = createDiscoveryHandler(getGate);
|
|
77
|
+
*
|
|
78
|
+
* Accepts either an AgentPassportInstance or a Promise/factory for one.
|
|
79
|
+
*/
|
|
80
|
+
export function createDiscoveryHandler(gateOrFactory) {
|
|
81
|
+
return async () => {
|
|
82
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
83
|
+
return new Response(JSON.stringify(gate.discoveryDoc()), { headers: { 'content-type': 'application/json' } });
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Register handler.
|
|
88
|
+
*
|
|
89
|
+
* // app/agent/register/route.ts
|
|
90
|
+
* export const runtime = 'nodejs';
|
|
91
|
+
* export const dynamic = 'force-dynamic';
|
|
92
|
+
* export const POST = createRegisterHandler(getGate);
|
|
93
|
+
*/
|
|
94
|
+
export function createRegisterHandler(gateOrFactory) {
|
|
95
|
+
return async (req) => {
|
|
96
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
97
|
+
return toResponse(await gate.handle(await toNormalized(req)));
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Authorize handler.
|
|
102
|
+
*
|
|
103
|
+
* // app/agent/authorize/route.ts
|
|
104
|
+
* export const runtime = 'nodejs';
|
|
105
|
+
* export const dynamic = 'force-dynamic';
|
|
106
|
+
* export const POST = createAuthorizeHandler(getGate);
|
|
107
|
+
*/
|
|
108
|
+
export function createAuthorizeHandler(gateOrFactory) {
|
|
109
|
+
return async (req) => {
|
|
110
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
111
|
+
return toResponse(await gate.handle(await toNormalized(req)));
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Audit log handler. Gate behind your own auth in production (Clerk session,
|
|
116
|
+
* API key, etc.) — audit data is sensitive.
|
|
117
|
+
*
|
|
118
|
+
* // app/agent/audit/route.ts
|
|
119
|
+
* export const runtime = 'nodejs';
|
|
120
|
+
* export const dynamic = 'force-dynamic';
|
|
121
|
+
* export const GET = createAuditHandler(getGate);
|
|
122
|
+
*/
|
|
123
|
+
export function createAuditHandler(gateOrFactory) {
|
|
124
|
+
return async () => {
|
|
125
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
126
|
+
return new Response(JSON.stringify({ entries: gate.auditEntries() }), { headers: { 'content-type': 'application/json' } });
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Catch-all dispatcher for all AgentPassport protocol endpoints.
|
|
131
|
+
*
|
|
132
|
+
* Mount this once and you don't need separate route files for register,
|
|
133
|
+
* authorize, audit, or the discovery doc. The gate routes internally based
|
|
134
|
+
* on the request URL.
|
|
135
|
+
*
|
|
136
|
+
* Two route files cover the four protocol surfaces:
|
|
137
|
+
*
|
|
138
|
+
* // app/agent/[...agentauth]/route.ts
|
|
139
|
+
* import { handleAgentPassport } from '@agentic-passport/next';
|
|
140
|
+
* import { getGate } from '@/lib/agent-gate';
|
|
141
|
+
* export const runtime = 'nodejs';
|
|
142
|
+
* export const dynamic = 'force-dynamic';
|
|
143
|
+
* export const GET = (r: Request) => handleAgentPassport(r, getGate);
|
|
144
|
+
* export const POST = (r: Request) => handleAgentPassport(r, getGate);
|
|
145
|
+
*
|
|
146
|
+
* // app/.well-known/agent-passport/route.ts
|
|
147
|
+
* export const runtime = 'nodejs';
|
|
148
|
+
* export const dynamic = 'force-dynamic';
|
|
149
|
+
* export const GET = (r: Request) => handleAgentPassport(r, getGate);
|
|
150
|
+
*
|
|
151
|
+
* (Next.js does not allow a single catch-all to cover both `/agent/*` and
|
|
152
|
+
* `/.well-known/agent-passport` — the well-known route lives outside `/agent`.
|
|
153
|
+
* Two files; one line each.)
|
|
154
|
+
*/
|
|
155
|
+
export async function handleAgentPassport(req, gateOrFactory) {
|
|
156
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
157
|
+
return toResponse(await gate.handle(await toNormalized(req)));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Wrap a vendor API route handler so it verifies the agent's signed request +
|
|
161
|
+
* DPoP-bound access token + required scope before running. The wrapped handler
|
|
162
|
+
* receives the verified principal as its second argument.
|
|
163
|
+
*
|
|
164
|
+
* // app/api/keyword-search/route.ts (or whatever the vendor's endpoint is)
|
|
165
|
+
* export const runtime = 'nodejs';
|
|
166
|
+
* export const dynamic = 'force-dynamic';
|
|
167
|
+
*
|
|
168
|
+
* export async function GET(req: NextRequest) {
|
|
169
|
+
* if (isAgentRequest({ headers: Object.fromEntries(req.headers) })) {
|
|
170
|
+
* return withAgent(getGate, 'keyword.search', async (r, principal) => {
|
|
171
|
+
* return Response.json(await searchKeywords(r, principal.agentId));
|
|
172
|
+
* })(req);
|
|
173
|
+
* }
|
|
174
|
+
* // Existing human flow stays exactly as-is:
|
|
175
|
+
* const { userId } = await auth();
|
|
176
|
+
* if (!userId) return new Response('unauthorized', { status: 401 });
|
|
177
|
+
* return Response.json(await searchKeywords(req, userId));
|
|
178
|
+
* }
|
|
179
|
+
*/
|
|
180
|
+
export function withAgent(gateOrFactory, requiredScope, handler) {
|
|
181
|
+
return async (req) => {
|
|
182
|
+
const gate = typeof gateOrFactory === 'function' ? await gateOrFactory() : gateOrFactory;
|
|
183
|
+
const normalized = await toNormalized(req);
|
|
184
|
+
const result = await gate.authorizeApiCall(normalized, requiredScope);
|
|
185
|
+
if (!result.ok) {
|
|
186
|
+
const status = result.code === 'SCOPE_NOT_GRANTED' ? 403 : 401;
|
|
187
|
+
const headers = { 'content-type': 'application/json' };
|
|
188
|
+
if (status === 401) {
|
|
189
|
+
const origin = gate.config.vendorOrigin.replace(/\/$/, '');
|
|
190
|
+
headers['www-authenticate'] = [
|
|
191
|
+
'AgentPassport',
|
|
192
|
+
`realm="${origin}"`,
|
|
193
|
+
`discovery="${origin}/.well-known/agent-passport"`,
|
|
194
|
+
`quickstart="${origin}/.well-known/agent-passport/quickstart.md"`,
|
|
195
|
+
`spec="${origin}/.well-known/agent-passport/spec.md"`,
|
|
196
|
+
].join(' ');
|
|
197
|
+
}
|
|
198
|
+
return new Response(JSON.stringify({ error: 'agent_passport_error', error_code: result.code }), { status, headers });
|
|
199
|
+
}
|
|
200
|
+
const value = await handler(req, {
|
|
201
|
+
agentId: result.principal.agentId,
|
|
202
|
+
scopes: result.principal.scopes,
|
|
203
|
+
mandateJti: result.principal.mandateJti,
|
|
204
|
+
});
|
|
205
|
+
if (value instanceof Response)
|
|
206
|
+
return value;
|
|
207
|
+
return new Response(JSON.stringify(value), { headers: { 'content-type': 'application/json' } });
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Resolve the request's principal — agent OR human OR none — without
|
|
212
|
+
* bridging cookies. This is the recommended way to write a vendor route
|
|
213
|
+
* that should be reachable by both.
|
|
214
|
+
*
|
|
215
|
+
* Behavior:
|
|
216
|
+
* 1. Agent path. If the request carries AgentPassport headers (signature
|
|
217
|
+
* + DPoP-bound token), verify them via `gate.authorizeApiCall` and
|
|
218
|
+
* return an `AgentSessionPrincipal`. Scope is verified iff
|
|
219
|
+
* `opts.requireScope` is provided.
|
|
220
|
+
* 2. Human path. If not an agent, and `opts.humanAuth` is provided,
|
|
221
|
+
* call it. If it returns `{ userId }`, return a `HumanSessionPrincipal`.
|
|
222
|
+
* 3. Otherwise return null.
|
|
223
|
+
*
|
|
224
|
+
* // app/api/account/route.ts
|
|
225
|
+
* import { getSession } from '@agentic-passport/next';
|
|
226
|
+
* import { auth } from '@clerk/nextjs/server';
|
|
227
|
+
*
|
|
228
|
+
* export async function GET(req: Request) {
|
|
229
|
+
* const principal = await getSession(req, {
|
|
230
|
+
* gate: getGate,
|
|
231
|
+
* humanAuth: async () => {
|
|
232
|
+
* const { userId } = await auth();
|
|
233
|
+
* return userId ? { userId } : null;
|
|
234
|
+
* },
|
|
235
|
+
* });
|
|
236
|
+
* if (!principal) return new Response('unauthorized', { status: 401 });
|
|
237
|
+
* // Same code path for agent and human. Branch only if you need to:
|
|
238
|
+
* // if (principal.type === 'agent') ...
|
|
239
|
+
* return Response.json({ user: principal.userId });
|
|
240
|
+
* }
|
|
241
|
+
*/
|
|
242
|
+
export async function getSession(req, opts) {
|
|
243
|
+
const headersRecord = {};
|
|
244
|
+
req.headers.forEach((v, k) => { headersRecord[k.toLowerCase()] = v; });
|
|
245
|
+
const looksLikeAgent = !!(headersRecord['signature'] && headersRecord['signature-agent']);
|
|
246
|
+
if (looksLikeAgent) {
|
|
247
|
+
const gate = typeof opts.gate === 'function' ? await opts.gate() : opts.gate;
|
|
248
|
+
const normalized = await toNormalized(req);
|
|
249
|
+
const result = await gate.authorizeApiCall(normalized, opts.requireScope ?? null);
|
|
250
|
+
if (!result.ok)
|
|
251
|
+
return null;
|
|
252
|
+
// Resolve the agent's vendor-side userId from the gate's store.
|
|
253
|
+
const agentRecord = await gate.store.getAgent(result.principal.agentId);
|
|
254
|
+
const userId = agentRecord?.user_id ?? `shadow:${result.principal.agentId}`;
|
|
255
|
+
return {
|
|
256
|
+
type: 'agent',
|
|
257
|
+
userId,
|
|
258
|
+
agentId: result.principal.agentId,
|
|
259
|
+
scopes: result.principal.scopes,
|
|
260
|
+
mandateJti: result.principal.mandateJti,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (opts.humanAuth) {
|
|
264
|
+
const human = await opts.humanAuth();
|
|
265
|
+
if (human)
|
|
266
|
+
return { type: 'human', userId: human.userId };
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=next-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next-adapter.js","sourceRoot":"","sources":["../src/next-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/stores.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State store implementations.
|
|
3
|
+
* - inMemoryStore() — default; per-instance Map-backed; fine for tests + single-isolate dev
|
|
4
|
+
* - kvStore(kv) — Cloudflare Workers KV-backed; works across isolates
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentStateStore } from './types.js';
|
|
7
|
+
export declare function inMemoryStore(): AgentStateStore;
|
|
8
|
+
/** Minimal KV shape we depend on. Cloudflare's KVNamespace satisfies it. */
|
|
9
|
+
export interface MinimalKV {
|
|
10
|
+
get(key: string): Promise<string | null>;
|
|
11
|
+
put(key: string, value: string, opts?: {
|
|
12
|
+
expirationTtl?: number;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
type KvStoreOpts = {
|
|
16
|
+
/** Used for keys + agents + revocation list (persistent across requests). */
|
|
17
|
+
state: MinimalKV;
|
|
18
|
+
/** Used for nonces, mandate jtis, dpop jtis, rate-limit counters (short-lived). */
|
|
19
|
+
cache?: MinimalKV;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* KV-backed state store. Use on Cloudflare Workers / Pages.
|
|
23
|
+
*
|
|
24
|
+
* Two namespaces are recommended:
|
|
25
|
+
* - state: persistent (keys, agents, revocation)
|
|
26
|
+
* - cache: ephemeral (nonces, jtis, rate-limit) — falls back to `state` if omitted
|
|
27
|
+
*
|
|
28
|
+
* Limits to be aware of:
|
|
29
|
+
* - KV writes are eventually consistent (≤60s globally). v0 demo: same-region requests
|
|
30
|
+
* usually see fresh writes within ~1s. For multi-region production, consider Durable Objects.
|
|
31
|
+
* - Each operation = 1 KV op = real cost / rate limit.
|
|
32
|
+
*/
|
|
33
|
+
export declare function kvStore(opts: KvStoreOpts): AgentStateStore;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=stores.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../src/stores.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAM/D,wBAAgB,aAAa,IAAI,eAAe,CA+D/C;AAID,4EAA4E;AAC5E,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF;AAED,KAAK,WAAW,GAAG;IACjB,6EAA6E;IAC7E,KAAK,EAAE,SAAS,CAAC;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAYF;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CA0E1D"}
|