@agentvisa/widget 0.2.2 → 0.2.4
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 +110 -64
- package/dist/core.d.ts +23 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +64 -1
- package/dist/core.js.map +1 -1
- package/dist/express/index.d.ts +2 -0
- package/dist/express/index.d.ts.map +1 -1
- package/dist/express/index.js +26 -9
- package/dist/express/index.js.map +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +38 -6
- package/dist/next/index.js.map +1 -1
- package/dist/widget.esm.js +0 -3
- package/dist/widget.js +0 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
# @agentvisa/widget
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Server-side middleware for **AgentVisa** verification — protect your Express or Next.js API routes so only AI agents backed by a verified human can access them.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
When an unverified agent hits a protected route, the middleware returns a `302` redirect (or `401`) pointing the agent to [agentvisa.ai/for-agents](https://agentvisa.ai/for-agents), where it prompts the human to sign up. Verified agents pass straight through.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
- Unified response shape for all plans
|
|
11
|
-
- Zero runtime dependencies
|
|
12
|
-
- < 5KB gzipped
|
|
13
|
-
- TypeScript support
|
|
7
|
+
> **Which package should I use?**
|
|
8
|
+
> - **`@agentvisa/widget`** (this package) — use when you want the full viral redirect loop: unverified AI agents are redirected to AgentVisa to get verified, then come back. Also works as a plain `block` or `passthrough` gate. Supports Express and Next.js.
|
|
9
|
+
> - **[`@agentvisa/verify`](https://www.npmjs.com/package/@agentvisa/verify)** — use when you just want to verify a token and get a result back. Simpler API, no redirect, no growth loop. Works with any Node.js framework.
|
|
14
10
|
|
|
15
11
|
## Installation
|
|
16
12
|
|
|
@@ -18,77 +14,134 @@ Drop-in verification for websites that need to confirm a real human has approved
|
|
|
18
14
|
npm install @agentvisa/widget
|
|
19
15
|
```
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
## Quick start — Express
|
|
22
18
|
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
```ts
|
|
20
|
+
import express from 'express';
|
|
21
|
+
import { agentVisa } from '@agentvisa/widget/express';
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
const app = express();
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
app.use('/api', agentVisa({
|
|
26
|
+
widgetId: process.env.AV_WIDGET_ID!,
|
|
27
|
+
apiKey: process.env.AV_API_KEY!,
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
app.post('/api/order', (req, res) => {
|
|
31
|
+
// Only reaches here if the agent is verified
|
|
32
|
+
console.log(req.agentVisa.result.plan); // "basic" | "pro"
|
|
33
|
+
res.json({ ok: true });
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick start — Next.js
|
|
30
38
|
|
|
31
39
|
```ts
|
|
32
|
-
|
|
40
|
+
// middleware.ts (project root)
|
|
41
|
+
import { withAgentVisa } from '@agentvisa/widget/next';
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
widgetId:
|
|
36
|
-
|
|
43
|
+
export default withAgentVisa({
|
|
44
|
+
widgetId: process.env.AV_WIDGET_ID!,
|
|
45
|
+
apiKey: process.env.AV_API_KEY!,
|
|
37
46
|
});
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
export const config = {
|
|
49
|
+
matcher: ['/api/:path*'],
|
|
50
|
+
};
|
|
42
51
|
```
|
|
43
52
|
|
|
44
|
-
|
|
53
|
+
## How agents send their token
|
|
54
|
+
|
|
55
|
+
Agents use the [AgentVisa MCP server](https://www.npmjs.com/package/@agentvisa/mcp) (`@agentvisa/mcp`) to exchange their permanent token for a short-lived `tmp_xxx` token, then send it in one of two ways:
|
|
56
|
+
|
|
57
|
+
**Standard mode:**
|
|
58
|
+
```
|
|
59
|
+
X-AgentVisa-Token: tmp_xxxxxxxxxxxxxxxxxxxx
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Web Bot Auth mode** (RFC 9421 — token bound in the signature):
|
|
63
|
+
```
|
|
64
|
+
AgentVisa-Assertion: tmp_xxxxxxxxxxxxxxxxxxxx
|
|
65
|
+
Signature-Input: sig1=("@method" "@path" "host" "agentvisa-assertion");keyid="key-1";created=1234567890
|
|
66
|
+
Signature: sig1=:base64sig:
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Options
|
|
70
|
+
|
|
71
|
+
| Option | Type | Default | Description |
|
|
72
|
+
|--------|------|---------|-------------|
|
|
73
|
+
| `widgetId` | `string` | **required** | Your `wgt_xxx` widget ID |
|
|
74
|
+
| `apiKey` | `string` | **required** | Your `wk_xxx` API key — **server-side only** |
|
|
75
|
+
| `onUnverified` | `'redirect' \| 'block' \| 'passthrough'` | `'redirect'` | What to do when an agent is unverified |
|
|
76
|
+
| `redirectUrl` | `string` | `'https://agentvisa.ai/for-agents'` | Where to redirect unverified AI agents |
|
|
77
|
+
| `timeoutMs` | `number` | `5000` | Timeout for the `/v1/verify` API call |
|
|
78
|
+
|
|
79
|
+
### `onUnverified` modes
|
|
80
|
+
|
|
81
|
+
- **`'redirect'`** (default) — AI agents (including headless/automation browsers) get a `302` to the AgentVisa challenge page (`/verify`), with `?w=<widget_id>&from=<host>` appended for attribution. Other browser-class requests get an instructive `401` **challenge** — an HTML page for browsers, JSON otherwise — that points both agents and humans to the next step, never a bare dead-end. This is the viral growth mode: unverified agents reach the sign-up path, then come back verified.
|
|
82
|
+
- **`'block'`** — All unverified requests get `401`. Quiet hard gate.
|
|
83
|
+
- **`'passthrough'`** — Attach the result to `req.agentVisa` (Express) or response headers (Next.js) and continue. Use this for soft-gating or analytics.
|
|
84
|
+
|
|
85
|
+
## Express — reading the result
|
|
86
|
+
|
|
87
|
+
On verified requests, `req.agentVisa` is populated:
|
|
45
88
|
|
|
46
89
|
```ts
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
90
|
+
app.post('/api/action', (req, res) => {
|
|
91
|
+
const av = req.agentVisa!;
|
|
92
|
+
console.log(av.verified); // true
|
|
93
|
+
console.log(av.result.plan); // "basic" | "pro"
|
|
94
|
+
console.log(av.result.valid); // true
|
|
95
|
+
console.log(av.result.verified_at); // ISO timestamp
|
|
96
|
+
console.log(av.result.web_bot_auth_bound); // true if RFC 9421 bound (Pro)
|
|
50
97
|
});
|
|
51
|
-
|
|
52
|
-
const result = await visa.verify();
|
|
53
98
|
```
|
|
54
99
|
|
|
55
|
-
|
|
100
|
+
In `'passthrough'` mode, unverified requests also reach your handler:
|
|
56
101
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
102
|
+
```ts
|
|
103
|
+
app.use(agentVisa({ widgetId, apiKey, onUnverified: 'passthrough' }));
|
|
104
|
+
|
|
105
|
+
app.get('/api/data', (req, res) => {
|
|
106
|
+
if (!req.agentVisa?.verified) {
|
|
107
|
+
return res.json({ data: publicData, premium: null });
|
|
108
|
+
}
|
|
109
|
+
res.json({ data: publicData, premium: premiumData });
|
|
110
|
+
});
|
|
65
111
|
```
|
|
66
112
|
|
|
67
|
-
##
|
|
113
|
+
## Next.js — reading the result
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
On the edge or in a route handler, read the forwarded headers:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
// app/api/route.ts
|
|
119
|
+
export async function POST(request: Request) {
|
|
120
|
+
const verified = request.headers.get('x-agentvisa-verified') === 'true';
|
|
121
|
+
const reason = request.headers.get('x-agentvisa-reason');
|
|
122
|
+
|
|
123
|
+
if (!verified) {
|
|
124
|
+
return Response.json({ error: reason }, { status: 401 });
|
|
125
|
+
}
|
|
126
|
+
// Proceed
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Response shape from `/v1/verify`
|
|
70
131
|
|
|
71
132
|
```json
|
|
72
133
|
{
|
|
73
134
|
"valid": true,
|
|
74
135
|
"reason": "ok",
|
|
75
136
|
"plan": "basic",
|
|
76
|
-
"widget_id": "
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"verified_at": null,
|
|
81
|
-
"expires_at": null
|
|
137
|
+
"widget_id": "wgt_abc123",
|
|
138
|
+
"verified_at": "2026-06-22T10:00:00Z",
|
|
139
|
+
"expires_at": "2026-06-23T10:00:00Z",
|
|
140
|
+
"domain_verified": true
|
|
82
141
|
}
|
|
83
142
|
```
|
|
84
143
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
| Option | Type | Default | Description |
|
|
88
|
-
|--------------|--------------------|--------------------------|---------------------------------|
|
|
89
|
-
| `widgetId` | `string` | **required** | Your AgentVisa widget ID |
|
|
90
|
-
| `plan` | `"basic" \| "pro"` | `"basic"` | Verification tier |
|
|
91
|
-
| `apiBaseUrl` | `string` | `"https://api.agentvisa.ai"` | Backend API base URL |
|
|
144
|
+
Pro plan additionally returns `age_over_18`, `age_over_21`, `multiple_agents_authorized`, `web_bot_auth_bound`, and AVS-style attribute confirmations. Raw PII (name, email, phone) is never returned.
|
|
92
145
|
|
|
93
146
|
## Development
|
|
94
147
|
|
|
@@ -98,17 +151,10 @@ npm run build
|
|
|
98
151
|
```
|
|
99
152
|
|
|
100
153
|
Build output is in `dist/`:
|
|
101
|
-
|
|
102
|
-
- `
|
|
103
|
-
- `
|
|
104
|
-
-
|
|
105
|
-
|
|
106
|
-
## Examples
|
|
107
|
-
|
|
108
|
-
See the `examples/` folder:
|
|
109
|
-
|
|
110
|
-
- `basic.html` — Basic tier demo
|
|
111
|
-
- `pro.html` — Pro tier demo with richer data
|
|
154
|
+
- `dist/express/index.js` — Express middleware
|
|
155
|
+
- `dist/next/index.js` — Next.js middleware
|
|
156
|
+
- `dist/core.js` — Shared verification logic
|
|
157
|
+
- TypeScript declarations alongside each `.js` file
|
|
112
158
|
|
|
113
159
|
## Contributing
|
|
114
160
|
|
|
@@ -120,4 +166,4 @@ See [SECURITY.md](SECURITY.md).
|
|
|
120
166
|
|
|
121
167
|
## License
|
|
122
168
|
|
|
123
|
-
MIT © [AgentVisa](https://agentvisa.ai)
|
|
169
|
+
MIT © [AgentVisa](https://agentvisa.ai)
|
package/dist/core.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Used by Express and Next.js middleware — no framework deps here.
|
|
4
4
|
*/
|
|
5
5
|
export declare const DEFAULT_API_BASE = "https://api.agentvisa.ai";
|
|
6
|
-
export declare const DEFAULT_REDIRECT_URL = "https://agentvisa.ai/
|
|
6
|
+
export declare const DEFAULT_REDIRECT_URL = "https://agentvisa.ai/verify";
|
|
7
7
|
/**
|
|
8
8
|
* Detect whether an incoming request looks like an AI agent.
|
|
9
9
|
*
|
|
@@ -18,6 +18,28 @@ export declare const DEFAULT_REDIRECT_URL = "https://agentvisa.ai/for-agents";
|
|
|
18
18
|
* Weak combo (both needed): no browser fingerprint headers + no text/html in Accept
|
|
19
19
|
*/
|
|
20
20
|
export declare function isLikelyAiAgent(headers: Record<string, string | string[] | undefined>): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* True when the caller will render HTML (a browser, or an agent driving one).
|
|
23
|
+
* Used to decide whether to serve an HTML challenge page vs a JSON challenge.
|
|
24
|
+
*/
|
|
25
|
+
export declare function wantsHtml(headers: Record<string, string | string[] | undefined>): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Instructive challenge page served to browser-class unverified requests.
|
|
28
|
+
*
|
|
29
|
+
* A bare 401 dead-ends AI agents that drive a browser (they never discover
|
|
30
|
+
* /for-agents). This page guides BOTH audiences: a human sees a short
|
|
31
|
+
* explainer; an agent sees the token instructions, the <meta agentvisa-required>
|
|
32
|
+
* discovery tag, and the link to follow — so no agent is ever stranded.
|
|
33
|
+
*/
|
|
34
|
+
export declare function challengeHtml(widgetId: string, redirectUrl: string, host?: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Append attribution params to the redirect URL so the landing page — and your
|
|
37
|
+
* analytics — know which site sent the agent: `?w=<widgetId>&from=<host>`.
|
|
38
|
+
* widget_id maps to a registered domain, so this is clean attribution with no PII.
|
|
39
|
+
* (We deliberately pass only the host, never the full path, which could carry the
|
|
40
|
+
* customer site's own query params.)
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildRedirectUrl(redirectUrl: string, widgetId: string, host?: string): string;
|
|
21
43
|
export interface AgentVisaConfig {
|
|
22
44
|
/** Your widget ID from the AgentVisa dashboard */
|
|
23
45
|
widgetId: string;
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAC3D,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAC3D,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAiClE;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,OAAO,CAyBT;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,OAAO,CAIT;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAiB1F;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAW7F;AAED,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC;IACpD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,WAAW,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IAC3C,0BAA0B,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IAItB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAIhC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,EACjC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7D,OAAO,CAAC,YAAY,CAAC,CAqCvB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC,CAQhF"}
|
package/dist/core.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Used by Express and Next.js middleware — no framework deps here.
|
|
4
4
|
*/
|
|
5
5
|
export const DEFAULT_API_BASE = "https://api.agentvisa.ai";
|
|
6
|
-
export const DEFAULT_REDIRECT_URL = "https://agentvisa.ai/
|
|
6
|
+
export const DEFAULT_REDIRECT_URL = "https://agentvisa.ai/verify";
|
|
7
7
|
// ── Agent signal detection ────────────────────────────────────────────────────
|
|
8
8
|
/**
|
|
9
9
|
* Known AI agent / LLM user-agent substrings (case-insensitive).
|
|
@@ -25,6 +25,13 @@ const AI_UA_PATTERNS = [
|
|
|
25
25
|
/agentvisa/i,
|
|
26
26
|
/python-httpx/i,
|
|
27
27
|
/python-requests/i,
|
|
28
|
+
// Browser automation frameworks — AI agents increasingly drive real or headless browsers
|
|
29
|
+
/headless/i,
|
|
30
|
+
/playwright/i,
|
|
31
|
+
/puppeteer/i,
|
|
32
|
+
/selenium/i,
|
|
33
|
+
/webdriver/i,
|
|
34
|
+
/phantomjs/i,
|
|
28
35
|
];
|
|
29
36
|
/**
|
|
30
37
|
* Detect whether an incoming request looks like an AI agent.
|
|
@@ -61,6 +68,62 @@ export function isLikelyAiAgent(headers) {
|
|
|
61
68
|
return true;
|
|
62
69
|
return false;
|
|
63
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* True when the caller will render HTML (a browser, or an agent driving one).
|
|
73
|
+
* Used to decide whether to serve an HTML challenge page vs a JSON challenge.
|
|
74
|
+
*/
|
|
75
|
+
export function wantsHtml(headers) {
|
|
76
|
+
const v = headers["accept"];
|
|
77
|
+
const accept = Array.isArray(v) ? v[0] : (v ?? "");
|
|
78
|
+
return accept.includes("text/html");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Instructive challenge page served to browser-class unverified requests.
|
|
82
|
+
*
|
|
83
|
+
* A bare 401 dead-ends AI agents that drive a browser (they never discover
|
|
84
|
+
* /for-agents). This page guides BOTH audiences: a human sees a short
|
|
85
|
+
* explainer; an agent sees the token instructions, the <meta agentvisa-required>
|
|
86
|
+
* discovery tag, and the link to follow — so no agent is ever stranded.
|
|
87
|
+
*/
|
|
88
|
+
export function challengeHtml(widgetId, redirectUrl, host) {
|
|
89
|
+
const link = buildRedirectUrl(redirectUrl, widgetId, host);
|
|
90
|
+
return `<!doctype html>
|
|
91
|
+
<html lang="en"><head><meta charset="utf-8">
|
|
92
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
93
|
+
<title>AgentVisa — verification required</title>
|
|
94
|
+
<meta name="agentvisa-required" content="${widgetId}">
|
|
95
|
+
</head>
|
|
96
|
+
<body style="font-family:system-ui,-apple-system,sans-serif;max-width:560px;margin:64px auto;padding:0 20px;line-height:1.55;color:#111">
|
|
97
|
+
<h1 style="font-size:22px;margin-bottom:8px">Verification required</h1>
|
|
98
|
+
<p>This site grants access only to AI agents that have a verified human behind them.</p>
|
|
99
|
+
<p><strong>If you are an AI agent:</strong> obtain an AgentVisa token, then retry this request with
|
|
100
|
+
the <code>X-AgentVisa-Token</code> header. Full instructions:
|
|
101
|
+
<a href="${link}">${redirectUrl}</a></p>
|
|
102
|
+
<p><strong>If you are a human:</strong> your AI agent needs an AgentVisa — it takes about 3 minutes
|
|
103
|
+
at <a href="https://agentvisa.ai/signup">agentvisa.ai/signup</a>.</p>
|
|
104
|
+
</body></html>`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Append attribution params to the redirect URL so the landing page — and your
|
|
108
|
+
* analytics — know which site sent the agent: `?w=<widgetId>&from=<host>`.
|
|
109
|
+
* widget_id maps to a registered domain, so this is clean attribution with no PII.
|
|
110
|
+
* (We deliberately pass only the host, never the full path, which could carry the
|
|
111
|
+
* customer site's own query params.)
|
|
112
|
+
*/
|
|
113
|
+
export function buildRedirectUrl(redirectUrl, widgetId, host) {
|
|
114
|
+
try {
|
|
115
|
+
const u = new URL(redirectUrl);
|
|
116
|
+
u.searchParams.set("w", widgetId);
|
|
117
|
+
if (host)
|
|
118
|
+
u.searchParams.set("from", host);
|
|
119
|
+
return u.toString();
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
const sep = redirectUrl.includes("?") ? "&" : "?";
|
|
123
|
+
const qs = `w=${encodeURIComponent(widgetId)}` + (host ? `&from=${encodeURIComponent(host)}` : "");
|
|
124
|
+
return `${redirectUrl}${sep}${qs}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
64
127
|
/**
|
|
65
128
|
* Call /v1/verify with a TemporaryToken.
|
|
66
129
|
*
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAC3D,MAAM,CAAC,MAAM,oBAAoB,GAAG,
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/express/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ interface Res {
|
|
|
31
31
|
status(code: number): Res;
|
|
32
32
|
json(body: unknown): void;
|
|
33
33
|
setHeader(name: string, value: string): void;
|
|
34
|
+
type(contentType: string): Res;
|
|
35
|
+
send(body: string): void;
|
|
34
36
|
}
|
|
35
37
|
type NextFn = (err?: unknown) => void;
|
|
36
38
|
export declare function agentVisa(config: AgentVisaConfig): (req: Req, res: Res, next: NextFn) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAA0F,MAAM,YAAY,CAAC;AAEnJ,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAI9C,UAAU,GAAG;IACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE,YAAY,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,UAAU,GAAG;IACX,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;IAC1B,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAEtC,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,IAI7C,KAAK,GAAG,EACR,KAAK,GAAG,EACR,MAAM,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAoHjB"}
|
package/dist/express/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* req.agentVisa.result — full VerifyResult (if verified)
|
|
18
18
|
* req.agentVisa.reason — failure reason (if not verified + passthrough mode)
|
|
19
19
|
*/
|
|
20
|
-
import { callVerify, resolveConfig, isLikelyAiAgent } from "../core.js";
|
|
20
|
+
import { callVerify, resolveConfig, isLikelyAiAgent, wantsHtml, challengeHtml, buildRedirectUrl } from "../core.js";
|
|
21
21
|
export function agentVisa(config) {
|
|
22
22
|
const resolved = resolveConfig(config);
|
|
23
23
|
return async function agentVisaMiddleware(req, res, next) {
|
|
@@ -27,6 +27,9 @@ export function agentVisa(config) {
|
|
|
27
27
|
const rawAssertion = req.headers["agentvisa-assertion"];
|
|
28
28
|
const rawToken = rawAssertion ?? req.headers["x-agentvisa-token"];
|
|
29
29
|
const token = Array.isArray(rawToken) ? rawToken[0] : rawToken;
|
|
30
|
+
// The blocking host — passed to the redirect for attribution (?from=).
|
|
31
|
+
const hostHeader = req.headers["host"];
|
|
32
|
+
const host = Array.isArray(hostHeader) ? hostHeader[0] : hostHeader;
|
|
30
33
|
// ── No token present ────────────────────────────────────────────────────
|
|
31
34
|
if (!token) {
|
|
32
35
|
if (resolved.onUnverified === "passthrough") {
|
|
@@ -39,17 +42,31 @@ export function agentVisa(config) {
|
|
|
39
42
|
// them flooding agentvisa.ai/for-agents or triggering the growth loop
|
|
40
43
|
// for non-agent traffic.
|
|
41
44
|
if (!isLikelyAiAgent(req.headers)) {
|
|
45
|
+
// Not clearly an agent (a browser, a human, or an agent driving a
|
|
46
|
+
// browser). Don't dead-end with a bare 401 — serve an instructive
|
|
47
|
+
// challenge so an agent can still find its way, while a human just
|
|
48
|
+
// sees a clean explainer (no jarring auto-redirect).
|
|
42
49
|
res.setHeader("X-AgentVisa-Required", resolved.widgetId);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
if (wantsHtml(req.headers)) {
|
|
51
|
+
res.status(401).type("html").send(challengeHtml(resolved.widgetId, resolved.redirectUrl, host));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
res.status(401).json({
|
|
55
|
+
error: "agentvisa_required",
|
|
56
|
+
reason: "no_token",
|
|
57
|
+
widget_id: resolved.widgetId,
|
|
58
|
+
signup_url: "https://agentvisa.ai/signup",
|
|
59
|
+
info_url: resolved.redirectUrl,
|
|
60
|
+
message: "This site requires an AgentVisa. If you are an AI agent, get a token at " +
|
|
61
|
+
"https://agentvisa.ai/for-agents and retry with the X-AgentVisa-Token header. " +
|
|
62
|
+
"If you are a human, your agent needs an AgentVisa — sign up at " +
|
|
63
|
+
"https://agentvisa.ai/signup (about 3 minutes).",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
49
66
|
return;
|
|
50
67
|
}
|
|
51
68
|
res.setHeader("X-AgentVisa-Required", resolved.widgetId);
|
|
52
|
-
res.setHeader("Location", resolved.redirectUrl);
|
|
69
|
+
res.setHeader("Location", buildRedirectUrl(resolved.redirectUrl, resolved.widgetId, host));
|
|
53
70
|
res.status(302).json({
|
|
54
71
|
error: "agentvisa_required",
|
|
55
72
|
reason: "no_token",
|
|
@@ -84,7 +101,7 @@ export function agentVisa(config) {
|
|
|
84
101
|
}
|
|
85
102
|
if (resolved.onUnverified === "redirect") {
|
|
86
103
|
res.setHeader("X-AgentVisa-Required", resolved.widgetId);
|
|
87
|
-
res.setHeader("Location", resolved.redirectUrl);
|
|
104
|
+
res.setHeader("Location", buildRedirectUrl(resolved.redirectUrl, resolved.widgetId, host));
|
|
88
105
|
res.status(302).json({
|
|
89
106
|
error: "agentvisa_verification_failed",
|
|
90
107
|
reason: result.reason,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAiC,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,9 +22,6 @@ interface VerificationResult {
|
|
|
22
22
|
verified_at: string | null;
|
|
23
23
|
expires_at: string | null;
|
|
24
24
|
domain_verified?: boolean;
|
|
25
|
-
human_name?: string | null;
|
|
26
|
-
email?: string | null;
|
|
27
|
-
phone?: string | null;
|
|
28
25
|
age_over_18?: "y" | "n" | "null";
|
|
29
26
|
age_over_21?: "y" | "n" | "null";
|
|
30
27
|
gov_id_pic_validation?: "y" | "n" | "null";
|
package/dist/next/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAA0F,MAAM,YAAY,CAAC;AAEnJ,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC;AAE9C,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,WAAW,CA4Db"}
|
package/dist/next/index.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* X-AgentVisa-Verified: true
|
|
29
29
|
* X-AgentVisa-Reason: ok
|
|
30
30
|
*/
|
|
31
|
-
import { callVerify, resolveConfig, isLikelyAiAgent } from "../core.js";
|
|
31
|
+
import { callVerify, resolveConfig, isLikelyAiAgent, wantsHtml, challengeHtml, buildRedirectUrl } from "../core.js";
|
|
32
32
|
/**
|
|
33
33
|
* Wrap a Next.js middleware handler (or use standalone).
|
|
34
34
|
*
|
|
@@ -45,6 +45,8 @@ export function withAgentVisa(config, handler) {
|
|
|
45
45
|
const token = request.headers.get("agentvisa-assertion") ??
|
|
46
46
|
request.headers.get("x-agentvisa-token") ??
|
|
47
47
|
undefined;
|
|
48
|
+
// The blocking host — passed to the redirect for attribution (?from=).
|
|
49
|
+
const host = request.headers.get("host") ?? undefined;
|
|
48
50
|
// Convert Headers to a plain object so we can forward Signature-Input
|
|
49
51
|
const forwardHeaders = {};
|
|
50
52
|
request.headers.forEach((value, key) => { forwardHeaders[key] = value; });
|
|
@@ -62,9 +64,11 @@ export function withAgentVisa(config, handler) {
|
|
|
62
64
|
const reqHeaders = {};
|
|
63
65
|
request.headers.forEach((v, k) => { reqHeaders[k] = v; });
|
|
64
66
|
if (!isLikelyAiAgent(reqHeaders)) {
|
|
65
|
-
|
|
67
|
+
// Not clearly an agent — serve an instructive challenge instead of a
|
|
68
|
+
// bare 401 so browser-driving agents aren't dead-ended.
|
|
69
|
+
return challengeResponse(resolved.widgetId, resolved.redirectUrl, wantsHtml(reqHeaders), host);
|
|
66
70
|
}
|
|
67
|
-
return redirectResponse(resolved.widgetId, "no_token", resolved.redirectUrl);
|
|
71
|
+
return redirectResponse(resolved.widgetId, "no_token", resolved.redirectUrl, host);
|
|
68
72
|
}
|
|
69
73
|
return blockedResponse(resolved.widgetId, "no_token", resolved.redirectUrl);
|
|
70
74
|
}
|
|
@@ -76,7 +80,7 @@ export function withAgentVisa(config, handler) {
|
|
|
76
80
|
return handler ? handler(req) : passthroughResponse(req);
|
|
77
81
|
}
|
|
78
82
|
if (resolved.onUnverified === "redirect") {
|
|
79
|
-
return redirectResponse(resolved.widgetId, result.reason, resolved.redirectUrl);
|
|
83
|
+
return redirectResponse(resolved.widgetId, result.reason, resolved.redirectUrl, host);
|
|
80
84
|
}
|
|
81
85
|
return blockedResponse(resolved.widgetId, result.reason, resolved.redirectUrl);
|
|
82
86
|
}
|
|
@@ -86,7 +90,7 @@ export function withAgentVisa(config, handler) {
|
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
89
|
-
function redirectResponse(widgetId, reason, redirectUrl) {
|
|
93
|
+
function redirectResponse(widgetId, reason, redirectUrl, host) {
|
|
90
94
|
const isVerificationFailure = reason !== "no_token";
|
|
91
95
|
return new Response(JSON.stringify({
|
|
92
96
|
error: isVerificationFailure ? "agentvisa_verification_failed" : "agentvisa_required",
|
|
@@ -101,7 +105,7 @@ function redirectResponse(widgetId, reason, redirectUrl) {
|
|
|
101
105
|
status: 302,
|
|
102
106
|
headers: {
|
|
103
107
|
"Content-Type": "application/json",
|
|
104
|
-
"Location": redirectUrl,
|
|
108
|
+
"Location": buildRedirectUrl(redirectUrl, widgetId, host),
|
|
105
109
|
"X-AgentVisa-Required": widgetId,
|
|
106
110
|
},
|
|
107
111
|
});
|
|
@@ -125,6 +129,34 @@ function blockedResponse(widgetId, reason, redirectUrl) {
|
|
|
125
129
|
},
|
|
126
130
|
});
|
|
127
131
|
}
|
|
132
|
+
function challengeResponse(widgetId, redirectUrl, html, host) {
|
|
133
|
+
if (html) {
|
|
134
|
+
return new Response(challengeHtml(widgetId, redirectUrl, host), {
|
|
135
|
+
status: 401,
|
|
136
|
+
headers: {
|
|
137
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
138
|
+
"X-AgentVisa-Required": widgetId,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return new Response(JSON.stringify({
|
|
143
|
+
error: "agentvisa_required",
|
|
144
|
+
reason: "no_token",
|
|
145
|
+
widget_id: widgetId,
|
|
146
|
+
signup_url: "https://agentvisa.ai/signup",
|
|
147
|
+
info_url: redirectUrl,
|
|
148
|
+
message: "This site requires an AgentVisa. If you are an AI agent, get a token at " +
|
|
149
|
+
"https://agentvisa.ai/for-agents and retry with the X-AgentVisa-Token header. " +
|
|
150
|
+
"If you are a human, your agent needs an AgentVisa — sign up at " +
|
|
151
|
+
"https://agentvisa.ai/signup (about 3 minutes).",
|
|
152
|
+
}), {
|
|
153
|
+
status: 401,
|
|
154
|
+
headers: {
|
|
155
|
+
"Content-Type": "application/json",
|
|
156
|
+
"X-AgentVisa-Required": widgetId,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
}
|
|
128
160
|
function addVerificationHeaders(request, verified, reason) {
|
|
129
161
|
// Clone the request and add verification headers so downstream handlers
|
|
130
162
|
// can read them via request.headers.get("x-agentvisa-verified")
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAiC,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"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"}
|
package/dist/widget.esm.js
CHANGED
package/dist/widget.js
CHANGED