@agent-id/nextjs 0.1.0 → 0.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agent-id/nextjs
2
2
 
3
- Next.js middleware that automatically detects AI-agent traffic and requires a valid [AgentPass](https://agentpass.vercel.app) JWT. Human browser traffic always passes through untouched.
3
+ Next.js proxy that automatically detects AI-agent traffic and requires a valid [AgentID](https://agentidapp.vercel.app) JWT. Human browser traffic always passes through untouched.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,22 +8,22 @@ Next.js middleware that automatically detects AI-agent traffic and requires a va
8
8
  npm install @agent-id/nextjs
9
9
  ```
10
10
 
11
- Requires `next >= 14`.
11
+ Requires `next >= 16`.
12
12
 
13
13
  ## Setup — 2 steps
14
14
 
15
- ### 1. Add the middleware
15
+ ### 1. Add the proxy
16
16
 
17
- Create (or update) `middleware.ts` in your project root:
17
+ Create `proxy.ts` in your project root (same level as `app/`):
18
18
 
19
19
  ```ts
20
- import { createAgentPassMiddleware } from '@agent-id/nextjs';
20
+ import { createAgentIDMiddleware } from '@agent-id/nextjs';
21
21
  import type { NextRequest } from 'next/server';
22
22
 
23
- const agentPass = createAgentPassMiddleware();
23
+ const agentID = createAgentIDMiddleware();
24
24
 
25
- export function middleware(request: NextRequest) {
26
- return agentPass(request);
25
+ export function proxy(request: NextRequest) {
26
+ return agentID(request);
27
27
  }
28
28
 
29
29
  // Protect your API routes
@@ -32,20 +32,20 @@ export const config = {
32
32
  };
33
33
  ```
34
34
 
35
- That's it. The middleware now:
35
+ That's it. The proxy now:
36
36
  - Lets all human browser traffic through unchanged
37
- - Requires a valid AgentPass JWT from any AI agent / bot
37
+ - Requires a valid AgentID JWT from any AI agent / bot
38
38
  - Returns `403 AGENT_UNAUTHORIZED` when the JWT is missing or invalid
39
39
 
40
40
  ### 2. Read the verified identity in your route handlers (optional)
41
41
 
42
42
  ```ts
43
43
  // app/api/anything/route.ts
44
- import { getAgentPassResult } from '@agent-id/nextjs';
44
+ import { getAgentIDResult } from '@agent-id/nextjs';
45
45
  import type { NextRequest } from 'next/server';
46
46
 
47
47
  export async function GET(request: NextRequest) {
48
- const agent = getAgentPassResult(request);
48
+ const agent = getAgentIDResult(request);
49
49
 
50
50
  if (agent.verified) {
51
51
  // Verified AI agent — claims are fully typed
@@ -63,41 +63,41 @@ export async function GET(request: NextRequest) {
63
63
  Agents add one header to every request:
64
64
 
65
65
  ```
66
- Authorization: Bearer <agentpass-jwt>
66
+ Authorization: Bearer <agentid-jwt>
67
67
  ```
68
68
 
69
- The JWT is obtained by completing a BankID flow at [agentpass.vercel.app](https://agentpass.vercel.app). Tokens are valid for 1 hour.
69
+ The JWT is obtained by completing a BankID flow at [agentidapp.vercel.app](https://agentidapp.vercel.app). Tokens are valid for 1 hour.
70
70
 
71
71
  ## Options
72
72
 
73
- All options are optional — `createAgentPassMiddleware()` with no arguments works out of the box.
73
+ All options are optional — `createAgentIDMiddleware()` with no arguments works out of the box.
74
74
 
75
75
  ```ts
76
- createAgentPassMiddleware({
76
+ createAgentIDMiddleware({
77
77
  // Return 403 when an agent has no valid token (default: true).
78
78
  // Set to false to let unverified agents through (useful for logging / gradual rollout).
79
79
  blockUnauthorizedAgents: true,
80
80
 
81
- // Override the JWKS endpoint — only needed if you self-host AgentPass.
82
- jwksUrl: 'https://your-agentpass.example.com/api/jwks',
81
+ // Override the JWKS endpoint — only needed if you self-host AgentID.
82
+ jwksUrl: 'https://your-agentid.example.com/api/jwks',
83
83
 
84
84
  // Clock skew tolerance in seconds (default: 30).
85
85
  clockTolerance: 30,
86
86
 
87
87
  // Fully custom response when an agent is rejected.
88
88
  onUnauthorizedAgent: (request, reason) =>
89
- NextResponse.json({ error: 'No AgentPass token', reason }, { status: 403 }),
89
+ NextResponse.json({ error: 'No AgentID token', reason }, { status: 403 }),
90
90
  })
91
91
  ```
92
92
 
93
93
  ## What gets verified
94
94
 
95
- Verification is **fully offline** after the first request. The public key is fetched once from the AgentPass JWKS endpoint and cached for 1 hour — no per-request network call.
95
+ Verification is **fully offline** after the first request. The public key is fetched once from the AgentID JWKS endpoint and cached for 1 hour — no per-request network call.
96
96
 
97
97
  | Check | Requirement |
98
98
  |---|---|
99
99
  | Signature | RS256 — `alg:none` and HS256 are explicitly rejected |
100
- | Issuer (`iss`) | Must equal `"agentpass"` |
100
+ | Issuer (`iss`) | Must equal `"agentid"` |
101
101
  | Expiry (`exp`) | Must be in the future |
102
102
  | `auth_method` | Must equal `"bankid"` |
103
103
 
@@ -107,7 +107,7 @@ Verification is **fully offline** after the first request. The public key is fet
107
107
  |---|---|
108
108
  | `sub` | Pseudonymous stable user ID (HMAC-SHA256 of BankID personal number — non-reversible, same person always gets the same ID) |
109
109
  | `auth_method` | Always `"bankid"` |
110
- | `iss` | `"agentpass"` |
110
+ | `iss` | `"agentid"` |
111
111
  | `exp` | Unix timestamp — 1 hour from issue |
112
112
  | `iat` | Unix timestamp — when issued |
113
113
  | `jti` | Unique token ID |
package/dist/index.d.mts CHANGED
@@ -1,16 +1,16 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
2
 
3
3
  /**
4
- * JWT claims present in every AgentPass token.
4
+ * JWT claims present in every AgentID token.
5
5
  *
6
6
  * The `sub` field is a pseudonymous identifier derived via HMAC-SHA256 of the
7
7
  * user's BankID personal number. It is stable per person but non-reversible —
8
8
  * organisations cannot extract the personal number from it.
9
9
  */
10
- interface AgentPassClaims {
10
+ interface AgentIDClaims {
11
11
  /** Pseudonymous, stable per-user identifier (HMAC-SHA256 of personal number). */
12
12
  sub: string;
13
- /** Issuer — always "agentpass". */
13
+ /** Issuer — always "agentid". */
14
14
  iss: string;
15
15
  /** Issued-at Unix timestamp. */
16
16
  iat: number;
@@ -21,27 +21,27 @@ interface AgentPassClaims {
21
21
  /** Authentication method — always "bankid" in this version. */
22
22
  auth_method: "bankid";
23
23
  }
24
- interface AgentPassVerified {
24
+ interface AgentIDVerified {
25
25
  verified: true;
26
- claims: AgentPassClaims;
26
+ claims: AgentIDClaims;
27
27
  }
28
- interface AgentPassUnverified {
28
+ interface AgentIDUnverified {
29
29
  verified: false;
30
30
  /** Why verification was skipped or failed. */
31
31
  reason: "not_agent" | "no_token" | "invalid_token";
32
32
  }
33
- /** Result attached to every request processed by the AgentPass middleware. */
34
- type AgentPassResult = AgentPassVerified | AgentPassUnverified;
33
+ /** Result attached to every request processed by the AgentID middleware. */
34
+ type AgentIDResult = AgentIDVerified | AgentIDUnverified;
35
35
  interface VerifierOptions {
36
36
  /**
37
- * URL of the AgentPass JWKS endpoint.
37
+ * URL of the AgentID JWKS endpoint.
38
38
  * Must use HTTPS (except `localhost` / `127.0.0.1` in development).
39
39
  *
40
- * @default 'https://agentpass.vercel.app/api/jwks'
40
+ * @default 'https://agentidapp.vercel.app/api/jwks'
41
41
  */
42
42
  jwksUrl?: string;
43
43
  /**
44
- * Block agent requests that carry no valid AgentPass token.
44
+ * Block agent requests that carry no valid AgentID token.
45
45
  * When `false` the middleware still runs but sets an unverified result
46
46
  * on the request instead of returning 403.
47
47
  *
@@ -57,7 +57,7 @@ interface VerifierOptions {
57
57
  clockTolerance?: number;
58
58
  }
59
59
 
60
- type AgentPassMiddlewareOptions = VerifierOptions & {
60
+ type AgentIDMiddlewareOptions = VerifierOptions & {
61
61
  /**
62
62
  * Optional callback invoked when an agent is detected but carries no
63
63
  * valid token. Return a `NextResponse` to override the default 403.
@@ -65,47 +65,47 @@ type AgentPassMiddlewareOptions = VerifierOptions & {
65
65
  onUnauthorizedAgent?: (request: NextRequest, reason: string) => NextResponse | void;
66
66
  };
67
67
  /**
68
- * Factory that returns a Next.js middleware function which detects AI-agent
69
- * traffic and enforces AgentPass JWT authentication.
68
+ * Factory that returns a Next.js proxy function which detects AI-agent
69
+ * traffic and enforces AgentID JWT authentication.
70
70
  *
71
71
  * @example
72
72
  * ```ts
73
- * // middleware.ts (project root)
74
- * import { createAgentPassMiddleware } from '@agent-id/nextjs';
73
+ * // proxy.ts (project root)
74
+ * import { createAgentIDMiddleware } from '@agent-id/nextjs';
75
75
  *
76
- * const agentPass = createAgentPassMiddleware({
76
+ * const agentID = createAgentIDMiddleware({
77
77
  * blockUnauthorizedAgents: true,
78
78
  * });
79
79
  *
80
- * export function middleware(request: NextRequest) {
81
- * return agentPass(request);
80
+ * export function proxy(request: NextRequest) {
81
+ * return agentID(request);
82
82
  * }
83
83
  *
84
84
  * export const config = { matcher: '/api/:path*' };
85
85
  * ```
86
86
  */
87
- declare function createAgentPassMiddleware(options?: AgentPassMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
87
+ declare function createAgentIDMiddleware(options?: AgentIDMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
88
88
 
89
89
  interface VerifyTokenOptions {
90
90
  jwksUrl?: string;
91
91
  clockTolerance?: number;
92
92
  }
93
93
  /**
94
- * Verify an AgentPass JWT and return its decoded claims.
94
+ * Verify an AgentID JWT and return its decoded claims.
95
95
  *
96
96
  * Security guarantees
97
97
  * ───────────────────
98
98
  * • Signature — RS256, verified against the live JWKS public key.
99
99
  * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are
100
100
  * rejected before signature verification even begins.
101
- * • Issuer — must be exactly "agentpass".
101
+ * • Issuer — must be exactly "agentid".
102
102
  * • Expiry — enforced; configurable clock tolerance (default 30 s).
103
103
  * • kid — jose matches the JWT `kid` header to the JWKS automatically.
104
104
  * • auth_method — validated at runtime; must equal "bankid".
105
105
  *
106
106
  * @throws if the token is invalid, expired, or fails any check.
107
107
  */
108
- declare function verifyAgentPassToken(token: string, options?: VerifyTokenOptions): Promise<AgentPassClaims>;
108
+ declare function verifyAgentIDToken(token: string, options?: VerifyTokenOptions): Promise<AgentIDClaims>;
109
109
 
110
110
  /**
111
111
  * Bot / AI-agent detection for Next.js (Edge Runtime compatible).
@@ -128,36 +128,36 @@ declare function verifyAgentPassToken(token: string, options?: VerifyTokenOption
128
128
  */
129
129
  declare function isAgentRequest(headers: Headers): boolean;
130
130
  /**
131
- * Extract the AgentPass JWT from request headers.
131
+ * Extract the AgentID JWT from request headers.
132
132
  *
133
133
  * Checks in order:
134
134
  * 1. `Authorization: Bearer <token>` (preferred)
135
- * 2. `X-AgentPass-Token` (fallback when Authorization is stripped by proxies)
135
+ * 2. `X-AgentID-Token` (fallback when Authorization is stripped by proxies)
136
136
  *
137
137
  * @returns The raw JWT string, or `null` if absent.
138
138
  */
139
139
  declare function extractToken(headers: Headers): string | null;
140
140
 
141
141
  /**
142
- * Extract the verified AgentPass identity from a Next.js App Router request.
142
+ * Extract the verified AgentID identity from a Next.js App Router request.
143
143
  *
144
- * This function reads the headers set by `createAgentPassMiddleware`. It must
145
- * only be called from route handlers that sit behind the middleware.
144
+ * This function reads the headers set by `createAgentIDMiddleware`. It must
145
+ * only be called from route handlers that sit behind the proxy.
146
146
  *
147
147
  * @example
148
148
  * ```ts
149
149
  * // app/api/data/route.ts
150
- * import { getAgentPassResult } from '@agent-id/nextjs';
150
+ * import { getAgentIDResult } from '@agent-id/nextjs';
151
151
  *
152
152
  * export async function GET(request: NextRequest) {
153
- * const agentPass = getAgentPassResult(request);
154
- * if (!agentPass.verified) {
153
+ * const agent = getAgentIDResult(request);
154
+ * if (!agent.verified) {
155
155
  * return Response.json({ error: 'Unauthorized' }, { status: 403 });
156
156
  * }
157
- * return Response.json({ sub: agentPass.claims.sub });
157
+ * return Response.json({ sub: agent.claims.sub });
158
158
  * }
159
159
  * ```
160
160
  */
161
- declare function getAgentPassResult(request: NextRequest): AgentPassResult;
161
+ declare function getAgentIDResult(request: NextRequest): AgentIDResult;
162
162
 
163
- export { type AgentPassClaims, type AgentPassMiddlewareOptions, type AgentPassResult, type AgentPassUnverified, type AgentPassVerified, type VerifierOptions, type VerifyTokenOptions, createAgentPassMiddleware, extractToken, getAgentPassResult, isAgentRequest, verifyAgentPassToken };
163
+ export { type AgentIDClaims, type AgentIDMiddlewareOptions, type AgentIDResult, type AgentIDUnverified, type AgentIDVerified, type VerifierOptions, type VerifyTokenOptions, createAgentIDMiddleware, extractToken, getAgentIDResult, isAgentRequest, verifyAgentIDToken };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,16 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
2
 
3
3
  /**
4
- * JWT claims present in every AgentPass token.
4
+ * JWT claims present in every AgentID token.
5
5
  *
6
6
  * The `sub` field is a pseudonymous identifier derived via HMAC-SHA256 of the
7
7
  * user's BankID personal number. It is stable per person but non-reversible —
8
8
  * organisations cannot extract the personal number from it.
9
9
  */
10
- interface AgentPassClaims {
10
+ interface AgentIDClaims {
11
11
  /** Pseudonymous, stable per-user identifier (HMAC-SHA256 of personal number). */
12
12
  sub: string;
13
- /** Issuer — always "agentpass". */
13
+ /** Issuer — always "agentid". */
14
14
  iss: string;
15
15
  /** Issued-at Unix timestamp. */
16
16
  iat: number;
@@ -21,27 +21,27 @@ interface AgentPassClaims {
21
21
  /** Authentication method — always "bankid" in this version. */
22
22
  auth_method: "bankid";
23
23
  }
24
- interface AgentPassVerified {
24
+ interface AgentIDVerified {
25
25
  verified: true;
26
- claims: AgentPassClaims;
26
+ claims: AgentIDClaims;
27
27
  }
28
- interface AgentPassUnverified {
28
+ interface AgentIDUnverified {
29
29
  verified: false;
30
30
  /** Why verification was skipped or failed. */
31
31
  reason: "not_agent" | "no_token" | "invalid_token";
32
32
  }
33
- /** Result attached to every request processed by the AgentPass middleware. */
34
- type AgentPassResult = AgentPassVerified | AgentPassUnverified;
33
+ /** Result attached to every request processed by the AgentID middleware. */
34
+ type AgentIDResult = AgentIDVerified | AgentIDUnverified;
35
35
  interface VerifierOptions {
36
36
  /**
37
- * URL of the AgentPass JWKS endpoint.
37
+ * URL of the AgentID JWKS endpoint.
38
38
  * Must use HTTPS (except `localhost` / `127.0.0.1` in development).
39
39
  *
40
- * @default 'https://agentpass.vercel.app/api/jwks'
40
+ * @default 'https://agentidapp.vercel.app/api/jwks'
41
41
  */
42
42
  jwksUrl?: string;
43
43
  /**
44
- * Block agent requests that carry no valid AgentPass token.
44
+ * Block agent requests that carry no valid AgentID token.
45
45
  * When `false` the middleware still runs but sets an unverified result
46
46
  * on the request instead of returning 403.
47
47
  *
@@ -57,7 +57,7 @@ interface VerifierOptions {
57
57
  clockTolerance?: number;
58
58
  }
59
59
 
60
- type AgentPassMiddlewareOptions = VerifierOptions & {
60
+ type AgentIDMiddlewareOptions = VerifierOptions & {
61
61
  /**
62
62
  * Optional callback invoked when an agent is detected but carries no
63
63
  * valid token. Return a `NextResponse` to override the default 403.
@@ -65,47 +65,47 @@ type AgentPassMiddlewareOptions = VerifierOptions & {
65
65
  onUnauthorizedAgent?: (request: NextRequest, reason: string) => NextResponse | void;
66
66
  };
67
67
  /**
68
- * Factory that returns a Next.js middleware function which detects AI-agent
69
- * traffic and enforces AgentPass JWT authentication.
68
+ * Factory that returns a Next.js proxy function which detects AI-agent
69
+ * traffic and enforces AgentID JWT authentication.
70
70
  *
71
71
  * @example
72
72
  * ```ts
73
- * // middleware.ts (project root)
74
- * import { createAgentPassMiddleware } from '@agent-id/nextjs';
73
+ * // proxy.ts (project root)
74
+ * import { createAgentIDMiddleware } from '@agent-id/nextjs';
75
75
  *
76
- * const agentPass = createAgentPassMiddleware({
76
+ * const agentID = createAgentIDMiddleware({
77
77
  * blockUnauthorizedAgents: true,
78
78
  * });
79
79
  *
80
- * export function middleware(request: NextRequest) {
81
- * return agentPass(request);
80
+ * export function proxy(request: NextRequest) {
81
+ * return agentID(request);
82
82
  * }
83
83
  *
84
84
  * export const config = { matcher: '/api/:path*' };
85
85
  * ```
86
86
  */
87
- declare function createAgentPassMiddleware(options?: AgentPassMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
87
+ declare function createAgentIDMiddleware(options?: AgentIDMiddlewareOptions): (request: NextRequest) => Promise<NextResponse>;
88
88
 
89
89
  interface VerifyTokenOptions {
90
90
  jwksUrl?: string;
91
91
  clockTolerance?: number;
92
92
  }
93
93
  /**
94
- * Verify an AgentPass JWT and return its decoded claims.
94
+ * Verify an AgentID JWT and return its decoded claims.
95
95
  *
96
96
  * Security guarantees
97
97
  * ───────────────────
98
98
  * • Signature — RS256, verified against the live JWKS public key.
99
99
  * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are
100
100
  * rejected before signature verification even begins.
101
- * • Issuer — must be exactly "agentpass".
101
+ * • Issuer — must be exactly "agentid".
102
102
  * • Expiry — enforced; configurable clock tolerance (default 30 s).
103
103
  * • kid — jose matches the JWT `kid` header to the JWKS automatically.
104
104
  * • auth_method — validated at runtime; must equal "bankid".
105
105
  *
106
106
  * @throws if the token is invalid, expired, or fails any check.
107
107
  */
108
- declare function verifyAgentPassToken(token: string, options?: VerifyTokenOptions): Promise<AgentPassClaims>;
108
+ declare function verifyAgentIDToken(token: string, options?: VerifyTokenOptions): Promise<AgentIDClaims>;
109
109
 
110
110
  /**
111
111
  * Bot / AI-agent detection for Next.js (Edge Runtime compatible).
@@ -128,36 +128,36 @@ declare function verifyAgentPassToken(token: string, options?: VerifyTokenOption
128
128
  */
129
129
  declare function isAgentRequest(headers: Headers): boolean;
130
130
  /**
131
- * Extract the AgentPass JWT from request headers.
131
+ * Extract the AgentID JWT from request headers.
132
132
  *
133
133
  * Checks in order:
134
134
  * 1. `Authorization: Bearer <token>` (preferred)
135
- * 2. `X-AgentPass-Token` (fallback when Authorization is stripped by proxies)
135
+ * 2. `X-AgentID-Token` (fallback when Authorization is stripped by proxies)
136
136
  *
137
137
  * @returns The raw JWT string, or `null` if absent.
138
138
  */
139
139
  declare function extractToken(headers: Headers): string | null;
140
140
 
141
141
  /**
142
- * Extract the verified AgentPass identity from a Next.js App Router request.
142
+ * Extract the verified AgentID identity from a Next.js App Router request.
143
143
  *
144
- * This function reads the headers set by `createAgentPassMiddleware`. It must
145
- * only be called from route handlers that sit behind the middleware.
144
+ * This function reads the headers set by `createAgentIDMiddleware`. It must
145
+ * only be called from route handlers that sit behind the proxy.
146
146
  *
147
147
  * @example
148
148
  * ```ts
149
149
  * // app/api/data/route.ts
150
- * import { getAgentPassResult } from '@agent-id/nextjs';
150
+ * import { getAgentIDResult } from '@agent-id/nextjs';
151
151
  *
152
152
  * export async function GET(request: NextRequest) {
153
- * const agentPass = getAgentPassResult(request);
154
- * if (!agentPass.verified) {
153
+ * const agent = getAgentIDResult(request);
154
+ * if (!agent.verified) {
155
155
  * return Response.json({ error: 'Unauthorized' }, { status: 403 });
156
156
  * }
157
- * return Response.json({ sub: agentPass.claims.sub });
157
+ * return Response.json({ sub: agent.claims.sub });
158
158
  * }
159
159
  * ```
160
160
  */
161
- declare function getAgentPassResult(request: NextRequest): AgentPassResult;
161
+ declare function getAgentIDResult(request: NextRequest): AgentIDResult;
162
162
 
163
- export { type AgentPassClaims, type AgentPassMiddlewareOptions, type AgentPassResult, type AgentPassUnverified, type AgentPassVerified, type VerifierOptions, type VerifyTokenOptions, createAgentPassMiddleware, extractToken, getAgentPassResult, isAgentRequest, verifyAgentPassToken };
163
+ export { type AgentIDClaims, type AgentIDMiddlewareOptions, type AgentIDResult, type AgentIDUnverified, type AgentIDVerified, type VerifierOptions, type VerifyTokenOptions, createAgentIDMiddleware, extractToken, getAgentIDResult, isAgentRequest, verifyAgentIDToken };
package/dist/index.js CHANGED
@@ -20,11 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- createAgentPassMiddleware: () => createAgentPassMiddleware,
23
+ createAgentIDMiddleware: () => createAgentIDMiddleware,
24
24
  extractToken: () => extractToken,
25
- getAgentPassResult: () => getAgentPassResult,
25
+ getAgentIDResult: () => getAgentIDResult,
26
26
  isAgentRequest: () => isAgentRequest,
27
- verifyAgentPassToken: () => verifyAgentPassToken
27
+ verifyAgentIDToken: () => verifyAgentIDToken
28
28
  });
29
29
  module.exports = __toCommonJS(index_exports);
30
30
 
@@ -41,6 +41,7 @@ var BOT_UA_PATTERNS = [
41
41
  /ClaudeBot/i,
42
42
  /Claude-Web/i,
43
43
  /anthropic-ai/i,
44
+ /Claude-User/i,
44
45
  // Google
45
46
  /Googlebot/i,
46
47
  /Google-Extended/i,
@@ -66,9 +67,9 @@ var BOT_UA_PATTERNS = [
66
67
  /\bspider\b/i,
67
68
  /\bscraper\b/i,
68
69
  /\bfetcher\b/i,
69
- // MCP / AgentPass clients
70
+ // MCP / AgentID clients
70
71
  /mcp-client/i,
71
- /agentpass-client/i
72
+ /agentid-client/i
72
73
  ];
73
74
  var BROWSER_UA_RE = /Mozilla\/5\.0/i;
74
75
  function isAgentRequest(headers) {
@@ -92,14 +93,14 @@ function extractToken(headers) {
92
93
  const token = auth.slice(7).trim();
93
94
  if (token) return token;
94
95
  }
95
- const custom = headers.get("x-agentpass-token")?.trim();
96
+ const custom = headers.get("x-agentid-token")?.trim();
96
97
  if (custom) return custom;
97
98
  return null;
98
99
  }
99
100
 
100
101
  // src/verify.ts
101
102
  var import_jose = require("jose");
102
- var DEFAULT_JWKS_URL = "https://agentpass.vercel.app/api/jwks";
103
+ var DEFAULT_JWKS_URL = "https://agentidapp.vercel.app/api/jwks";
103
104
  var jwksSets = /* @__PURE__ */ new Map();
104
105
  function getJwks(rawUrl) {
105
106
  if (!jwksSets.has(rawUrl)) {
@@ -120,11 +121,11 @@ function getJwks(rawUrl) {
120
121
  }
121
122
  return jwksSets.get(rawUrl);
122
123
  }
123
- async function verifyAgentPassToken(token, options = {}) {
124
+ async function verifyAgentIDToken(token, options = {}) {
124
125
  const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;
125
126
  const JWKS = getJwks(jwksUrl);
126
127
  const { payload } = await (0, import_jose.jwtVerify)(token, JWKS, {
127
- issuer: "agentpass",
128
+ issuer: "agentid",
128
129
  // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.
129
130
  // Any token claiming alg:"none", alg:"HS256", or anything else is
130
131
  // rejected before signature verification.
@@ -144,18 +145,18 @@ async function verifyAgentPassToken(token, options = {}) {
144
145
 
145
146
  // src/middleware.ts
146
147
  var MANAGED_HEADERS = [
147
- "x-agentpass-verified",
148
- "x-agentpass-sub",
149
- "x-agentpass-claims"
148
+ "x-agentid-verified",
149
+ "x-agentid-sub",
150
+ "x-agentid-claims"
150
151
  ];
151
- function createAgentPassMiddleware(options = {}) {
152
+ function createAgentIDMiddleware(options = {}) {
152
153
  const {
153
154
  jwksUrl,
154
155
  blockUnauthorizedAgents = true,
155
156
  clockTolerance = 30,
156
157
  onUnauthorizedAgent
157
158
  } = options;
158
- return async function agentPassMiddleware(request) {
159
+ return async function agentIDMiddleware(request) {
159
160
  const requestHeaders = new Headers(request.headers);
160
161
  for (const name of MANAGED_HEADERS) {
161
162
  requestHeaders.delete(name);
@@ -168,14 +169,14 @@ function createAgentPassMiddleware(options = {}) {
168
169
  return unauthorized(
169
170
  request,
170
171
  requestHeaders,
171
- 'Agent request missing AgentPass token. Provide "Authorization: Bearer <token>".',
172
+ 'Agent request missing AgentID token. Provide "Authorization: Bearer <token>".',
172
173
  blockUnauthorizedAgents,
173
174
  onUnauthorizedAgent
174
175
  );
175
176
  }
176
177
  let claims;
177
178
  try {
178
- claims = await verifyAgentPassToken(token, {
179
+ claims = await verifyAgentIDToken(token, {
179
180
  ...jwksUrl !== void 0 && { jwksUrl },
180
181
  clockTolerance
181
182
  });
@@ -187,14 +188,14 @@ function createAgentPassMiddleware(options = {}) {
187
188
  return unauthorized(
188
189
  request,
189
190
  requestHeaders,
190
- "Invalid or expired AgentPass token.",
191
+ "Invalid or expired AgentID token.",
191
192
  blockUnauthorizedAgents,
192
193
  onUnauthorizedAgent
193
194
  );
194
195
  }
195
- requestHeaders.set("x-agentpass-verified", "true");
196
- requestHeaders.set("x-agentpass-sub", claims.sub);
197
- requestHeaders.set("x-agentpass-claims", JSON.stringify(claims));
196
+ requestHeaders.set("x-agentid-verified", "true");
197
+ requestHeaders.set("x-agentid-sub", claims.sub);
198
+ requestHeaders.set("x-agentid-claims", JSON.stringify(claims));
198
199
  return import_server.NextResponse.next({ request: { headers: requestHeaders } });
199
200
  };
200
201
  }
@@ -209,20 +210,20 @@ function unauthorized(request, requestHeaders, message, block, onUnauthorized) {
209
210
  { status: 403 }
210
211
  );
211
212
  }
212
- requestHeaders.set("x-agentpass-verified", "false");
213
+ requestHeaders.set("x-agentid-verified", "false");
213
214
  return import_server.NextResponse.next({ request: { headers: requestHeaders } });
214
215
  }
215
216
 
216
217
  // src/index.ts
217
- function getAgentPassResult(request) {
218
- const verified = request.headers.get("x-agentpass-verified");
218
+ function getAgentIDResult(request) {
219
+ const verified = request.headers.get("x-agentid-verified");
219
220
  if (verified !== "true") {
220
221
  return {
221
222
  verified: false,
222
223
  reason: verified === "false" ? "no_token" : "not_agent"
223
224
  };
224
225
  }
225
- const claimsJson = request.headers.get("x-agentpass-claims");
226
+ const claimsJson = request.headers.get("x-agentid-claims");
226
227
  if (!claimsJson) {
227
228
  return { verified: false, reason: "invalid_token" };
228
229
  }
@@ -235,10 +236,10 @@ function getAgentPassResult(request) {
235
236
  }
236
237
  // Annotate the CommonJS export names for ESM import in node:
237
238
  0 && (module.exports = {
238
- createAgentPassMiddleware,
239
+ createAgentIDMiddleware,
239
240
  extractToken,
240
- getAgentPassResult,
241
+ getAgentIDResult,
241
242
  isAgentRequest,
242
- verifyAgentPassToken
243
+ verifyAgentIDToken
243
244
  });
244
245
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/detect.ts","../src/verify.ts"],"sourcesContent":["export { createAgentPassMiddleware } from \"./middleware.js\";\nexport type { AgentPassMiddlewareOptions } from \"./middleware.js\";\n\nexport { verifyAgentPassToken } from \"./verify.js\";\nexport type { VerifyTokenOptions } from \"./verify.js\";\n\nexport { isAgentRequest, extractToken } from \"./detect.js\";\n\nexport type {\n AgentPassClaims,\n AgentPassResult,\n AgentPassVerified,\n AgentPassUnverified,\n VerifierOptions,\n} from \"./types.js\";\n\n// ── App Router helper ────────────────────────────────────────────────────────\n\nimport type { NextRequest } from \"next/server\";\nimport type { AgentPassResult, AgentPassClaims } from \"./types.js\";\n\n/**\n * Extract the verified AgentPass identity from a Next.js App Router request.\n *\n * This function reads the headers set by `createAgentPassMiddleware`. It must\n * only be called from route handlers that sit behind the middleware.\n *\n * @example\n * ```ts\n * // app/api/data/route.ts\n * import { getAgentPassResult } from '@agent-id/nextjs';\n *\n * export async function GET(request: NextRequest) {\n * const agentPass = getAgentPassResult(request);\n * if (!agentPass.verified) {\n * return Response.json({ error: 'Unauthorized' }, { status: 403 });\n * }\n * return Response.json({ sub: agentPass.claims.sub });\n * }\n * ```\n */\nexport function getAgentPassResult(request: NextRequest): AgentPassResult {\n const verified = request.headers.get(\"x-agentpass-verified\");\n\n if (verified !== \"true\") {\n return {\n verified: false,\n reason: verified === \"false\" ? \"no_token\" : \"not_agent\",\n };\n }\n\n const claimsJson = request.headers.get(\"x-agentpass-claims\");\n if (!claimsJson) {\n return { verified: false, reason: \"invalid_token\" };\n }\n\n try {\n const claims = JSON.parse(claimsJson) as AgentPassClaims;\n return { verified: true, claims };\n } catch {\n return { verified: false, reason: \"invalid_token\" };\n }\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { isAgentRequest, extractToken } from \"./detect.js\";\nimport { verifyAgentPassToken } from \"./verify.js\";\nimport type { VerifierOptions, AgentPassClaims } from \"./types.js\";\n\nexport type AgentPassMiddlewareOptions = VerifierOptions & {\n /**\n * Optional callback invoked when an agent is detected but carries no\n * valid token. Return a `NextResponse` to override the default 403.\n */\n onUnauthorizedAgent?: (\n request: NextRequest,\n reason: string\n ) => NextResponse | void;\n};\n\n/**\n * Headers injected by this middleware into downstream route handlers.\n * They are stripped from the *incoming* request first to prevent spoofing.\n */\nconst MANAGED_HEADERS = [\n \"x-agentpass-verified\",\n \"x-agentpass-sub\",\n \"x-agentpass-claims\",\n] as const;\n\n/**\n * Factory that returns a Next.js middleware function which detects AI-agent\n * traffic and enforces AgentPass JWT authentication.\n *\n * @example\n * ```ts\n * // middleware.ts (project root)\n * import { createAgentPassMiddleware } from '@agent-id/nextjs';\n *\n * const agentPass = createAgentPassMiddleware({\n * blockUnauthorizedAgents: true,\n * });\n *\n * export function middleware(request: NextRequest) {\n * return agentPass(request);\n * }\n *\n * export const config = { matcher: '/api/:path*' };\n * ```\n */\nexport function createAgentPassMiddleware(\n options: AgentPassMiddlewareOptions = {}\n) {\n const {\n jwksUrl,\n blockUnauthorizedAgents = true,\n clockTolerance = 30,\n onUnauthorizedAgent,\n } = options;\n\n return async function agentPassMiddleware(\n request: NextRequest\n ): Promise<NextResponse> {\n // Clone incoming headers so we can safely mutate them.\n const requestHeaders = new Headers(request.headers);\n\n // ── Security: strip client-supplied AgentPass headers ──────────────────\n // Without this, a malicious agent could send x-agentpass-verified: true\n // and bypass the check in route handlers.\n for (const name of MANAGED_HEADERS) {\n requestHeaders.delete(name);\n }\n\n // ── Non-agent traffic ──────────────────────────────────────────────────\n if (!isAgentRequest(requestHeaders)) {\n return NextResponse.next({ request: { headers: requestHeaders } });\n }\n\n // ── Agent detected — require a valid JWT ───────────────────────────────\n const token = extractToken(requestHeaders);\n\n if (!token) {\n return unauthorized(\n request,\n requestHeaders,\n 'Agent request missing AgentPass token. Provide \"Authorization: Bearer <token>\".',\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verify the JWT ─────────────────────────────────────────────────────\n let claims: AgentPassClaims;\n try {\n claims = await verifyAgentPassToken(token, {\n ...(jwksUrl !== undefined && { jwksUrl }),\n clockTolerance,\n });\n } catch (err) {\n // Never surface the raw error to the caller — it might leak internals.\n console.warn(\n \"[agent-id] Token verification failed:\",\n err instanceof Error ? err.message : String(err)\n );\n return unauthorized(\n request,\n requestHeaders,\n \"Invalid or expired AgentPass token.\",\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verified — inject identity into request headers ────────────────────\n // Route handlers read these via getAgentPassResult(request).\n requestHeaders.set(\"x-agentpass-verified\", \"true\");\n requestHeaders.set(\"x-agentpass-sub\", claims.sub);\n // Full claims encoded as JSON for type-safe extraction by helpers.\n requestHeaders.set(\"x-agentpass-claims\", JSON.stringify(claims));\n\n return NextResponse.next({ request: { headers: requestHeaders } });\n };\n}\n\n// ── Internal helper ──────────────────────────────────────────────────────────\n\nfunction unauthorized(\n request: NextRequest,\n requestHeaders: Headers,\n message: string,\n block: boolean,\n onUnauthorized: AgentPassMiddlewareOptions[\"onUnauthorizedAgent\"]\n): NextResponse {\n if (onUnauthorized) {\n const custom = onUnauthorized(request, message);\n if (custom) return custom;\n }\n\n if (block) {\n return NextResponse.json(\n { error: \"AGENT_UNAUTHORIZED\", message },\n { status: 403 }\n );\n }\n\n // Pass through with a \"not verified\" marker so route handlers can still\n // distinguish agent traffic from human traffic.\n requestHeaders.set(\"x-agentpass-verified\", \"false\");\n return NextResponse.next({ request: { headers: requestHeaders } });\n}\n","/**\n * Bot / AI-agent detection for Next.js (Edge Runtime compatible).\n *\n * Uses layered heuristics:\n * 1. Explicit bot / AI-agent User-Agent strings\n * 2. Cloudflare Bot Management score (if header is present)\n * 3. Absence of browser signals (no Accept-Language + non-browser UA)\n *\n * The detector is intentionally conservative — when in doubt it lets the\n * request through (fail open). A false negative (undetected bot) means the\n * request passes without a JWT check. A false positive (human flagged as bot)\n * would block legitimate traffic, which is much worse.\n */\n\nconst BOT_UA_PATTERNS: RegExp[] = [\n // OpenAI\n /GPTBot/i,\n /ChatGPT-User/i,\n /OAI-SearchBot/i,\n // Anthropic / Claude\n /ClaudeBot/i,\n /Claude-Web/i,\n /anthropic-ai/i,\n // Google\n /Googlebot/i,\n /Google-Extended/i,\n /AdsBot-Google/i,\n // Microsoft / Bing\n /bingbot/i,\n /msnbot/i,\n // AI search engines\n /PerplexityBot/i,\n /YouBot/i,\n // Common HTTP automation libraries\n /python-requests/i,\n /node-fetch/i,\n /\\baxios\\b/i,\n /\\bgot\\b\\//i,\n /\\bundici\\b/i,\n /\\bcurl\\b/i,\n /\\bwget\\b/i,\n /\\bhttpie\\b/i,\n // Generic crawler signals (word-boundary matched to reduce false positives)\n /\\bbot\\b/i,\n /\\bcrawler\\b/i,\n /\\bspider\\b/i,\n /\\bscraper\\b/i,\n /\\bfetcher\\b/i,\n // MCP / AgentPass clients\n /mcp-client/i,\n /agentpass-client/i,\n];\n\n// Every major browser includes \"Mozilla/5.0\" — its absence is a strong signal\nconst BROWSER_UA_RE = /Mozilla\\/5\\.0/i;\n\n/**\n * Returns `true` if the request appears to originate from an automated\n * agent or bot rather than a human browser.\n *\n * @param headers - The `Headers` object from a Next.js `NextRequest`\n */\nexport function isAgentRequest(headers: Headers): boolean {\n const ua = headers.get(\"user-agent\") ?? \"\";\n\n // No User-Agent → definitely automated\n if (!ua) return true;\n\n // Explicit bot / agent UA match\n if (BOT_UA_PATTERNS.some((p) => p.test(ua))) return true;\n\n // Cloudflare Bot Management: the `cf-bot-management` header contains a\n // JSON blob with a `score` field (0–100). Score < 30 → highly likely bot.\n const cfRaw = headers.get(\"cf-bot-management\");\n if (cfRaw) {\n try {\n const parsed = JSON.parse(cfRaw) as { score?: unknown };\n if (typeof parsed.score === \"number\" && parsed.score < 30) return true;\n } catch {\n // Malformed header — fail open (pass through)\n }\n }\n\n // Heuristic: non-browser UA + no Accept-Language → likely automated\n if (!BROWSER_UA_RE.test(ua) && !headers.get(\"accept-language\")) return true;\n\n return false;\n}\n\n/**\n * Extract the AgentPass JWT from request headers.\n *\n * Checks in order:\n * 1. `Authorization: Bearer <token>` (preferred)\n * 2. `X-AgentPass-Token` (fallback when Authorization is stripped by proxies)\n *\n * @returns The raw JWT string, or `null` if absent.\n */\nexport function extractToken(headers: Headers): string | null {\n // Primary: standard Authorization header\n const auth = headers.get(\"authorization\") ?? \"\";\n if (auth.startsWith(\"Bearer \")) {\n const token = auth.slice(7).trim();\n if (token) return token;\n }\n\n // Fallback: custom header\n const custom = headers.get(\"x-agentpass-token\")?.trim();\n if (custom) return custom;\n\n return null;\n}\n","import { createRemoteJWKSet, jwtVerify } from \"jose\";\nimport type { AgentPassClaims } from \"./types.js\";\n\nconst DEFAULT_JWKS_URL = \"https://agentpass.vercel.app/api/jwks\";\n\n/**\n * Module-level JWKS cache — one RemoteJWKSet instance per unique URL.\n * jose caches the fetched key material internally; re-fetches when the\n * cache TTL (1 h) expires or a new `kid` is seen.\n */\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nfunction getJwks(rawUrl: string): ReturnType<typeof createRemoteJWKSet> {\n if (!jwksSets.has(rawUrl)) {\n const url = new URL(rawUrl); // throws on malformed URL\n\n // Security: HTTPS is mandatory to prevent MITM on the public-key fetch.\n // Localhost is whitelisted for local development / CI.\n const isLocal =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\";\n\n if (url.protocol !== \"https:\" && !isLocal) {\n throw new Error(\n `[agent-id] jwksUrl must use HTTPS, received: ${rawUrl}`\n );\n }\n\n jwksSets.set(\n rawUrl,\n createRemoteJWKSet(url, {\n cacheMaxAge: 60 * 60 * 1_000, // 1 hour in ms\n })\n );\n }\n\n return jwksSets.get(rawUrl)!;\n}\n\nexport interface VerifyTokenOptions {\n jwksUrl?: string;\n clockTolerance?: number;\n}\n\n/**\n * Verify an AgentPass JWT and return its decoded claims.\n *\n * Security guarantees\n * ───────────────────\n * • Signature — RS256, verified against the live JWKS public key.\n * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are\n * rejected before signature verification even begins.\n * • Issuer — must be exactly \"agentpass\".\n * • Expiry — enforced; configurable clock tolerance (default 30 s).\n * • kid — jose matches the JWT `kid` header to the JWKS automatically.\n * • auth_method — validated at runtime; must equal \"bankid\".\n *\n * @throws if the token is invalid, expired, or fails any check.\n */\nexport async function verifyAgentPassToken(\n token: string,\n options: VerifyTokenOptions = {}\n): Promise<AgentPassClaims> {\n const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;\n const JWKS = getJwks(jwksUrl);\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: \"agentpass\",\n // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.\n // Any token claiming alg:\"none\", alg:\"HS256\", or anything else is\n // rejected before signature verification.\n algorithms: [\"RS256\"],\n clockTolerance: options.clockTolerance ?? 30,\n });\n\n // Runtime validation of AgentPass-specific claims.\n if (payload.auth_method !== \"bankid\") {\n throw new Error(\n `[agent-id] JWT has invalid auth_method: expected \"bankid\", got \"${\n payload.auth_method ?? \"undefined\"\n }\"`\n );\n }\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0) {\n throw new Error(\"[agent-id] JWT is missing the sub claim\");\n }\n\n return payload as unknown as AgentPassClaims;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA0C;;;ACc1C,IAAM,kBAA4B;AAAA;AAAA,EAEhC;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AACF;AAGA,IAAM,gBAAgB;AAQf,SAAS,eAAe,SAA2B;AACxD,QAAM,KAAK,QAAQ,IAAI,YAAY,KAAK;AAGxC,MAAI,CAAC,GAAI,QAAO;AAGhB,MAAI,gBAAgB,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAG,QAAO;AAIpD,QAAM,QAAQ,QAAQ,IAAI,mBAAmB;AAC7C,MAAI,OAAO;AACT,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,UAAI,OAAO,OAAO,UAAU,YAAY,OAAO,QAAQ,GAAI,QAAO;AAAA,IACpE,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,MAAI,CAAC,cAAc,KAAK,EAAE,KAAK,CAAC,QAAQ,IAAI,iBAAiB,EAAG,QAAO;AAEvE,SAAO;AACT;AAWO,SAAS,aAAa,SAAiC;AAE5D,QAAM,OAAO,QAAQ,IAAI,eAAe,KAAK;AAC7C,MAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,UAAM,QAAQ,KAAK,MAAM,CAAC,EAAE,KAAK;AACjC,QAAI,MAAO,QAAO;AAAA,EACpB;AAGA,QAAM,SAAS,QAAQ,IAAI,mBAAmB,GAAG,KAAK;AACtD,MAAI,OAAQ,QAAO;AAEnB,SAAO;AACT;;;AC/GA,kBAA8C;AAG9C,IAAM,mBAAmB;AAOzB,IAAM,WAAW,oBAAI,IAAmD;AAExE,SAAS,QAAQ,QAAuD;AACtE,MAAI,CAAC,SAAS,IAAI,MAAM,GAAG;AACzB,UAAM,MAAM,IAAI,IAAI,MAAM;AAI1B,UAAM,UACJ,IAAI,aAAa,eACjB,IAAI,aAAa,eACjB,IAAI,aAAa;AAEnB,QAAI,IAAI,aAAa,YAAY,CAAC,SAAS;AACzC,YAAM,IAAI;AAAA,QACR,gDAAgD,MAAM;AAAA,MACxD;AAAA,IACF;AAEA,aAAS;AAAA,MACP;AAAA,UACA,gCAAmB,KAAK;AAAA,QACtB,aAAa,KAAK,KAAK;AAAA;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,SAAS,IAAI,MAAM;AAC5B;AAsBA,eAAsB,qBACpB,OACA,UAA8B,CAAC,GACL;AAC1B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,OAAO,QAAQ,OAAO;AAE5B,QAAM,EAAE,QAAQ,IAAI,UAAM,uBAAU,OAAO,MAAM;AAAA,IAC/C,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIR,YAAY,CAAC,OAAO;AAAA,IACpB,gBAAgB,QAAQ,kBAAkB;AAAA,EAC5C,CAAC;AAGD,MAAI,QAAQ,gBAAgB,UAAU;AACpC,UAAM,IAAI;AAAA,MACR,mEACE,QAAQ,eAAe,WACzB;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,QAAQ,YAAY,QAAQ,IAAI,WAAW,GAAG;AAC/D,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,SAAO;AACT;;;AFrEA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF;AAsBO,SAAS,0BACd,UAAsC,CAAC,GACvC;AACA,QAAM;AAAA,IACJ;AAAA,IACA,0BAA0B;AAAA,IAC1B,iBAAiB;AAAA,IACjB;AAAA,EACF,IAAI;AAEJ,SAAO,eAAe,oBACpB,SACuB;AAEvB,UAAM,iBAAiB,IAAI,QAAQ,QAAQ,OAAO;AAKlD,eAAW,QAAQ,iBAAiB;AAClC,qBAAe,OAAO,IAAI;AAAA,IAC5B;AAGA,QAAI,CAAC,eAAe,cAAc,GAAG;AACnC,aAAO,2BAAa,KAAK,EAAE,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;AAAA,IACnE;AAGA,UAAM,QAAQ,aAAa,cAAc;AAEzC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,eAAS,MAAM,qBAAqB,OAAO;AAAA,QACzC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,QACvC;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAK;AAEZ,cAAQ;AAAA,QACN;AAAA,QACA,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACjD;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAIA,mBAAe,IAAI,wBAAwB,MAAM;AACjD,mBAAe,IAAI,mBAAmB,OAAO,GAAG;AAEhD,mBAAe,IAAI,sBAAsB,KAAK,UAAU,MAAM,CAAC;AAE/D,WAAO,2BAAa,KAAK,EAAE,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;AAAA,EACnE;AACF;AAIA,SAAS,aACP,SACA,gBACA,SACA,OACA,gBACc;AACd,MAAI,gBAAgB;AAClB,UAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,QAAI,OAAQ,QAAO;AAAA,EACrB;AAEA,MAAI,OAAO;AACT,WAAO,2BAAa;AAAA,MAClB,EAAE,OAAO,sBAAsB,QAAQ;AAAA,MACvC,EAAE,QAAQ,IAAI;AAAA,IAChB;AAAA,EACF;AAIA,iBAAe,IAAI,wBAAwB,OAAO;AAClD,SAAO,2BAAa,KAAK,EAAE,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;AACnE;;;ADxGO,SAAS,mBAAmB,SAAuC;AACxE,QAAM,WAAW,QAAQ,QAAQ,IAAI,sBAAsB;AAE3D,MAAI,aAAa,QAAQ;AACvB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,aAAa,UAAU,aAAa;AAAA,IAC9C;AAAA,EACF;AAEA,QAAM,aAAa,QAAQ,QAAQ,IAAI,oBAAoB;AAC3D,MAAI,CAAC,YAAY;AACf,WAAO,EAAE,UAAU,OAAO,QAAQ,gBAAgB;AAAA,EACpD;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,WAAO,EAAE,UAAU,MAAM,OAAO;AAAA,EAClC,QAAQ;AACN,WAAO,EAAE,UAAU,OAAO,QAAQ,gBAAgB;AAAA,EACpD;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/middleware.ts","../src/detect.ts","../src/verify.ts"],"sourcesContent":["export { createAgentIDMiddleware } from \"./middleware.js\";\nexport type { AgentIDMiddlewareOptions } from \"./middleware.js\";\n\nexport { verifyAgentIDToken } from \"./verify.js\";\nexport type { VerifyTokenOptions } from \"./verify.js\";\n\nexport { isAgentRequest, extractToken } from \"./detect.js\";\n\nexport type {\n AgentIDClaims,\n AgentIDResult,\n AgentIDVerified,\n AgentIDUnverified,\n VerifierOptions,\n} from \"./types.js\";\n\n// ── App Router helper ────────────────────────────────────────────────────────\n\nimport type { NextRequest } from \"next/server\";\nimport type { AgentIDResult, AgentIDClaims } from \"./types.js\";\n\n/**\n * Extract the verified AgentID identity from a Next.js App Router request.\n *\n * This function reads the headers set by `createAgentIDMiddleware`. It must\n * only be called from route handlers that sit behind the proxy.\n *\n * @example\n * ```ts\n * // app/api/data/route.ts\n * import { getAgentIDResult } from '@agent-id/nextjs';\n *\n * export async function GET(request: NextRequest) {\n * const agent = getAgentIDResult(request);\n * if (!agent.verified) {\n * return Response.json({ error: 'Unauthorized' }, { status: 403 });\n * }\n * return Response.json({ sub: agent.claims.sub });\n * }\n * ```\n */\nexport function getAgentIDResult(request: NextRequest): AgentIDResult {\n const verified = request.headers.get(\"x-agentid-verified\");\n\n if (verified !== \"true\") {\n return {\n verified: false,\n reason: verified === \"false\" ? \"no_token\" : \"not_agent\",\n };\n }\n\n const claimsJson = request.headers.get(\"x-agentid-claims\");\n if (!claimsJson) {\n return { verified: false, reason: \"invalid_token\" };\n }\n\n try {\n const claims = JSON.parse(claimsJson) as AgentIDClaims;\n return { verified: true, claims };\n } catch {\n return { verified: false, reason: \"invalid_token\" };\n }\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { isAgentRequest, extractToken } from \"./detect.js\";\nimport { verifyAgentIDToken } from \"./verify.js\";\nimport type { VerifierOptions, AgentIDClaims } from \"./types.js\";\n\nexport type AgentIDMiddlewareOptions = VerifierOptions & {\n /**\n * Optional callback invoked when an agent is detected but carries no\n * valid token. Return a `NextResponse` to override the default 403.\n */\n onUnauthorizedAgent?: (\n request: NextRequest,\n reason: string\n ) => NextResponse | void;\n};\n\n/**\n * Headers injected by this proxy into downstream route handlers.\n * They are stripped from the *incoming* request first to prevent spoofing.\n */\nconst MANAGED_HEADERS = [\n \"x-agentid-verified\",\n \"x-agentid-sub\",\n \"x-agentid-claims\",\n] as const;\n\n/**\n * Factory that returns a Next.js proxy function which detects AI-agent\n * traffic and enforces AgentID JWT authentication.\n *\n * @example\n * ```ts\n * // proxy.ts (project root)\n * import { createAgentIDMiddleware } from '@agent-id/nextjs';\n *\n * const agentID = createAgentIDMiddleware({\n * blockUnauthorizedAgents: true,\n * });\n *\n * export function proxy(request: NextRequest) {\n * return agentID(request);\n * }\n *\n * export const config = { matcher: '/api/:path*' };\n * ```\n */\nexport function createAgentIDMiddleware(\n options: AgentIDMiddlewareOptions = {}\n) {\n const {\n jwksUrl,\n blockUnauthorizedAgents = true,\n clockTolerance = 30,\n onUnauthorizedAgent,\n } = options;\n\n return async function agentIDMiddleware(\n request: NextRequest\n ): Promise<NextResponse> {\n // Clone incoming headers so we can safely mutate them.\n const requestHeaders = new Headers(request.headers);\n\n // ── Security: strip client-supplied AgentID headers ──────────────────\n // Without this, a malicious agent could send x-agentid-verified: true\n // and bypass the check in route handlers.\n for (const name of MANAGED_HEADERS) {\n requestHeaders.delete(name);\n }\n\n // ── Non-agent traffic ──────────────────────────────────────────────────\n if (!isAgentRequest(requestHeaders)) {\n return NextResponse.next({ request: { headers: requestHeaders } });\n }\n\n // ── Agent detected — require a valid JWT ───────────────────────────────\n const token = extractToken(requestHeaders);\n\n if (!token) {\n return unauthorized(\n request,\n requestHeaders,\n 'Agent request missing AgentID token. Provide \"Authorization: Bearer <token>\".',\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verify the JWT ─────────────────────────────────────────────────────\n let claims: AgentIDClaims;\n try {\n claims = await verifyAgentIDToken(token, {\n ...(jwksUrl !== undefined && { jwksUrl }),\n clockTolerance,\n });\n } catch (err) {\n // Never surface the raw error to the caller — it might leak internals.\n console.warn(\n \"[agent-id] Token verification failed:\",\n err instanceof Error ? err.message : String(err)\n );\n return unauthorized(\n request,\n requestHeaders,\n \"Invalid or expired AgentID token.\",\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verified — inject identity into request headers ────────────────────\n // Route handlers read these via getAgentIDResult(request).\n requestHeaders.set(\"x-agentid-verified\", \"true\");\n requestHeaders.set(\"x-agentid-sub\", claims.sub);\n // Full claims encoded as JSON for type-safe extraction by helpers.\n requestHeaders.set(\"x-agentid-claims\", JSON.stringify(claims));\n\n return NextResponse.next({ request: { headers: requestHeaders } });\n };\n}\n\n// ── Internal helper ──────────────────────────────────────────────────────────\n\nfunction unauthorized(\n request: NextRequest,\n requestHeaders: Headers,\n message: string,\n block: boolean,\n onUnauthorized: AgentIDMiddlewareOptions[\"onUnauthorizedAgent\"]\n): NextResponse {\n if (onUnauthorized) {\n const custom = onUnauthorized(request, message);\n if (custom) return custom;\n }\n\n if (block) {\n return NextResponse.json(\n { error: \"AGENT_UNAUTHORIZED\", message },\n { status: 403 }\n );\n }\n\n // Pass through with a \"not verified\" marker so route handlers can still\n // distinguish agent traffic from human traffic.\n requestHeaders.set(\"x-agentid-verified\", \"false\");\n return NextResponse.next({ request: { headers: requestHeaders } });\n}\n","/**\n * Bot / AI-agent detection for Next.js (Edge Runtime compatible).\n *\n * Uses layered heuristics:\n * 1. Explicit bot / AI-agent User-Agent strings\n * 2. Cloudflare Bot Management score (if header is present)\n * 3. Absence of browser signals (no Accept-Language + non-browser UA)\n *\n * The detector is intentionally conservative — when in doubt it lets the\n * request through (fail open). A false negative (undetected bot) means the\n * request passes without a JWT check. A false positive (human flagged as bot)\n * would block legitimate traffic, which is much worse.\n */\n\nconst BOT_UA_PATTERNS: RegExp[] = [\n // OpenAI\n /GPTBot/i,\n /ChatGPT-User/i,\n /OAI-SearchBot/i,\n // Anthropic / Claude\n /ClaudeBot/i,\n /Claude-Web/i,\n /anthropic-ai/i,\n /Claude-User/i,\n // Google\n /Googlebot/i,\n /Google-Extended/i,\n /AdsBot-Google/i,\n // Microsoft / Bing\n /bingbot/i,\n /msnbot/i,\n // AI search engines\n /PerplexityBot/i,\n /YouBot/i,\n // Common HTTP automation libraries\n /python-requests/i,\n /node-fetch/i,\n /\\baxios\\b/i,\n /\\bgot\\b\\//i,\n /\\bundici\\b/i,\n /\\bcurl\\b/i,\n /\\bwget\\b/i,\n /\\bhttpie\\b/i,\n // Generic crawler signals (word-boundary matched to reduce false positives)\n /\\bbot\\b/i,\n /\\bcrawler\\b/i,\n /\\bspider\\b/i,\n /\\bscraper\\b/i,\n /\\bfetcher\\b/i,\n // MCP / AgentID clients\n /mcp-client/i,\n /agentid-client/i,\n];\n\n// Every major browser includes \"Mozilla/5.0\" — its absence is a strong signal\nconst BROWSER_UA_RE = /Mozilla\\/5\\.0/i;\n\n/**\n * Returns `true` if the request appears to originate from an automated\n * agent or bot rather than a human browser.\n *\n * @param headers - The `Headers` object from a Next.js `NextRequest`\n */\nexport function isAgentRequest(headers: Headers): boolean {\n const ua = headers.get(\"user-agent\") ?? \"\";\n\n // No User-Agent → definitely automated\n if (!ua) return true;\n\n // Explicit bot / agent UA match\n if (BOT_UA_PATTERNS.some((p) => p.test(ua))) return true;\n\n // Cloudflare Bot Management: the `cf-bot-management` header contains a\n // JSON blob with a `score` field (0–100). Score < 30 → highly likely bot.\n const cfRaw = headers.get(\"cf-bot-management\");\n if (cfRaw) {\n try {\n const parsed = JSON.parse(cfRaw) as { score?: unknown };\n if (typeof parsed.score === \"number\" && parsed.score < 30) return true;\n } catch {\n // Malformed header — fail open (pass through)\n }\n }\n\n // Heuristic: non-browser UA + no Accept-Language → likely automated\n if (!BROWSER_UA_RE.test(ua) && !headers.get(\"accept-language\")) return true;\n\n return false;\n}\n\n/**\n * Extract the AgentID JWT from request headers.\n *\n * Checks in order:\n * 1. `Authorization: Bearer <token>` (preferred)\n * 2. `X-AgentID-Token` (fallback when Authorization is stripped by proxies)\n *\n * @returns The raw JWT string, or `null` if absent.\n */\nexport function extractToken(headers: Headers): string | null {\n // Primary: standard Authorization header\n const auth = headers.get(\"authorization\") ?? \"\";\n if (auth.startsWith(\"Bearer \")) {\n const token = auth.slice(7).trim();\n if (token) return token;\n }\n\n // Fallback: custom header\n const custom = headers.get(\"x-agentid-token\")?.trim();\n if (custom) return custom;\n\n return null;\n}\n","import { createRemoteJWKSet, jwtVerify } from \"jose\";\nimport type { AgentIDClaims } from \"./types.js\";\n\nconst DEFAULT_JWKS_URL = \"https://agentidapp.vercel.app/api/jwks\";\n\n/**\n * Module-level JWKS cache — one RemoteJWKSet instance per unique URL.\n * jose caches the fetched key material internally; re-fetches when the\n * cache TTL (1 h) expires or a new `kid` is seen.\n */\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nfunction getJwks(rawUrl: string): ReturnType<typeof createRemoteJWKSet> {\n if (!jwksSets.has(rawUrl)) {\n const url = new URL(rawUrl); // throws on malformed URL\n\n // Security: HTTPS is mandatory to prevent MITM on the public-key fetch.\n // Localhost is whitelisted for local development / CI.\n const isLocal =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\";\n\n if (url.protocol !== \"https:\" && !isLocal) {\n throw new Error(\n `[agent-id] jwksUrl must use HTTPS, received: ${rawUrl}`\n );\n }\n\n jwksSets.set(\n rawUrl,\n createRemoteJWKSet(url, {\n cacheMaxAge: 60 * 60 * 1_000, // 1 hour in ms\n })\n );\n }\n\n return jwksSets.get(rawUrl)!;\n}\n\nexport interface VerifyTokenOptions {\n jwksUrl?: string;\n clockTolerance?: number;\n}\n\n/**\n * Verify an AgentID JWT and return its decoded claims.\n *\n * Security guarantees\n * ───────────────────\n * • Signature — RS256, verified against the live JWKS public key.\n * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are\n * rejected before signature verification even begins.\n * • Issuer — must be exactly \"agentid\".\n * • Expiry — enforced; configurable clock tolerance (default 30 s).\n * • kid — jose matches the JWT `kid` header to the JWKS automatically.\n * • auth_method — validated at runtime; must equal \"bankid\".\n *\n * @throws if the token is invalid, expired, or fails any check.\n */\nexport async function verifyAgentIDToken(\n token: string,\n options: VerifyTokenOptions = {}\n): Promise<AgentIDClaims> {\n const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;\n const JWKS = getJwks(jwksUrl);\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: \"agentid\",\n // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.\n // Any token claiming alg:\"none\", alg:\"HS256\", or anything else is\n // rejected before signature verification.\n algorithms: [\"RS256\"],\n clockTolerance: options.clockTolerance ?? 30,\n });\n\n // Runtime validation of AgentID-specific claims.\n if (payload.auth_method !== \"bankid\") {\n throw new Error(\n `[agent-id] JWT has invalid auth_method: expected \"bankid\", got \"${\n payload.auth_method ?? \"undefined\"\n }\"`\n );\n }\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0) {\n throw new Error(\"[agent-id] JWT is missing the sub claim\");\n }\n\n return payload as unknown as AgentIDClaims;\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -11,6 +11,7 @@ var BOT_UA_PATTERNS = [
11
11
  /ClaudeBot/i,
12
12
  /Claude-Web/i,
13
13
  /anthropic-ai/i,
14
+ /Claude-User/i,
14
15
  // Google
15
16
  /Googlebot/i,
16
17
  /Google-Extended/i,
@@ -36,9 +37,9 @@ var BOT_UA_PATTERNS = [
36
37
  /\bspider\b/i,
37
38
  /\bscraper\b/i,
38
39
  /\bfetcher\b/i,
39
- // MCP / AgentPass clients
40
+ // MCP / AgentID clients
40
41
  /mcp-client/i,
41
- /agentpass-client/i
42
+ /agentid-client/i
42
43
  ];
43
44
  var BROWSER_UA_RE = /Mozilla\/5\.0/i;
44
45
  function isAgentRequest(headers) {
@@ -62,14 +63,14 @@ function extractToken(headers) {
62
63
  const token = auth.slice(7).trim();
63
64
  if (token) return token;
64
65
  }
65
- const custom = headers.get("x-agentpass-token")?.trim();
66
+ const custom = headers.get("x-agentid-token")?.trim();
66
67
  if (custom) return custom;
67
68
  return null;
68
69
  }
69
70
 
70
71
  // src/verify.ts
71
72
  import { createRemoteJWKSet, jwtVerify } from "jose";
72
- var DEFAULT_JWKS_URL = "https://agentpass.vercel.app/api/jwks";
73
+ var DEFAULT_JWKS_URL = "https://agentidapp.vercel.app/api/jwks";
73
74
  var jwksSets = /* @__PURE__ */ new Map();
74
75
  function getJwks(rawUrl) {
75
76
  if (!jwksSets.has(rawUrl)) {
@@ -90,11 +91,11 @@ function getJwks(rawUrl) {
90
91
  }
91
92
  return jwksSets.get(rawUrl);
92
93
  }
93
- async function verifyAgentPassToken(token, options = {}) {
94
+ async function verifyAgentIDToken(token, options = {}) {
94
95
  const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;
95
96
  const JWKS = getJwks(jwksUrl);
96
97
  const { payload } = await jwtVerify(token, JWKS, {
97
- issuer: "agentpass",
98
+ issuer: "agentid",
98
99
  // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.
99
100
  // Any token claiming alg:"none", alg:"HS256", or anything else is
100
101
  // rejected before signature verification.
@@ -114,18 +115,18 @@ async function verifyAgentPassToken(token, options = {}) {
114
115
 
115
116
  // src/middleware.ts
116
117
  var MANAGED_HEADERS = [
117
- "x-agentpass-verified",
118
- "x-agentpass-sub",
119
- "x-agentpass-claims"
118
+ "x-agentid-verified",
119
+ "x-agentid-sub",
120
+ "x-agentid-claims"
120
121
  ];
121
- function createAgentPassMiddleware(options = {}) {
122
+ function createAgentIDMiddleware(options = {}) {
122
123
  const {
123
124
  jwksUrl,
124
125
  blockUnauthorizedAgents = true,
125
126
  clockTolerance = 30,
126
127
  onUnauthorizedAgent
127
128
  } = options;
128
- return async function agentPassMiddleware(request) {
129
+ return async function agentIDMiddleware(request) {
129
130
  const requestHeaders = new Headers(request.headers);
130
131
  for (const name of MANAGED_HEADERS) {
131
132
  requestHeaders.delete(name);
@@ -138,14 +139,14 @@ function createAgentPassMiddleware(options = {}) {
138
139
  return unauthorized(
139
140
  request,
140
141
  requestHeaders,
141
- 'Agent request missing AgentPass token. Provide "Authorization: Bearer <token>".',
142
+ 'Agent request missing AgentID token. Provide "Authorization: Bearer <token>".',
142
143
  blockUnauthorizedAgents,
143
144
  onUnauthorizedAgent
144
145
  );
145
146
  }
146
147
  let claims;
147
148
  try {
148
- claims = await verifyAgentPassToken(token, {
149
+ claims = await verifyAgentIDToken(token, {
149
150
  ...jwksUrl !== void 0 && { jwksUrl },
150
151
  clockTolerance
151
152
  });
@@ -157,14 +158,14 @@ function createAgentPassMiddleware(options = {}) {
157
158
  return unauthorized(
158
159
  request,
159
160
  requestHeaders,
160
- "Invalid or expired AgentPass token.",
161
+ "Invalid or expired AgentID token.",
161
162
  blockUnauthorizedAgents,
162
163
  onUnauthorizedAgent
163
164
  );
164
165
  }
165
- requestHeaders.set("x-agentpass-verified", "true");
166
- requestHeaders.set("x-agentpass-sub", claims.sub);
167
- requestHeaders.set("x-agentpass-claims", JSON.stringify(claims));
166
+ requestHeaders.set("x-agentid-verified", "true");
167
+ requestHeaders.set("x-agentid-sub", claims.sub);
168
+ requestHeaders.set("x-agentid-claims", JSON.stringify(claims));
168
169
  return NextResponse.next({ request: { headers: requestHeaders } });
169
170
  };
170
171
  }
@@ -179,20 +180,20 @@ function unauthorized(request, requestHeaders, message, block, onUnauthorized) {
179
180
  { status: 403 }
180
181
  );
181
182
  }
182
- requestHeaders.set("x-agentpass-verified", "false");
183
+ requestHeaders.set("x-agentid-verified", "false");
183
184
  return NextResponse.next({ request: { headers: requestHeaders } });
184
185
  }
185
186
 
186
187
  // src/index.ts
187
- function getAgentPassResult(request) {
188
- const verified = request.headers.get("x-agentpass-verified");
188
+ function getAgentIDResult(request) {
189
+ const verified = request.headers.get("x-agentid-verified");
189
190
  if (verified !== "true") {
190
191
  return {
191
192
  verified: false,
192
193
  reason: verified === "false" ? "no_token" : "not_agent"
193
194
  };
194
195
  }
195
- const claimsJson = request.headers.get("x-agentpass-claims");
196
+ const claimsJson = request.headers.get("x-agentid-claims");
196
197
  if (!claimsJson) {
197
198
  return { verified: false, reason: "invalid_token" };
198
199
  }
@@ -204,10 +205,10 @@ function getAgentPassResult(request) {
204
205
  }
205
206
  }
206
207
  export {
207
- createAgentPassMiddleware,
208
+ createAgentIDMiddleware,
208
209
  extractToken,
209
- getAgentPassResult,
210
+ getAgentIDResult,
210
211
  isAgentRequest,
211
- verifyAgentPassToken
212
+ verifyAgentIDToken
212
213
  };
213
214
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/detect.ts","../src/verify.ts","../src/index.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport { isAgentRequest, extractToken } from \"./detect.js\";\nimport { verifyAgentPassToken } from \"./verify.js\";\nimport type { VerifierOptions, AgentPassClaims } from \"./types.js\";\n\nexport type AgentPassMiddlewareOptions = VerifierOptions & {\n /**\n * Optional callback invoked when an agent is detected but carries no\n * valid token. Return a `NextResponse` to override the default 403.\n */\n onUnauthorizedAgent?: (\n request: NextRequest,\n reason: string\n ) => NextResponse | void;\n};\n\n/**\n * Headers injected by this middleware into downstream route handlers.\n * They are stripped from the *incoming* request first to prevent spoofing.\n */\nconst MANAGED_HEADERS = [\n \"x-agentpass-verified\",\n \"x-agentpass-sub\",\n \"x-agentpass-claims\",\n] as const;\n\n/**\n * Factory that returns a Next.js middleware function which detects AI-agent\n * traffic and enforces AgentPass JWT authentication.\n *\n * @example\n * ```ts\n * // middleware.ts (project root)\n * import { createAgentPassMiddleware } from '@agent-id/nextjs';\n *\n * const agentPass = createAgentPassMiddleware({\n * blockUnauthorizedAgents: true,\n * });\n *\n * export function middleware(request: NextRequest) {\n * return agentPass(request);\n * }\n *\n * export const config = { matcher: '/api/:path*' };\n * ```\n */\nexport function createAgentPassMiddleware(\n options: AgentPassMiddlewareOptions = {}\n) {\n const {\n jwksUrl,\n blockUnauthorizedAgents = true,\n clockTolerance = 30,\n onUnauthorizedAgent,\n } = options;\n\n return async function agentPassMiddleware(\n request: NextRequest\n ): Promise<NextResponse> {\n // Clone incoming headers so we can safely mutate them.\n const requestHeaders = new Headers(request.headers);\n\n // ── Security: strip client-supplied AgentPass headers ──────────────────\n // Without this, a malicious agent could send x-agentpass-verified: true\n // and bypass the check in route handlers.\n for (const name of MANAGED_HEADERS) {\n requestHeaders.delete(name);\n }\n\n // ── Non-agent traffic ──────────────────────────────────────────────────\n if (!isAgentRequest(requestHeaders)) {\n return NextResponse.next({ request: { headers: requestHeaders } });\n }\n\n // ── Agent detected — require a valid JWT ───────────────────────────────\n const token = extractToken(requestHeaders);\n\n if (!token) {\n return unauthorized(\n request,\n requestHeaders,\n 'Agent request missing AgentPass token. Provide \"Authorization: Bearer <token>\".',\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verify the JWT ─────────────────────────────────────────────────────\n let claims: AgentPassClaims;\n try {\n claims = await verifyAgentPassToken(token, {\n ...(jwksUrl !== undefined && { jwksUrl }),\n clockTolerance,\n });\n } catch (err) {\n // Never surface the raw error to the caller — it might leak internals.\n console.warn(\n \"[agent-id] Token verification failed:\",\n err instanceof Error ? err.message : String(err)\n );\n return unauthorized(\n request,\n requestHeaders,\n \"Invalid or expired AgentPass token.\",\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verified — inject identity into request headers ────────────────────\n // Route handlers read these via getAgentPassResult(request).\n requestHeaders.set(\"x-agentpass-verified\", \"true\");\n requestHeaders.set(\"x-agentpass-sub\", claims.sub);\n // Full claims encoded as JSON for type-safe extraction by helpers.\n requestHeaders.set(\"x-agentpass-claims\", JSON.stringify(claims));\n\n return NextResponse.next({ request: { headers: requestHeaders } });\n };\n}\n\n// ── Internal helper ──────────────────────────────────────────────────────────\n\nfunction unauthorized(\n request: NextRequest,\n requestHeaders: Headers,\n message: string,\n block: boolean,\n onUnauthorized: AgentPassMiddlewareOptions[\"onUnauthorizedAgent\"]\n): NextResponse {\n if (onUnauthorized) {\n const custom = onUnauthorized(request, message);\n if (custom) return custom;\n }\n\n if (block) {\n return NextResponse.json(\n { error: \"AGENT_UNAUTHORIZED\", message },\n { status: 403 }\n );\n }\n\n // Pass through with a \"not verified\" marker so route handlers can still\n // distinguish agent traffic from human traffic.\n requestHeaders.set(\"x-agentpass-verified\", \"false\");\n return NextResponse.next({ request: { headers: requestHeaders } });\n}\n","/**\n * Bot / AI-agent detection for Next.js (Edge Runtime compatible).\n *\n * Uses layered heuristics:\n * 1. Explicit bot / AI-agent User-Agent strings\n * 2. Cloudflare Bot Management score (if header is present)\n * 3. Absence of browser signals (no Accept-Language + non-browser UA)\n *\n * The detector is intentionally conservative — when in doubt it lets the\n * request through (fail open). A false negative (undetected bot) means the\n * request passes without a JWT check. A false positive (human flagged as bot)\n * would block legitimate traffic, which is much worse.\n */\n\nconst BOT_UA_PATTERNS: RegExp[] = [\n // OpenAI\n /GPTBot/i,\n /ChatGPT-User/i,\n /OAI-SearchBot/i,\n // Anthropic / Claude\n /ClaudeBot/i,\n /Claude-Web/i,\n /anthropic-ai/i,\n // Google\n /Googlebot/i,\n /Google-Extended/i,\n /AdsBot-Google/i,\n // Microsoft / Bing\n /bingbot/i,\n /msnbot/i,\n // AI search engines\n /PerplexityBot/i,\n /YouBot/i,\n // Common HTTP automation libraries\n /python-requests/i,\n /node-fetch/i,\n /\\baxios\\b/i,\n /\\bgot\\b\\//i,\n /\\bundici\\b/i,\n /\\bcurl\\b/i,\n /\\bwget\\b/i,\n /\\bhttpie\\b/i,\n // Generic crawler signals (word-boundary matched to reduce false positives)\n /\\bbot\\b/i,\n /\\bcrawler\\b/i,\n /\\bspider\\b/i,\n /\\bscraper\\b/i,\n /\\bfetcher\\b/i,\n // MCP / AgentPass clients\n /mcp-client/i,\n /agentpass-client/i,\n];\n\n// Every major browser includes \"Mozilla/5.0\" — its absence is a strong signal\nconst BROWSER_UA_RE = /Mozilla\\/5\\.0/i;\n\n/**\n * Returns `true` if the request appears to originate from an automated\n * agent or bot rather than a human browser.\n *\n * @param headers - The `Headers` object from a Next.js `NextRequest`\n */\nexport function isAgentRequest(headers: Headers): boolean {\n const ua = headers.get(\"user-agent\") ?? \"\";\n\n // No User-Agent → definitely automated\n if (!ua) return true;\n\n // Explicit bot / agent UA match\n if (BOT_UA_PATTERNS.some((p) => p.test(ua))) return true;\n\n // Cloudflare Bot Management: the `cf-bot-management` header contains a\n // JSON blob with a `score` field (0–100). Score < 30 → highly likely bot.\n const cfRaw = headers.get(\"cf-bot-management\");\n if (cfRaw) {\n try {\n const parsed = JSON.parse(cfRaw) as { score?: unknown };\n if (typeof parsed.score === \"number\" && parsed.score < 30) return true;\n } catch {\n // Malformed header — fail open (pass through)\n }\n }\n\n // Heuristic: non-browser UA + no Accept-Language → likely automated\n if (!BROWSER_UA_RE.test(ua) && !headers.get(\"accept-language\")) return true;\n\n return false;\n}\n\n/**\n * Extract the AgentPass JWT from request headers.\n *\n * Checks in order:\n * 1. `Authorization: Bearer <token>` (preferred)\n * 2. `X-AgentPass-Token` (fallback when Authorization is stripped by proxies)\n *\n * @returns The raw JWT string, or `null` if absent.\n */\nexport function extractToken(headers: Headers): string | null {\n // Primary: standard Authorization header\n const auth = headers.get(\"authorization\") ?? \"\";\n if (auth.startsWith(\"Bearer \")) {\n const token = auth.slice(7).trim();\n if (token) return token;\n }\n\n // Fallback: custom header\n const custom = headers.get(\"x-agentpass-token\")?.trim();\n if (custom) return custom;\n\n return null;\n}\n","import { createRemoteJWKSet, jwtVerify } from \"jose\";\nimport type { AgentPassClaims } from \"./types.js\";\n\nconst DEFAULT_JWKS_URL = \"https://agentpass.vercel.app/api/jwks\";\n\n/**\n * Module-level JWKS cache — one RemoteJWKSet instance per unique URL.\n * jose caches the fetched key material internally; re-fetches when the\n * cache TTL (1 h) expires or a new `kid` is seen.\n */\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nfunction getJwks(rawUrl: string): ReturnType<typeof createRemoteJWKSet> {\n if (!jwksSets.has(rawUrl)) {\n const url = new URL(rawUrl); // throws on malformed URL\n\n // Security: HTTPS is mandatory to prevent MITM on the public-key fetch.\n // Localhost is whitelisted for local development / CI.\n const isLocal =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\";\n\n if (url.protocol !== \"https:\" && !isLocal) {\n throw new Error(\n `[agent-id] jwksUrl must use HTTPS, received: ${rawUrl}`\n );\n }\n\n jwksSets.set(\n rawUrl,\n createRemoteJWKSet(url, {\n cacheMaxAge: 60 * 60 * 1_000, // 1 hour in ms\n })\n );\n }\n\n return jwksSets.get(rawUrl)!;\n}\n\nexport interface VerifyTokenOptions {\n jwksUrl?: string;\n clockTolerance?: number;\n}\n\n/**\n * Verify an AgentPass JWT and return its decoded claims.\n *\n * Security guarantees\n * ───────────────────\n * • Signature — RS256, verified against the live JWKS public key.\n * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are\n * rejected before signature verification even begins.\n * • Issuer — must be exactly \"agentpass\".\n * • Expiry — enforced; configurable clock tolerance (default 30 s).\n * • kid — jose matches the JWT `kid` header to the JWKS automatically.\n * • auth_method — validated at runtime; must equal \"bankid\".\n *\n * @throws if the token is invalid, expired, or fails any check.\n */\nexport async function verifyAgentPassToken(\n token: string,\n options: VerifyTokenOptions = {}\n): Promise<AgentPassClaims> {\n const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;\n const JWKS = getJwks(jwksUrl);\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: \"agentpass\",\n // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.\n // Any token claiming alg:\"none\", alg:\"HS256\", or anything else is\n // rejected before signature verification.\n algorithms: [\"RS256\"],\n clockTolerance: options.clockTolerance ?? 30,\n });\n\n // Runtime validation of AgentPass-specific claims.\n if (payload.auth_method !== \"bankid\") {\n throw new Error(\n `[agent-id] JWT has invalid auth_method: expected \"bankid\", got \"${\n payload.auth_method ?? \"undefined\"\n }\"`\n );\n }\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0) {\n throw new Error(\"[agent-id] JWT is missing the sub claim\");\n }\n\n return payload as unknown as AgentPassClaims;\n}\n","export { createAgentPassMiddleware } from \"./middleware.js\";\nexport type { AgentPassMiddlewareOptions } from \"./middleware.js\";\n\nexport { verifyAgentPassToken } from \"./verify.js\";\nexport type { VerifyTokenOptions } from \"./verify.js\";\n\nexport { isAgentRequest, extractToken } from \"./detect.js\";\n\nexport type {\n AgentPassClaims,\n AgentPassResult,\n AgentPassVerified,\n AgentPassUnverified,\n VerifierOptions,\n} from \"./types.js\";\n\n// ── App Router helper ────────────────────────────────────────────────────────\n\nimport type { NextRequest } from \"next/server\";\nimport type { AgentPassResult, AgentPassClaims } from \"./types.js\";\n\n/**\n * Extract the verified AgentPass identity from a Next.js App Router request.\n *\n * This function reads the headers set by `createAgentPassMiddleware`. It must\n * only be called from route handlers that sit behind the middleware.\n *\n * @example\n * ```ts\n * // app/api/data/route.ts\n * import { getAgentPassResult } from '@agent-id/nextjs';\n *\n * export async function GET(request: NextRequest) {\n * const agentPass = getAgentPassResult(request);\n * if (!agentPass.verified) {\n * return Response.json({ error: 'Unauthorized' }, { status: 403 });\n * }\n * return Response.json({ sub: agentPass.claims.sub });\n * }\n * ```\n */\nexport function getAgentPassResult(request: NextRequest): AgentPassResult {\n const verified = request.headers.get(\"x-agentpass-verified\");\n\n if (verified !== \"true\") {\n return {\n verified: false,\n reason: verified === \"false\" ? \"no_token\" : \"not_agent\",\n };\n }\n\n const claimsJson = request.headers.get(\"x-agentpass-claims\");\n if (!claimsJson) {\n return { verified: false, reason: \"invalid_token\" };\n }\n\n try {\n const claims = JSON.parse(claimsJson) as AgentPassClaims;\n return { verified: true, claims };\n } catch {\n return { verified: false, reason: \"invalid_token\" };\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/middleware.ts","../src/detect.ts","../src/verify.ts","../src/index.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport { isAgentRequest, extractToken } from \"./detect.js\";\nimport { verifyAgentIDToken } from \"./verify.js\";\nimport type { VerifierOptions, AgentIDClaims } from \"./types.js\";\n\nexport type AgentIDMiddlewareOptions = VerifierOptions & {\n /**\n * Optional callback invoked when an agent is detected but carries no\n * valid token. Return a `NextResponse` to override the default 403.\n */\n onUnauthorizedAgent?: (\n request: NextRequest,\n reason: string\n ) => NextResponse | void;\n};\n\n/**\n * Headers injected by this proxy into downstream route handlers.\n * They are stripped from the *incoming* request first to prevent spoofing.\n */\nconst MANAGED_HEADERS = [\n \"x-agentid-verified\",\n \"x-agentid-sub\",\n \"x-agentid-claims\",\n] as const;\n\n/**\n * Factory that returns a Next.js proxy function which detects AI-agent\n * traffic and enforces AgentID JWT authentication.\n *\n * @example\n * ```ts\n * // proxy.ts (project root)\n * import { createAgentIDMiddleware } from '@agent-id/nextjs';\n *\n * const agentID = createAgentIDMiddleware({\n * blockUnauthorizedAgents: true,\n * });\n *\n * export function proxy(request: NextRequest) {\n * return agentID(request);\n * }\n *\n * export const config = { matcher: '/api/:path*' };\n * ```\n */\nexport function createAgentIDMiddleware(\n options: AgentIDMiddlewareOptions = {}\n) {\n const {\n jwksUrl,\n blockUnauthorizedAgents = true,\n clockTolerance = 30,\n onUnauthorizedAgent,\n } = options;\n\n return async function agentIDMiddleware(\n request: NextRequest\n ): Promise<NextResponse> {\n // Clone incoming headers so we can safely mutate them.\n const requestHeaders = new Headers(request.headers);\n\n // ── Security: strip client-supplied AgentID headers ──────────────────\n // Without this, a malicious agent could send x-agentid-verified: true\n // and bypass the check in route handlers.\n for (const name of MANAGED_HEADERS) {\n requestHeaders.delete(name);\n }\n\n // ── Non-agent traffic ──────────────────────────────────────────────────\n if (!isAgentRequest(requestHeaders)) {\n return NextResponse.next({ request: { headers: requestHeaders } });\n }\n\n // ── Agent detected — require a valid JWT ───────────────────────────────\n const token = extractToken(requestHeaders);\n\n if (!token) {\n return unauthorized(\n request,\n requestHeaders,\n 'Agent request missing AgentID token. Provide \"Authorization: Bearer <token>\".',\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verify the JWT ─────────────────────────────────────────────────────\n let claims: AgentIDClaims;\n try {\n claims = await verifyAgentIDToken(token, {\n ...(jwksUrl !== undefined && { jwksUrl }),\n clockTolerance,\n });\n } catch (err) {\n // Never surface the raw error to the caller — it might leak internals.\n console.warn(\n \"[agent-id] Token verification failed:\",\n err instanceof Error ? err.message : String(err)\n );\n return unauthorized(\n request,\n requestHeaders,\n \"Invalid or expired AgentID token.\",\n blockUnauthorizedAgents,\n onUnauthorizedAgent\n );\n }\n\n // ── Verified — inject identity into request headers ────────────────────\n // Route handlers read these via getAgentIDResult(request).\n requestHeaders.set(\"x-agentid-verified\", \"true\");\n requestHeaders.set(\"x-agentid-sub\", claims.sub);\n // Full claims encoded as JSON for type-safe extraction by helpers.\n requestHeaders.set(\"x-agentid-claims\", JSON.stringify(claims));\n\n return NextResponse.next({ request: { headers: requestHeaders } });\n };\n}\n\n// ── Internal helper ──────────────────────────────────────────────────────────\n\nfunction unauthorized(\n request: NextRequest,\n requestHeaders: Headers,\n message: string,\n block: boolean,\n onUnauthorized: AgentIDMiddlewareOptions[\"onUnauthorizedAgent\"]\n): NextResponse {\n if (onUnauthorized) {\n const custom = onUnauthorized(request, message);\n if (custom) return custom;\n }\n\n if (block) {\n return NextResponse.json(\n { error: \"AGENT_UNAUTHORIZED\", message },\n { status: 403 }\n );\n }\n\n // Pass through with a \"not verified\" marker so route handlers can still\n // distinguish agent traffic from human traffic.\n requestHeaders.set(\"x-agentid-verified\", \"false\");\n return NextResponse.next({ request: { headers: requestHeaders } });\n}\n","/**\n * Bot / AI-agent detection for Next.js (Edge Runtime compatible).\n *\n * Uses layered heuristics:\n * 1. Explicit bot / AI-agent User-Agent strings\n * 2. Cloudflare Bot Management score (if header is present)\n * 3. Absence of browser signals (no Accept-Language + non-browser UA)\n *\n * The detector is intentionally conservative — when in doubt it lets the\n * request through (fail open). A false negative (undetected bot) means the\n * request passes without a JWT check. A false positive (human flagged as bot)\n * would block legitimate traffic, which is much worse.\n */\n\nconst BOT_UA_PATTERNS: RegExp[] = [\n // OpenAI\n /GPTBot/i,\n /ChatGPT-User/i,\n /OAI-SearchBot/i,\n // Anthropic / Claude\n /ClaudeBot/i,\n /Claude-Web/i,\n /anthropic-ai/i,\n /Claude-User/i,\n // Google\n /Googlebot/i,\n /Google-Extended/i,\n /AdsBot-Google/i,\n // Microsoft / Bing\n /bingbot/i,\n /msnbot/i,\n // AI search engines\n /PerplexityBot/i,\n /YouBot/i,\n // Common HTTP automation libraries\n /python-requests/i,\n /node-fetch/i,\n /\\baxios\\b/i,\n /\\bgot\\b\\//i,\n /\\bundici\\b/i,\n /\\bcurl\\b/i,\n /\\bwget\\b/i,\n /\\bhttpie\\b/i,\n // Generic crawler signals (word-boundary matched to reduce false positives)\n /\\bbot\\b/i,\n /\\bcrawler\\b/i,\n /\\bspider\\b/i,\n /\\bscraper\\b/i,\n /\\bfetcher\\b/i,\n // MCP / AgentID clients\n /mcp-client/i,\n /agentid-client/i,\n];\n\n// Every major browser includes \"Mozilla/5.0\" — its absence is a strong signal\nconst BROWSER_UA_RE = /Mozilla\\/5\\.0/i;\n\n/**\n * Returns `true` if the request appears to originate from an automated\n * agent or bot rather than a human browser.\n *\n * @param headers - The `Headers` object from a Next.js `NextRequest`\n */\nexport function isAgentRequest(headers: Headers): boolean {\n const ua = headers.get(\"user-agent\") ?? \"\";\n\n // No User-Agent → definitely automated\n if (!ua) return true;\n\n // Explicit bot / agent UA match\n if (BOT_UA_PATTERNS.some((p) => p.test(ua))) return true;\n\n // Cloudflare Bot Management: the `cf-bot-management` header contains a\n // JSON blob with a `score` field (0–100). Score < 30 → highly likely bot.\n const cfRaw = headers.get(\"cf-bot-management\");\n if (cfRaw) {\n try {\n const parsed = JSON.parse(cfRaw) as { score?: unknown };\n if (typeof parsed.score === \"number\" && parsed.score < 30) return true;\n } catch {\n // Malformed header — fail open (pass through)\n }\n }\n\n // Heuristic: non-browser UA + no Accept-Language → likely automated\n if (!BROWSER_UA_RE.test(ua) && !headers.get(\"accept-language\")) return true;\n\n return false;\n}\n\n/**\n * Extract the AgentID JWT from request headers.\n *\n * Checks in order:\n * 1. `Authorization: Bearer <token>` (preferred)\n * 2. `X-AgentID-Token` (fallback when Authorization is stripped by proxies)\n *\n * @returns The raw JWT string, or `null` if absent.\n */\nexport function extractToken(headers: Headers): string | null {\n // Primary: standard Authorization header\n const auth = headers.get(\"authorization\") ?? \"\";\n if (auth.startsWith(\"Bearer \")) {\n const token = auth.slice(7).trim();\n if (token) return token;\n }\n\n // Fallback: custom header\n const custom = headers.get(\"x-agentid-token\")?.trim();\n if (custom) return custom;\n\n return null;\n}\n","import { createRemoteJWKSet, jwtVerify } from \"jose\";\nimport type { AgentIDClaims } from \"./types.js\";\n\nconst DEFAULT_JWKS_URL = \"https://agentidapp.vercel.app/api/jwks\";\n\n/**\n * Module-level JWKS cache — one RemoteJWKSet instance per unique URL.\n * jose caches the fetched key material internally; re-fetches when the\n * cache TTL (1 h) expires or a new `kid` is seen.\n */\nconst jwksSets = new Map<string, ReturnType<typeof createRemoteJWKSet>>();\n\nfunction getJwks(rawUrl: string): ReturnType<typeof createRemoteJWKSet> {\n if (!jwksSets.has(rawUrl)) {\n const url = new URL(rawUrl); // throws on malformed URL\n\n // Security: HTTPS is mandatory to prevent MITM on the public-key fetch.\n // Localhost is whitelisted for local development / CI.\n const isLocal =\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\";\n\n if (url.protocol !== \"https:\" && !isLocal) {\n throw new Error(\n `[agent-id] jwksUrl must use HTTPS, received: ${rawUrl}`\n );\n }\n\n jwksSets.set(\n rawUrl,\n createRemoteJWKSet(url, {\n cacheMaxAge: 60 * 60 * 1_000, // 1 hour in ms\n })\n );\n }\n\n return jwksSets.get(rawUrl)!;\n}\n\nexport interface VerifyTokenOptions {\n jwksUrl?: string;\n clockTolerance?: number;\n}\n\n/**\n * Verify an AgentID JWT and return its decoded claims.\n *\n * Security guarantees\n * ───────────────────\n * • Signature — RS256, verified against the live JWKS public key.\n * • Algorithm — strict allowlist `['RS256']`; alg:none and HS256 are\n * rejected before signature verification even begins.\n * • Issuer — must be exactly \"agentid\".\n * • Expiry — enforced; configurable clock tolerance (default 30 s).\n * • kid — jose matches the JWT `kid` header to the JWKS automatically.\n * • auth_method — validated at runtime; must equal \"bankid\".\n *\n * @throws if the token is invalid, expired, or fails any check.\n */\nexport async function verifyAgentIDToken(\n token: string,\n options: VerifyTokenOptions = {}\n): Promise<AgentIDClaims> {\n const jwksUrl = options.jwksUrl ?? DEFAULT_JWKS_URL;\n const JWKS = getJwks(jwksUrl);\n\n const { payload } = await jwtVerify(token, JWKS, {\n issuer: \"agentid\",\n // ↓ Critical — explicit allowlist prevents algorithm-confusion attacks.\n // Any token claiming alg:\"none\", alg:\"HS256\", or anything else is\n // rejected before signature verification.\n algorithms: [\"RS256\"],\n clockTolerance: options.clockTolerance ?? 30,\n });\n\n // Runtime validation of AgentID-specific claims.\n if (payload.auth_method !== \"bankid\") {\n throw new Error(\n `[agent-id] JWT has invalid auth_method: expected \"bankid\", got \"${\n payload.auth_method ?? \"undefined\"\n }\"`\n );\n }\n if (typeof payload.sub !== \"string\" || payload.sub.length === 0) {\n throw new Error(\"[agent-id] JWT is missing the sub claim\");\n }\n\n return payload as unknown as AgentIDClaims;\n}\n","export { createAgentIDMiddleware } from \"./middleware.js\";\nexport type { AgentIDMiddlewareOptions } from \"./middleware.js\";\n\nexport { verifyAgentIDToken } from \"./verify.js\";\nexport type { VerifyTokenOptions } from \"./verify.js\";\n\nexport { isAgentRequest, extractToken } from \"./detect.js\";\n\nexport type {\n AgentIDClaims,\n AgentIDResult,\n AgentIDVerified,\n AgentIDUnverified,\n VerifierOptions,\n} from \"./types.js\";\n\n// ── App Router helper ────────────────────────────────────────────────────────\n\nimport type { NextRequest } from \"next/server\";\nimport type { AgentIDResult, AgentIDClaims } from \"./types.js\";\n\n/**\n * Extract the verified AgentID identity from a Next.js App Router request.\n *\n * This function reads the headers set by `createAgentIDMiddleware`. It must\n * only be called from route handlers that sit behind the proxy.\n *\n * @example\n * ```ts\n * // app/api/data/route.ts\n * import { getAgentIDResult } from '@agent-id/nextjs';\n *\n * export async function GET(request: NextRequest) {\n * const agent = getAgentIDResult(request);\n * if (!agent.verified) {\n * return Response.json({ error: 'Unauthorized' }, { status: 403 });\n * }\n * return Response.json({ sub: agent.claims.sub });\n * }\n * ```\n */\nexport function getAgentIDResult(request: NextRequest): AgentIDResult {\n const verified = request.headers.get(\"x-agentid-verified\");\n\n if (verified !== \"true\") {\n return {\n verified: false,\n reason: verified === \"false\" ? \"no_token\" : \"not_agent\",\n };\n }\n\n const claimsJson = request.headers.get(\"x-agentid-claims\");\n if (!claimsJson) {\n return { verified: false, reason: \"invalid_token\" };\n }\n\n try {\n const claims = JSON.parse(claimsJson) as AgentIDClaims;\n return { verified: true, claims };\n } catch {\n return { verified: false, reason: \"invalid_token\" };\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@agent-id/nextjs",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Agent-ID verifier middleware for Next.js — blocks unauthorized AI agents from your API routes",
5
- "keywords": ["agent-id", "agentpass", "bankid", "jwt", "nextjs", "middleware", "ai-agent", "mcp", "bot-detection"],
5
+ "keywords": ["agent-id", "bankid", "jwt", "nextjs", "middleware", "ai-agent", "mcp", "bot-detection"],
6
6
  "license": "MIT",
7
7
  "main": "./dist/index.cjs",
8
8
  "module": "./dist/index.js",