auth-agents 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +264 -259
- package/dist/index.d.mts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +9 -5
- package/dist/index.mjs +9 -5
- package/package.json +46 -46
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Auth-Agents
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Auth-Agents
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,273 +1,278 @@
|
|
|
1
|
-
# auth-agents
|
|
2
|
-
|
|
3
|
-
Verify AI agent identities with [Agent Auth](https://usevigil.dev). DID-based authentication using Ed25519 and Verifiable Credentials.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install auth-agents
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Quick Start — Verify a Credential
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { AuthAgents } from "auth-agents"
|
|
15
|
-
|
|
16
|
-
const client = new AuthAgents()
|
|
17
|
-
|
|
18
|
-
const result = await client.verify("eyJhbGciOiJFZERTQSJ9...")
|
|
19
|
-
|
|
20
|
-
if (result.valid) {
|
|
21
|
-
console.log(result.did) // "did:key:z6Mk..."
|
|
22
|
-
console.log(result.agent_name) // "Claude"
|
|
23
|
-
console.log(result.agent_model) // "claude-opus-4-6"
|
|
24
|
-
console.log(result.agent_provider) // "Anthropic"
|
|
25
|
-
console.log(result.agent_purpose) // "Research assistant"
|
|
26
|
-
console.log(result.key_origin) // "server_generated" | "client_provided"
|
|
27
|
-
console.log(result.expires_at) // "2026-02-27T01:58:15.000Z"
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Identity Registration Flows
|
|
32
|
-
|
|
33
|
-
Agent Auth supports two registration paths. Both produce the same DID, credential, and challenge-response authentication. The difference is who holds the private key.
|
|
34
|
-
|
|
35
|
-
### Flow A — Server-Generated Keys (zero friction)
|
|
36
|
-
|
|
37
|
-
The server generates an Ed25519 key pair. You receive the private key once and must store it securely. `key_origin` will be `"server_generated"`.
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { AuthAgents } from "auth-agents"
|
|
41
|
-
|
|
42
|
-
const client = new AuthAgents()
|
|
43
|
-
|
|
44
|
-
// 1. Register — server generates keys
|
|
45
|
-
const identity = await client.register({
|
|
46
|
-
agent_name: "Claude",
|
|
47
|
-
agent_model: "claude-opus-4-6",
|
|
48
|
-
agent_provider: "Anthropic",
|
|
49
|
-
agent_purpose: "Research assistant",
|
|
50
|
-
})
|
|
51
|
-
// identity.did — "did:key:z6Mk..."
|
|
52
|
-
// identity.credential — VC-JWT (present to websites)
|
|
53
|
-
// identity.key_origin — "server_generated"
|
|
54
|
-
// identity.private_key_jwk — SAVE THIS. Server does not keep it.
|
|
55
|
-
|
|
56
|
-
// 2. Request a challenge
|
|
57
|
-
const challenge = await client.challenge(identity.did)
|
|
58
|
-
|
|
59
|
-
// 3. Sign the nonce with the private key
|
|
60
|
-
const signature = await AuthAgents.signChallenge(
|
|
61
|
-
identity.private_key_jwk!,
|
|
62
|
-
challenge.nonce
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
// 4. Authenticate and receive a fresh credential
|
|
66
|
-
const auth = await client.authenticate({
|
|
67
|
-
challenge_id: challenge.challenge_id,
|
|
68
|
-
did: identity.did,
|
|
69
|
-
signature,
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
if (auth.valid) {
|
|
73
|
-
console.log(auth.credential) // fresh VC-JWT
|
|
74
|
-
console.log(auth.session_token) // session ID
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Flow B — Bring Your Own Key (BYOK)
|
|
79
|
-
|
|
80
|
-
Generate the key pair locally. The private key never leaves your process. `key_origin` will be `"client_provided"`.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { AuthAgents } from "auth-agents"
|
|
84
|
-
|
|
85
|
-
const client = new AuthAgents()
|
|
86
|
-
|
|
87
|
-
// 1. Generate an Ed25519 key pair locally
|
|
88
|
-
const keyPair = await AuthAgents.generateKeyPair()
|
|
89
|
-
// keyPair.publicKeyJwk — { kty, crv, x }
|
|
90
|
-
// keyPair.privateKeyJwk — { kty, crv, x, d } ← never sent to server
|
|
91
|
-
|
|
92
|
-
// 2. Register with your public key (no private_key_jwk returned)
|
|
93
|
-
const identity = await client.register({
|
|
94
|
-
agent_name: "Claude",
|
|
95
|
-
agent_model: "claude-opus-4-6",
|
|
96
|
-
agent_provider: "Anthropic",
|
|
97
|
-
agent_purpose: "Research assistant",
|
|
98
|
-
public_key_jwk: keyPair.publicKeyJwk,
|
|
99
|
-
})
|
|
100
|
-
// identity.did — "did:key:z6Mk..."
|
|
101
|
-
// identity.credential — VC-JWT
|
|
102
|
-
// identity.key_origin — "client_provided"
|
|
103
|
-
|
|
104
|
-
// 3. Request a challenge
|
|
105
|
-
const challenge = await client.challenge(identity.did)
|
|
106
|
-
|
|
107
|
-
// 4. Sign the nonce with your private key
|
|
108
|
-
const signature = await AuthAgents.signChallenge(
|
|
109
|
-
keyPair.privateKeyJwk,
|
|
110
|
-
challenge.nonce
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
// 5. Authenticate and receive a fresh credential
|
|
114
|
-
const auth = await client.authenticate({
|
|
115
|
-
challenge_id: challenge.challenge_id,
|
|
116
|
-
did: identity.did,
|
|
117
|
-
signature,
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
if (auth.valid) {
|
|
121
|
-
console.log(auth.credential) // fresh VC-JWT
|
|
122
|
-
console.log(auth.session_token) // session ID
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Website Integration (Next.js)
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// app/api/auth/agent-login/route.ts
|
|
130
|
-
import { AuthAgents } from "auth-agents"
|
|
131
|
-
|
|
132
|
-
const authAgents = new AuthAgents()
|
|
133
|
-
|
|
134
|
-
export async function POST(request: Request) {
|
|
135
|
-
const { credential } = await request.json()
|
|
136
|
-
|
|
137
|
-
const result = await authAgents.verify(credential)
|
|
138
|
-
|
|
139
|
-
if (!result.valid) {
|
|
140
|
-
return Response.json({ error: result.message }, { status: 401 })
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Create a session with the verified agent identity
|
|
144
|
-
const sessionId = crypto.randomUUID()
|
|
145
|
-
await db.insert("sessions", {
|
|
146
|
-
session_id: sessionId,
|
|
147
|
-
did: result.did,
|
|
148
|
-
agent_name: result.agent_name,
|
|
149
|
-
agent_model: result.agent_model,
|
|
150
|
-
key_origin: result.key_origin,
|
|
151
|
-
expires_at: result.expires_at,
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
return Response.json({ authenticated: true, session_id: sessionId })
|
|
155
|
-
}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Website Integration (Express.js)
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
import express from "express"
|
|
162
|
-
import { AuthAgents } from "auth-agents"
|
|
163
|
-
|
|
164
|
-
const app = express()
|
|
165
|
-
const authAgents = new AuthAgents()
|
|
166
|
-
|
|
167
|
-
app.post("/auth/agent-login", express.json(), async (req, res) => {
|
|
168
|
-
const { credential } = req.body
|
|
169
|
-
const result = await authAgents.verify(credential)
|
|
170
|
-
|
|
171
|
-
if (!result.valid) {
|
|
172
|
-
return res.status(401).json({ error: result.message })
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Agent verified — create session
|
|
176
|
-
req.session.agent = {
|
|
177
|
-
did: result.did,
|
|
178
|
-
name: result.agent_name,
|
|
179
|
-
model: result.agent_model,
|
|
180
|
-
key_origin: result.key_origin,
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
res.json({ authenticated: true, agent_name: result.agent_name })
|
|
184
|
-
})
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## API
|
|
188
|
-
|
|
189
|
-
### `new AuthAgents(config?)`
|
|
190
|
-
|
|
191
|
-
- `config.baseUrl` — API base URL (default: `https://auth.usevigil.dev`). The SDK enforces HTTPS for all API communication. HTTP is only allowed for `localhost` during development.
|
|
192
|
-
|
|
193
|
-
### `AuthAgents.generateKeyPair()` — Static
|
|
194
|
-
|
|
195
|
-
Generate a local Ed25519 key pair using the Web Crypto API. No network call.
|
|
196
|
-
|
|
197
|
-
Returns `Ed25519KeyPair`:
|
|
198
|
-
```typescript
|
|
199
|
-
{
|
|
200
|
-
publicKeyJwk: { kty: string; crv: string; x: string }
|
|
201
|
-
privateKeyJwk: { kty: string; crv: string; x: string; d: string }
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### `AuthAgents.signChallenge(privateKeyJwk, nonce)` — Static
|
|
206
|
-
|
|
207
|
-
Sign an authentication challenge nonce with an Ed25519 private key. No network call.
|
|
208
|
-
|
|
209
|
-
- `privateKeyJwk` — JWK with `d` parameter (the private key from `generateKeyPair()` or from `register()`)
|
|
210
|
-
- `nonce` — the nonce string from `client.challenge()`
|
|
211
|
-
|
|
212
|
-
Returns a `Promise<string>` — base64url-encoded Ed25519 signature.
|
|
213
|
-
|
|
214
|
-
### `client.verify(credential)` — Verify a VC-JWT credential
|
|
215
|
-
|
|
216
|
-
Returns `VerifyResult` on success:
|
|
217
|
-
```typescript
|
|
218
|
-
{
|
|
219
|
-
valid: true
|
|
220
|
-
did: string
|
|
221
|
-
agent_name: string | null
|
|
222
|
-
agent_model: string | null
|
|
223
|
-
agent_provider: string | null
|
|
224
|
-
agent_purpose: string | null
|
|
225
|
-
key_fingerprint: string | null
|
|
226
|
-
key_origin: string | null // "server_generated" | "client_provided"
|
|
227
|
-
issued_at: string | null
|
|
228
|
-
expires_at: string | null
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Returns `VerifyError` (HTTP 401) without throwing:
|
|
233
|
-
```typescript
|
|
234
|
-
{
|
|
235
|
-
valid: false
|
|
236
|
-
error: "credential_expired" | "invalid_issuer" | "signature_invalid" | "credential_revoked"
|
|
237
|
-
message: string
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### `client.register(input)` — Register a new agent identity
|
|
242
|
-
|
|
243
|
-
`input.public_key_jwk` is optional. Omit it for server-generated keys (Flow A). Provide it for BYOK (Flow B).
|
|
244
|
-
You can also pass:
|
|
245
|
-
- `
|
|
246
|
-
|
|
247
|
-
|
|
1
|
+
# auth-agents
|
|
2
|
+
|
|
3
|
+
Verify AI agent identities with [Agent Auth](https://usevigil.dev). DID-based authentication using Ed25519 and Verifiable Credentials.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install auth-agents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start — Verify a Credential
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AuthAgents } from "auth-agents"
|
|
15
|
+
|
|
16
|
+
const client = new AuthAgents()
|
|
17
|
+
|
|
18
|
+
const result = await client.verify("eyJhbGciOiJFZERTQSJ9...")
|
|
19
|
+
|
|
20
|
+
if (result.valid) {
|
|
21
|
+
console.log(result.did) // "did:key:z6Mk..."
|
|
22
|
+
console.log(result.agent_name) // "Claude"
|
|
23
|
+
console.log(result.agent_model) // "claude-opus-4-6"
|
|
24
|
+
console.log(result.agent_provider) // "Anthropic"
|
|
25
|
+
console.log(result.agent_purpose) // "Research assistant"
|
|
26
|
+
console.log(result.key_origin) // "server_generated" | "client_provided"
|
|
27
|
+
console.log(result.expires_at) // "2026-02-27T01:58:15.000Z"
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Identity Registration Flows
|
|
32
|
+
|
|
33
|
+
Agent Auth supports two registration paths. Both produce the same DID, credential, and challenge-response authentication. The difference is who holds the private key.
|
|
34
|
+
|
|
35
|
+
### Flow A — Server-Generated Keys (zero friction)
|
|
36
|
+
|
|
37
|
+
The server generates an Ed25519 key pair. You receive the private key once and must store it securely. `key_origin` will be `"server_generated"`.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { AuthAgents } from "auth-agents"
|
|
41
|
+
|
|
42
|
+
const client = new AuthAgents()
|
|
43
|
+
|
|
44
|
+
// 1. Register — server generates keys
|
|
45
|
+
const identity = await client.register({
|
|
46
|
+
agent_name: "Claude",
|
|
47
|
+
agent_model: "claude-opus-4-6",
|
|
48
|
+
agent_provider: "Anthropic",
|
|
49
|
+
agent_purpose: "Research assistant",
|
|
50
|
+
})
|
|
51
|
+
// identity.did — "did:key:z6Mk..."
|
|
52
|
+
// identity.credential — VC-JWT (present to websites)
|
|
53
|
+
// identity.key_origin — "server_generated"
|
|
54
|
+
// identity.private_key_jwk — SAVE THIS. Server does not keep it.
|
|
55
|
+
|
|
56
|
+
// 2. Request a challenge
|
|
57
|
+
const challenge = await client.challenge(identity.did)
|
|
58
|
+
|
|
59
|
+
// 3. Sign the nonce with the private key
|
|
60
|
+
const signature = await AuthAgents.signChallenge(
|
|
61
|
+
identity.private_key_jwk!,
|
|
62
|
+
challenge.nonce
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// 4. Authenticate and receive a fresh credential
|
|
66
|
+
const auth = await client.authenticate({
|
|
67
|
+
challenge_id: challenge.challenge_id,
|
|
68
|
+
did: identity.did,
|
|
69
|
+
signature,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
if (auth.valid) {
|
|
73
|
+
console.log(auth.credential) // fresh VC-JWT
|
|
74
|
+
console.log(auth.session_token) // session ID
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Flow B — Bring Your Own Key (BYOK)
|
|
79
|
+
|
|
80
|
+
Generate the key pair locally. The private key never leaves your process. `key_origin` will be `"client_provided"`.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { AuthAgents } from "auth-agents"
|
|
84
|
+
|
|
85
|
+
const client = new AuthAgents()
|
|
86
|
+
|
|
87
|
+
// 1. Generate an Ed25519 key pair locally
|
|
88
|
+
const keyPair = await AuthAgents.generateKeyPair()
|
|
89
|
+
// keyPair.publicKeyJwk — { kty, crv, x }
|
|
90
|
+
// keyPair.privateKeyJwk — { kty, crv, x, d } ← never sent to server
|
|
91
|
+
|
|
92
|
+
// 2. Register with your public key (no private_key_jwk returned)
|
|
93
|
+
const identity = await client.register({
|
|
94
|
+
agent_name: "Claude",
|
|
95
|
+
agent_model: "claude-opus-4-6",
|
|
96
|
+
agent_provider: "Anthropic",
|
|
97
|
+
agent_purpose: "Research assistant",
|
|
98
|
+
public_key_jwk: keyPair.publicKeyJwk,
|
|
99
|
+
})
|
|
100
|
+
// identity.did — "did:key:z6Mk..."
|
|
101
|
+
// identity.credential — VC-JWT
|
|
102
|
+
// identity.key_origin — "client_provided"
|
|
103
|
+
|
|
104
|
+
// 3. Request a challenge
|
|
105
|
+
const challenge = await client.challenge(identity.did)
|
|
106
|
+
|
|
107
|
+
// 4. Sign the nonce with your private key
|
|
108
|
+
const signature = await AuthAgents.signChallenge(
|
|
109
|
+
keyPair.privateKeyJwk,
|
|
110
|
+
challenge.nonce
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// 5. Authenticate and receive a fresh credential
|
|
114
|
+
const auth = await client.authenticate({
|
|
115
|
+
challenge_id: challenge.challenge_id,
|
|
116
|
+
did: identity.did,
|
|
117
|
+
signature,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
if (auth.valid) {
|
|
121
|
+
console.log(auth.credential) // fresh VC-JWT
|
|
122
|
+
console.log(auth.session_token) // session ID
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Website Integration (Next.js)
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// app/api/auth/agent-login/route.ts
|
|
130
|
+
import { AuthAgents } from "auth-agents"
|
|
131
|
+
|
|
132
|
+
const authAgents = new AuthAgents()
|
|
133
|
+
|
|
134
|
+
export async function POST(request: Request) {
|
|
135
|
+
const { credential } = await request.json()
|
|
136
|
+
|
|
137
|
+
const result = await authAgents.verify(credential)
|
|
138
|
+
|
|
139
|
+
if (!result.valid) {
|
|
140
|
+
return Response.json({ error: result.message }, { status: 401 })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Create a session with the verified agent identity
|
|
144
|
+
const sessionId = crypto.randomUUID()
|
|
145
|
+
await db.insert("sessions", {
|
|
146
|
+
session_id: sessionId,
|
|
147
|
+
did: result.did,
|
|
148
|
+
agent_name: result.agent_name,
|
|
149
|
+
agent_model: result.agent_model,
|
|
150
|
+
key_origin: result.key_origin,
|
|
151
|
+
expires_at: result.expires_at,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
return Response.json({ authenticated: true, session_id: sessionId })
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Website Integration (Express.js)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import express from "express"
|
|
162
|
+
import { AuthAgents } from "auth-agents"
|
|
163
|
+
|
|
164
|
+
const app = express()
|
|
165
|
+
const authAgents = new AuthAgents()
|
|
166
|
+
|
|
167
|
+
app.post("/auth/agent-login", express.json(), async (req, res) => {
|
|
168
|
+
const { credential } = req.body
|
|
169
|
+
const result = await authAgents.verify(credential)
|
|
170
|
+
|
|
171
|
+
if (!result.valid) {
|
|
172
|
+
return res.status(401).json({ error: result.message })
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Agent verified — create session
|
|
176
|
+
req.session.agent = {
|
|
177
|
+
did: result.did,
|
|
178
|
+
name: result.agent_name,
|
|
179
|
+
model: result.agent_model,
|
|
180
|
+
key_origin: result.key_origin,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
res.json({ authenticated: true, agent_name: result.agent_name })
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## API
|
|
188
|
+
|
|
189
|
+
### `new AuthAgents(config?)`
|
|
190
|
+
|
|
191
|
+
- `config.baseUrl` — API base URL (default: `https://auth.usevigil.dev`). The SDK enforces HTTPS for all API communication. HTTP is only allowed for `localhost` during development.
|
|
192
|
+
|
|
193
|
+
### `AuthAgents.generateKeyPair()` — Static
|
|
194
|
+
|
|
195
|
+
Generate a local Ed25519 key pair using the Web Crypto API. No network call.
|
|
196
|
+
|
|
197
|
+
Returns `Ed25519KeyPair`:
|
|
198
|
+
```typescript
|
|
199
|
+
{
|
|
200
|
+
publicKeyJwk: { kty: string; crv: string; x: string }
|
|
201
|
+
privateKeyJwk: { kty: string; crv: string; x: string; d: string }
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `AuthAgents.signChallenge(privateKeyJwk, nonce)` — Static
|
|
206
|
+
|
|
207
|
+
Sign an authentication challenge nonce with an Ed25519 private key. No network call.
|
|
208
|
+
|
|
209
|
+
- `privateKeyJwk` — JWK with `d` parameter (the private key from `generateKeyPair()` or from `register()`)
|
|
210
|
+
- `nonce` — the nonce string from `client.challenge()`
|
|
211
|
+
|
|
212
|
+
Returns a `Promise<string>` — base64url-encoded Ed25519 signature.
|
|
213
|
+
|
|
214
|
+
### `client.verify(credential)` — Verify a VC-JWT credential
|
|
215
|
+
|
|
216
|
+
Returns `VerifyResult` on success:
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
valid: true
|
|
220
|
+
did: string
|
|
221
|
+
agent_name: string | null
|
|
222
|
+
agent_model: string | null
|
|
223
|
+
agent_provider: string | null
|
|
224
|
+
agent_purpose: string | null
|
|
225
|
+
key_fingerprint: string | null
|
|
226
|
+
key_origin: string | null // "server_generated" | "client_provided"
|
|
227
|
+
issued_at: string | null
|
|
228
|
+
expires_at: string | null
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Returns `VerifyError` (HTTP 401) without throwing:
|
|
233
|
+
```typescript
|
|
234
|
+
{
|
|
235
|
+
valid: false
|
|
236
|
+
error: "credential_expired" | "invalid_issuer" | "signature_invalid" | "credential_revoked"
|
|
237
|
+
message: string
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `client.register(input)` — Register a new agent identity
|
|
242
|
+
|
|
243
|
+
`input.public_key_jwk` is optional. Omit it for server-generated keys (Flow A). Provide it for BYOK (Flow B).
|
|
244
|
+
You can also pass:
|
|
245
|
+
- `metadata` — key/value metadata map (max key/value sizes enforced server-side)
|
|
246
|
+
|
|
248
247
|
Returns `RegisterResult`:
|
|
249
248
|
```typescript
|
|
250
249
|
{
|
|
251
250
|
did: string
|
|
252
|
-
credential: string
|
|
253
|
-
key_fingerprint: string
|
|
251
|
+
credential: string
|
|
252
|
+
key_fingerprint: string
|
|
254
253
|
key_origin?: "server_generated" | "client_provided"
|
|
255
254
|
private_key_jwk?: { kty, crv, x, d } // only present for server_generated
|
|
256
255
|
}
|
|
257
256
|
```
|
|
258
257
|
|
|
259
|
-
|
|
258
|
+
Registration always returns a credential with the server default lifetime (24 hours).
|
|
259
|
+
If you need a different lifetime, pass `credential_expires_in` to `client.challenge(...)`.
|
|
260
260
|
|
|
261
|
-
`site_id`
|
|
261
|
+
### `client.challenge(did, site_id?, credential_expires_in?)` — Request an auth challenge
|
|
262
262
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
`input` supports optional `credential_expires_in` to customize the issued credential lifetime (`0` means non-expiring).
|
|
266
|
-
|
|
267
|
-
## Documentation
|
|
263
|
+
`site_id` is optional and scopes the challenge/session when your deployment uses site-level org provisioning.
|
|
268
264
|
|
|
269
|
-
|
|
265
|
+
`credential_expires_in` is optional and allows website developers to set the credential lifetime in seconds for the resulting authentication. Use `0` for non-expiring credentials. Min 300, max 2592000.
|
|
270
266
|
|
|
271
|
-
|
|
267
|
+
### `client.authenticate(input)` — Submit signed challenge
|
|
272
268
|
|
|
273
|
-
|
|
269
|
+
`input` contains `challenge_id`, `did`, and `signature` (base64url-encoded).
|
|
270
|
+
The credential returned from `client.authenticate(...)` uses the lifetime chosen in the preceding `client.challenge(...)` call.
|
|
271
|
+
|
|
272
|
+
## Documentation
|
|
273
|
+
|
|
274
|
+
Full API reference at [usevigil.dev/docs](https://usevigil.dev/docs/)
|
|
275
|
+
|
|
276
|
+
## License
|
|
277
|
+
|
|
278
|
+
MIT - Agent Auth
|
package/dist/index.d.mts
CHANGED
|
@@ -44,7 +44,6 @@ interface RegisterInput {
|
|
|
44
44
|
crv: string;
|
|
45
45
|
x: string;
|
|
46
46
|
};
|
|
47
|
-
credential_expires_in?: number;
|
|
48
47
|
metadata?: Record<string, string>;
|
|
49
48
|
}
|
|
50
49
|
interface RegisterResult {
|
|
@@ -69,7 +68,6 @@ interface AuthVerifyInput {
|
|
|
69
68
|
challenge_id: string;
|
|
70
69
|
did: string;
|
|
71
70
|
signature: string;
|
|
72
|
-
credential_expires_in?: number;
|
|
73
71
|
}
|
|
74
72
|
interface AuthVerifyResult {
|
|
75
73
|
valid: true;
|
|
@@ -141,13 +139,14 @@ declare class AuthAgents {
|
|
|
141
139
|
*
|
|
142
140
|
* @param did - The agent's DID
|
|
143
141
|
* @param site_id - Optional site scope for provisioned deployments
|
|
142
|
+
* @param credential_expires_in - Optional credential lifetime in seconds (set by website developer). Use 0 for non-expiring. Min 300, max 2592000.
|
|
144
143
|
* @returns Challenge ID and nonce to sign
|
|
145
144
|
*/
|
|
146
|
-
challenge(did: string, site_id?: string): Promise<ChallengeResult>;
|
|
145
|
+
challenge(did: string, site_id?: string, credential_expires_in?: number): Promise<ChallengeResult>;
|
|
147
146
|
/**
|
|
148
147
|
* Submit a signed challenge to authenticate and receive a fresh credential.
|
|
149
148
|
*
|
|
150
|
-
* @param input - challenge_id, did, base64url signature
|
|
149
|
+
* @param input - challenge_id, did, and base64url signature
|
|
151
150
|
* @returns Session token and fresh VC-JWT credential. If API returns 401,
|
|
152
151
|
* returns `{ valid: false, error, message }` instead of throwing.
|
|
153
152
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,6 @@ interface RegisterInput {
|
|
|
44
44
|
crv: string;
|
|
45
45
|
x: string;
|
|
46
46
|
};
|
|
47
|
-
credential_expires_in?: number;
|
|
48
47
|
metadata?: Record<string, string>;
|
|
49
48
|
}
|
|
50
49
|
interface RegisterResult {
|
|
@@ -69,7 +68,6 @@ interface AuthVerifyInput {
|
|
|
69
68
|
challenge_id: string;
|
|
70
69
|
did: string;
|
|
71
70
|
signature: string;
|
|
72
|
-
credential_expires_in?: number;
|
|
73
71
|
}
|
|
74
72
|
interface AuthVerifyResult {
|
|
75
73
|
valid: true;
|
|
@@ -141,13 +139,14 @@ declare class AuthAgents {
|
|
|
141
139
|
*
|
|
142
140
|
* @param did - The agent's DID
|
|
143
141
|
* @param site_id - Optional site scope for provisioned deployments
|
|
142
|
+
* @param credential_expires_in - Optional credential lifetime in seconds (set by website developer). Use 0 for non-expiring. Min 300, max 2592000.
|
|
144
143
|
* @returns Challenge ID and nonce to sign
|
|
145
144
|
*/
|
|
146
|
-
challenge(did: string, site_id?: string): Promise<ChallengeResult>;
|
|
145
|
+
challenge(did: string, site_id?: string, credential_expires_in?: number): Promise<ChallengeResult>;
|
|
147
146
|
/**
|
|
148
147
|
* Submit a signed challenge to authenticate and receive a fresh credential.
|
|
149
148
|
*
|
|
150
|
-
* @param input - challenge_id, did, base64url signature
|
|
149
|
+
* @param input - challenge_id, did, and base64url signature
|
|
151
150
|
* @returns Session token and fresh VC-JWT credential. If API returns 401,
|
|
152
151
|
* returns `{ valid: false, error, message }` instead of throwing.
|
|
153
152
|
*/
|
package/dist/index.js
CHANGED
|
@@ -152,7 +152,7 @@ var AuthAgents = class {
|
|
|
152
152
|
if (!res.ok) {
|
|
153
153
|
const body = await res.json().catch(() => ({}));
|
|
154
154
|
throw new Error(
|
|
155
|
-
body.error ?? `Registration failed (${res.status})`
|
|
155
|
+
body.error_description ?? body.error ?? `Registration failed (${res.status})`
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
158
|
return res.json();
|
|
@@ -163,19 +163,23 @@ var AuthAgents = class {
|
|
|
163
163
|
*
|
|
164
164
|
* @param did - The agent's DID
|
|
165
165
|
* @param site_id - Optional site scope for provisioned deployments
|
|
166
|
+
* @param credential_expires_in - Optional credential lifetime in seconds (set by website developer). Use 0 for non-expiring. Min 300, max 2592000.
|
|
166
167
|
* @returns Challenge ID and nonce to sign
|
|
167
168
|
*/
|
|
168
|
-
async challenge(did, site_id) {
|
|
169
|
+
async challenge(did, site_id, credential_expires_in) {
|
|
170
|
+
const payload = { did };
|
|
171
|
+
if (site_id) payload.site_id = site_id;
|
|
172
|
+
if (credential_expires_in !== void 0) payload.credential_expires_in = credential_expires_in;
|
|
169
173
|
const res = await fetch(`${this.baseUrl}/v1/auth/challenge`, {
|
|
170
174
|
method: "POST",
|
|
171
175
|
headers: { "Content-Type": "application/json" },
|
|
172
|
-
body: JSON.stringify(
|
|
176
|
+
body: JSON.stringify(payload),
|
|
173
177
|
signal: AbortSignal.timeout(1e4)
|
|
174
178
|
});
|
|
175
179
|
if (!res.ok) {
|
|
176
180
|
const body = await res.json().catch(() => ({}));
|
|
177
181
|
throw new Error(
|
|
178
|
-
body.error ?? `Challenge failed (${res.status})`
|
|
182
|
+
body.error_description ?? body.error ?? `Challenge failed (${res.status})`
|
|
179
183
|
);
|
|
180
184
|
}
|
|
181
185
|
return res.json();
|
|
@@ -183,7 +187,7 @@ var AuthAgents = class {
|
|
|
183
187
|
/**
|
|
184
188
|
* Submit a signed challenge to authenticate and receive a fresh credential.
|
|
185
189
|
*
|
|
186
|
-
* @param input - challenge_id, did, base64url signature
|
|
190
|
+
* @param input - challenge_id, did, and base64url signature
|
|
187
191
|
* @returns Session token and fresh VC-JWT credential. If API returns 401,
|
|
188
192
|
* returns `{ valid: false, error, message }` instead of throwing.
|
|
189
193
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -127,7 +127,7 @@ var AuthAgents = class {
|
|
|
127
127
|
if (!res.ok) {
|
|
128
128
|
const body = await res.json().catch(() => ({}));
|
|
129
129
|
throw new Error(
|
|
130
|
-
body.error ?? `Registration failed (${res.status})`
|
|
130
|
+
body.error_description ?? body.error ?? `Registration failed (${res.status})`
|
|
131
131
|
);
|
|
132
132
|
}
|
|
133
133
|
return res.json();
|
|
@@ -138,19 +138,23 @@ var AuthAgents = class {
|
|
|
138
138
|
*
|
|
139
139
|
* @param did - The agent's DID
|
|
140
140
|
* @param site_id - Optional site scope for provisioned deployments
|
|
141
|
+
* @param credential_expires_in - Optional credential lifetime in seconds (set by website developer). Use 0 for non-expiring. Min 300, max 2592000.
|
|
141
142
|
* @returns Challenge ID and nonce to sign
|
|
142
143
|
*/
|
|
143
|
-
async challenge(did, site_id) {
|
|
144
|
+
async challenge(did, site_id, credential_expires_in) {
|
|
145
|
+
const payload = { did };
|
|
146
|
+
if (site_id) payload.site_id = site_id;
|
|
147
|
+
if (credential_expires_in !== void 0) payload.credential_expires_in = credential_expires_in;
|
|
144
148
|
const res = await fetch(`${this.baseUrl}/v1/auth/challenge`, {
|
|
145
149
|
method: "POST",
|
|
146
150
|
headers: { "Content-Type": "application/json" },
|
|
147
|
-
body: JSON.stringify(
|
|
151
|
+
body: JSON.stringify(payload),
|
|
148
152
|
signal: AbortSignal.timeout(1e4)
|
|
149
153
|
});
|
|
150
154
|
if (!res.ok) {
|
|
151
155
|
const body = await res.json().catch(() => ({}));
|
|
152
156
|
throw new Error(
|
|
153
|
-
body.error ?? `Challenge failed (${res.status})`
|
|
157
|
+
body.error_description ?? body.error ?? `Challenge failed (${res.status})`
|
|
154
158
|
);
|
|
155
159
|
}
|
|
156
160
|
return res.json();
|
|
@@ -158,7 +162,7 @@ var AuthAgents = class {
|
|
|
158
162
|
/**
|
|
159
163
|
* Submit a signed challenge to authenticate and receive a fresh credential.
|
|
160
164
|
*
|
|
161
|
-
* @param input - challenge_id, did, base64url signature
|
|
165
|
+
* @param input - challenge_id, did, and base64url signature
|
|
162
166
|
* @returns Session token and fresh VC-JWT credential. If API returns 401,
|
|
163
167
|
* returns `{ valid: false, error, message }` instead of throwing.
|
|
164
168
|
*/
|
package/package.json
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "auth-agents",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Verify AI agent identities with Agent Auth. DID-based authentication using Ed25519 and Verifiable Credentials.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"types": "./dist/index.d.ts",
|
|
10
|
-
"import": "./dist/index.mjs",
|
|
11
|
-
"require": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
22
|
-
},
|
|
23
|
-
"keywords": [
|
|
24
|
-
"agent-auth",
|
|
25
|
-
"usevigil",
|
|
26
|
-
"auth-agents",
|
|
27
|
-
"ai-agent",
|
|
28
|
-
"did",
|
|
29
|
-
"verifiable-credential",
|
|
30
|
-
"ed25519",
|
|
31
|
-
"authentication",
|
|
32
|
-
"identity",
|
|
33
|
-
"decentralized-identity"
|
|
34
|
-
],
|
|
35
|
-
"author": "Zeliang Cheng",
|
|
36
|
-
"license": "MIT",
|
|
37
|
-
"repository": {
|
|
38
|
-
"type": "git",
|
|
39
|
-
"url": "https://github.com/AgenthAgent/auth-agents-sdk-node"
|
|
40
|
-
},
|
|
41
|
-
"homepage": "https://usevigil.dev",
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"tsup": "^8.0.0",
|
|
44
|
-
"typescript": "^5.0.0"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "auth-agents",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "Verify AI agent identities with Agent Auth. DID-based authentication using Ed25519 and Verifiable Credentials.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"agent-auth",
|
|
25
|
+
"usevigil",
|
|
26
|
+
"auth-agents",
|
|
27
|
+
"ai-agent",
|
|
28
|
+
"did",
|
|
29
|
+
"verifiable-credential",
|
|
30
|
+
"ed25519",
|
|
31
|
+
"authentication",
|
|
32
|
+
"identity",
|
|
33
|
+
"decentralized-identity"
|
|
34
|
+
],
|
|
35
|
+
"author": "Zeliang Cheng",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/AgenthAgent/auth-agents-sdk-node"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://usevigil.dev",
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsup": "^8.0.0",
|
|
44
|
+
"typescript": "^5.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|