auth-agents 0.1.3 → 0.4.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
  # auth-agents
2
2
 
3
- Verify AI agent identities with [Agent Auth](https://getagentauth.com). DID-based authentication using Ed25519 and Verifiable Credentials.
3
+ Verify AI agent identities with [Agent Auth](https://usevigil.dev). DID-based authentication using Ed25519 and Verifiable Credentials.
4
4
 
5
5
  ## Install
6
6
 
@@ -23,14 +23,110 @@ if (result.valid) {
23
23
  console.log(result.agent_model) // "claude-opus-4-6"
24
24
  console.log(result.agent_provider) // "Anthropic"
25
25
  console.log(result.agent_purpose) // "Research assistant"
26
+ console.log(result.key_origin) // "server_generated" | "client_provided"
26
27
  console.log(result.expires_at) // "2026-02-27T01:58:15.000Z"
27
28
  }
28
29
  ```
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
+
30
126
  ## Website Integration (Next.js)
31
127
 
32
128
  ```typescript
33
- // app/api/auth/agent/route.ts
129
+ // app/api/auth/agent-login/route.ts
34
130
  import { AuthAgents } from "auth-agents"
35
131
 
36
132
  const authAgents = new AuthAgents()
@@ -51,6 +147,7 @@ export async function POST(request: Request) {
51
147
  did: result.did,
52
148
  agent_name: result.agent_name,
53
149
  agent_model: result.agent_model,
150
+ key_origin: result.key_origin,
54
151
  expires_at: result.expires_at,
55
152
  })
56
153
 
@@ -67,7 +164,7 @@ import { AuthAgents } from "auth-agents"
67
164
  const app = express()
68
165
  const authAgents = new AuthAgents()
69
166
 
70
- app.post("/auth/agent", express.json(), async (req, res) => {
167
+ app.post("/auth/agent-login", express.json(), async (req, res) => {
71
168
  const { credential } = req.body
72
169
  const result = await authAgents.verify(credential)
73
170
 
@@ -80,57 +177,96 @@ app.post("/auth/agent", express.json(), async (req, res) => {
80
177
  did: result.did,
81
178
  name: result.agent_name,
82
179
  model: result.agent_model,
180
+ key_origin: result.key_origin,
83
181
  }
84
182
 
85
183
  res.json({ authenticated: true, agent_name: result.agent_name })
86
184
  })
87
185
  ```
88
186
 
89
- ## Full Agent Auth Flow
187
+ ## API
90
188
 
91
- ```typescript
92
- import { AuthAgents } from "auth-agents"
189
+ ### `new AuthAgents(config?)`
93
190
 
94
- const client = new AuthAgents()
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.
95
192
 
96
- // 1. Register
97
- const identity = await client.register({
98
- agent_name: "Claude",
99
- agent_model: "claude-opus-4-6",
100
- agent_provider: "Anthropic",
101
- agent_purpose: "Research assistant",
102
- })
193
+ ### `AuthAgents.generateKeyPair()` — Static
103
194
 
104
- // 2. Request challenge
105
- const challenge = await client.challenge(identity.did)
195
+ Generate a local Ed25519 key pair using the Web Crypto API. No network call.
106
196
 
107
- // 3. Sign nonce and authenticate
108
- const auth = await client.authenticate({
109
- challenge_id: challenge.challenge_id,
110
- did: identity.did,
111
- signature: signedNonce, // base64url Ed25519 signature of challenge.nonce
112
- })
113
- // auth.credential — fresh VC-JWT
114
- // auth.session_token — session ID
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
+ }
115
203
  ```
116
204
 
117
- ## API
205
+ ### `AuthAgents.signChallenge(privateKeyJwk, nonce)` — Static
118
206
 
119
- ### `new AuthAgents(config?)`
207
+ Sign an authentication challenge nonce with an Ed25519 private key. No network call.
120
208
 
121
- - `config.baseUrl` — API base URL (default: `https://auth.getagentauth.com`)
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.
122
213
 
123
214
  ### `client.verify(credential)` — Verify a VC-JWT credential
124
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
+
125
241
  ### `client.register(input)` — Register a new agent identity
126
242
 
127
- ### `client.challenge(did)` Request an auth challenge
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
+ - `credential_expires_in` — credential lifetime in seconds (`0` means non-expiring)
246
+ - `metadata` — key/value metadata map (max key/value sizes enforced server-side)
247
+
248
+ Returns `RegisterResult`:
249
+ ```typescript
250
+ {
251
+ did: string
252
+ credential: string
253
+ key_fingerprint: string
254
+ key_origin?: "server_generated" | "client_provided"
255
+ private_key_jwk?: { kty, crv, x, d } // only present for server_generated
256
+ }
257
+ ```
258
+
259
+ ### `client.challenge(did, site_id?)` — Request an auth challenge
260
+
261
+ `site_id` is optional and scopes the challenge/session when your deployment uses site-level org provisioning.
128
262
 
129
263
  ### `client.authenticate(input)` — Submit signed challenge
130
264
 
265
+ `input` supports optional `credential_expires_in` to customize the issued credential lifetime (`0` means non-expiring).
266
+
131
267
  ## Documentation
132
268
 
133
- Full API reference at [getagentauth.com/docs](https://getagentauth.com/docs/)
269
+ Full API reference at [usevigil.dev/docs](https://usevigil.dev/docs/)
134
270
 
135
271
  ## License
136
272
 
package/dist/index.d.mts CHANGED
@@ -1,7 +1,21 @@
1
1
  interface AuthAgentsConfig {
2
- /** Base URL of the Agent Auth API. Defaults to https://auth.getagentauth.com */
2
+ /** Base URL of the Agent Auth API. Defaults to https://auth.usevigil.dev */
3
3
  baseUrl?: string;
4
4
  }
5
+ /** Ed25519 key pair in JWK format, returned by generateKeyPair() */
6
+ interface Ed25519KeyPair {
7
+ publicKeyJwk: {
8
+ kty: string;
9
+ crv: string;
10
+ x: string;
11
+ };
12
+ privateKeyJwk: {
13
+ kty: string;
14
+ crv: string;
15
+ x: string;
16
+ d: string;
17
+ };
18
+ }
5
19
  interface VerifyResult {
6
20
  valid: true;
7
21
  did: string;
@@ -12,10 +26,11 @@ interface VerifyResult {
12
26
  key_fingerprint: string | null;
13
27
  issued_at: string | null;
14
28
  expires_at: string | null;
29
+ key_origin: string | null;
15
30
  }
16
31
  interface VerifyError {
17
32
  valid: false;
18
- error: "credential_expired" | "invalid_issuer" | "signature_invalid";
33
+ error: "credential_expired" | "invalid_issuer" | "signature_invalid" | "credential_revoked";
19
34
  message: string;
20
35
  }
21
36
  type VerifyResponse = VerifyResult | VerifyError;
@@ -29,11 +44,14 @@ interface RegisterInput {
29
44
  crv: string;
30
45
  x: string;
31
46
  };
47
+ credential_expires_in?: number;
48
+ metadata?: Record<string, string>;
32
49
  }
33
50
  interface RegisterResult {
34
51
  did: string;
35
52
  credential: string;
36
53
  key_fingerprint: string;
54
+ key_origin?: "server_generated" | "client_provided";
37
55
  private_key_jwk?: {
38
56
  kty: string;
39
57
  crv: string;
@@ -51,6 +69,7 @@ interface AuthVerifyInput {
51
69
  challenge_id: string;
52
70
  did: string;
53
71
  signature: string;
72
+ credential_expires_in?: number;
54
73
  }
55
74
  interface AuthVerifyResult {
56
75
  valid: true;
@@ -75,33 +94,62 @@ type AuthVerifyResponse = AuthVerifyResult | AuthVerifyError;
75
94
  declare class AuthAgents {
76
95
  private baseUrl;
77
96
  constructor(config?: AuthAgentsConfig);
97
+ /**
98
+ * Generate an Ed25519 key pair using the Web Crypto API.
99
+ * Use the returned public key JWK for BYOK registration and the private key
100
+ * JWK with signChallenge() to complete headless authentication.
101
+ *
102
+ * SECURITY: Store the private key securely. Never transmit it over the wire.
103
+ *
104
+ * @returns Ed25519 key pair in JWK format
105
+ */
106
+ static generateKeyPair(): Promise<Ed25519KeyPair>;
107
+ /**
108
+ * Sign an authentication challenge nonce with an Ed25519 private key.
109
+ * The nonce is signed as UTF-8 encoded text (NOT hex-decoded bytes).
110
+ * Returns a base64url-encoded signature string.
111
+ *
112
+ * @param privateKeyJwk - The agent's private key in JWK format (must include `d`)
113
+ * @param nonce - The challenge nonce string returned by client.challenge()
114
+ * @returns base64url-encoded Ed25519 signature
115
+ */
116
+ static signChallenge(privateKeyJwk: {
117
+ kty: string;
118
+ crv: string;
119
+ x: string;
120
+ d: string;
121
+ }, nonce: string): Promise<string>;
78
122
  /**
79
123
  * Verify a VC-JWT credential issued by Agent Auth.
80
124
  *
81
125
  * @param credential - The VC-JWT string from the agent
82
- * @returns Verified agent identity or error details
126
+ * @returns Verified agent identity. If API returns 401, returns
127
+ * `{ valid: false, error, message }` instead of throwing.
83
128
  */
84
129
  verify(credential: string): Promise<VerifyResponse>;
85
130
  /**
86
- * Register a new agent identity. The server generates an Ed25519 keypair
87
- * and returns a DID, credential, and private key.
131
+ * Register a new agent identity. By default the server generates an Ed25519
132
+ * keypair and returns the private key. Pass `public_key_jwk` to use your own
133
+ * key (BYOK) — in that case no private key is returned.
88
134
  *
89
- * @param input - Agent metadata (name, model, provider, purpose)
90
- * @returns DID, credential, and private key (save securely!)
135
+ * @param input - Agent metadata and optional public key JWK for BYOK
136
+ * @returns DID, credential, key_origin, and private key if server-generated
91
137
  */
92
138
  register(input: RegisterInput): Promise<RegisterResult>;
93
139
  /**
94
140
  * Request an authentication challenge nonce.
95
141
  *
96
142
  * @param did - The agent's DID
143
+ * @param site_id - Optional site scope for provisioned deployments
97
144
  * @returns Challenge ID and nonce to sign
98
145
  */
99
- challenge(did: string): Promise<ChallengeResult>;
146
+ challenge(did: string, site_id?: string): Promise<ChallengeResult>;
100
147
  /**
101
148
  * Submit a signed challenge to authenticate and receive a fresh credential.
102
149
  *
103
- * @param input - challenge_id, did, and base64url signature
104
- * @returns Session token and fresh VC-JWT credential
150
+ * @param input - challenge_id, did, base64url signature, and optional credential_expires_in
151
+ * @returns Session token and fresh VC-JWT credential. If API returns 401,
152
+ * returns `{ valid: false, error, message }` instead of throwing.
105
153
  */
106
154
  authenticate(input: AuthVerifyInput): Promise<AuthVerifyResponse>;
107
155
  }
@@ -111,4 +159,4 @@ declare class AuthAgents {
111
159
  */
112
160
  declare function verify(credential: string): Promise<VerifyResponse>;
113
161
 
114
- export { AuthAgents, type AuthAgentsConfig, type AuthVerifyError, type AuthVerifyInput, type AuthVerifyResponse, type AuthVerifyResult, type ChallengeResult, type RegisterInput, type RegisterResult, type VerifyError, type VerifyResponse, type VerifyResult, verify };
162
+ export { AuthAgents, type AuthAgentsConfig, type AuthVerifyError, type AuthVerifyInput, type AuthVerifyResponse, type AuthVerifyResult, type ChallengeResult, type Ed25519KeyPair, type RegisterInput, type RegisterResult, type VerifyError, type VerifyResponse, type VerifyResult, verify };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,21 @@
1
1
  interface AuthAgentsConfig {
2
- /** Base URL of the Agent Auth API. Defaults to https://auth.getagentauth.com */
2
+ /** Base URL of the Agent Auth API. Defaults to https://auth.usevigil.dev */
3
3
  baseUrl?: string;
4
4
  }
5
+ /** Ed25519 key pair in JWK format, returned by generateKeyPair() */
6
+ interface Ed25519KeyPair {
7
+ publicKeyJwk: {
8
+ kty: string;
9
+ crv: string;
10
+ x: string;
11
+ };
12
+ privateKeyJwk: {
13
+ kty: string;
14
+ crv: string;
15
+ x: string;
16
+ d: string;
17
+ };
18
+ }
5
19
  interface VerifyResult {
6
20
  valid: true;
7
21
  did: string;
@@ -12,10 +26,11 @@ interface VerifyResult {
12
26
  key_fingerprint: string | null;
13
27
  issued_at: string | null;
14
28
  expires_at: string | null;
29
+ key_origin: string | null;
15
30
  }
16
31
  interface VerifyError {
17
32
  valid: false;
18
- error: "credential_expired" | "invalid_issuer" | "signature_invalid";
33
+ error: "credential_expired" | "invalid_issuer" | "signature_invalid" | "credential_revoked";
19
34
  message: string;
20
35
  }
21
36
  type VerifyResponse = VerifyResult | VerifyError;
@@ -29,11 +44,14 @@ interface RegisterInput {
29
44
  crv: string;
30
45
  x: string;
31
46
  };
47
+ credential_expires_in?: number;
48
+ metadata?: Record<string, string>;
32
49
  }
33
50
  interface RegisterResult {
34
51
  did: string;
35
52
  credential: string;
36
53
  key_fingerprint: string;
54
+ key_origin?: "server_generated" | "client_provided";
37
55
  private_key_jwk?: {
38
56
  kty: string;
39
57
  crv: string;
@@ -51,6 +69,7 @@ interface AuthVerifyInput {
51
69
  challenge_id: string;
52
70
  did: string;
53
71
  signature: string;
72
+ credential_expires_in?: number;
54
73
  }
55
74
  interface AuthVerifyResult {
56
75
  valid: true;
@@ -75,33 +94,62 @@ type AuthVerifyResponse = AuthVerifyResult | AuthVerifyError;
75
94
  declare class AuthAgents {
76
95
  private baseUrl;
77
96
  constructor(config?: AuthAgentsConfig);
97
+ /**
98
+ * Generate an Ed25519 key pair using the Web Crypto API.
99
+ * Use the returned public key JWK for BYOK registration and the private key
100
+ * JWK with signChallenge() to complete headless authentication.
101
+ *
102
+ * SECURITY: Store the private key securely. Never transmit it over the wire.
103
+ *
104
+ * @returns Ed25519 key pair in JWK format
105
+ */
106
+ static generateKeyPair(): Promise<Ed25519KeyPair>;
107
+ /**
108
+ * Sign an authentication challenge nonce with an Ed25519 private key.
109
+ * The nonce is signed as UTF-8 encoded text (NOT hex-decoded bytes).
110
+ * Returns a base64url-encoded signature string.
111
+ *
112
+ * @param privateKeyJwk - The agent's private key in JWK format (must include `d`)
113
+ * @param nonce - The challenge nonce string returned by client.challenge()
114
+ * @returns base64url-encoded Ed25519 signature
115
+ */
116
+ static signChallenge(privateKeyJwk: {
117
+ kty: string;
118
+ crv: string;
119
+ x: string;
120
+ d: string;
121
+ }, nonce: string): Promise<string>;
78
122
  /**
79
123
  * Verify a VC-JWT credential issued by Agent Auth.
80
124
  *
81
125
  * @param credential - The VC-JWT string from the agent
82
- * @returns Verified agent identity or error details
126
+ * @returns Verified agent identity. If API returns 401, returns
127
+ * `{ valid: false, error, message }` instead of throwing.
83
128
  */
84
129
  verify(credential: string): Promise<VerifyResponse>;
85
130
  /**
86
- * Register a new agent identity. The server generates an Ed25519 keypair
87
- * and returns a DID, credential, and private key.
131
+ * Register a new agent identity. By default the server generates an Ed25519
132
+ * keypair and returns the private key. Pass `public_key_jwk` to use your own
133
+ * key (BYOK) — in that case no private key is returned.
88
134
  *
89
- * @param input - Agent metadata (name, model, provider, purpose)
90
- * @returns DID, credential, and private key (save securely!)
135
+ * @param input - Agent metadata and optional public key JWK for BYOK
136
+ * @returns DID, credential, key_origin, and private key if server-generated
91
137
  */
92
138
  register(input: RegisterInput): Promise<RegisterResult>;
93
139
  /**
94
140
  * Request an authentication challenge nonce.
95
141
  *
96
142
  * @param did - The agent's DID
143
+ * @param site_id - Optional site scope for provisioned deployments
97
144
  * @returns Challenge ID and nonce to sign
98
145
  */
99
- challenge(did: string): Promise<ChallengeResult>;
146
+ challenge(did: string, site_id?: string): Promise<ChallengeResult>;
100
147
  /**
101
148
  * Submit a signed challenge to authenticate and receive a fresh credential.
102
149
  *
103
- * @param input - challenge_id, did, and base64url signature
104
- * @returns Session token and fresh VC-JWT credential
150
+ * @param input - challenge_id, did, base64url signature, and optional credential_expires_in
151
+ * @returns Session token and fresh VC-JWT credential. If API returns 401,
152
+ * returns `{ valid: false, error, message }` instead of throwing.
105
153
  */
106
154
  authenticate(input: AuthVerifyInput): Promise<AuthVerifyResponse>;
107
155
  }
@@ -111,4 +159,4 @@ declare class AuthAgents {
111
159
  */
112
160
  declare function verify(credential: string): Promise<VerifyResponse>;
113
161
 
114
- export { AuthAgents, type AuthAgentsConfig, type AuthVerifyError, type AuthVerifyInput, type AuthVerifyResponse, type AuthVerifyResult, type ChallengeResult, type RegisterInput, type RegisterResult, type VerifyError, type VerifyResponse, type VerifyResult, verify };
162
+ export { AuthAgents, type AuthAgentsConfig, type AuthVerifyError, type AuthVerifyInput, type AuthVerifyResponse, type AuthVerifyResult, type ChallengeResult, type Ed25519KeyPair, type RegisterInput, type RegisterResult, type VerifyError, type VerifyResponse, type VerifyResult, verify };
package/dist/index.js CHANGED
@@ -24,39 +24,130 @@ __export(index_exports, {
24
24
  verify: () => verify
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
- var DEFAULT_BASE_URL = "https://auth.getagentauth.com";
27
+ var DEFAULT_BASE_URL = "https://auth.usevigil.dev";
28
28
  var AuthAgents = class {
29
29
  constructor(config) {
30
- this.baseUrl = (config?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
30
+ const url = (config?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
31
+ try {
32
+ const parsed = new URL(url);
33
+ if (parsed.protocol !== "https:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
34
+ throw new Error("AuthAgents: baseUrl must use HTTPS (http://localhost allowed for development)");
35
+ }
36
+ } catch (e) {
37
+ if (e instanceof Error && e.message.startsWith("AuthAgents:")) throw e;
38
+ throw new Error("AuthAgents: baseUrl must be a valid URL");
39
+ }
40
+ this.baseUrl = url;
41
+ }
42
+ // ---- Static Crypto Helpers ---------------------------------------------
43
+ /**
44
+ * Generate an Ed25519 key pair using the Web Crypto API.
45
+ * Use the returned public key JWK for BYOK registration and the private key
46
+ * JWK with signChallenge() to complete headless authentication.
47
+ *
48
+ * SECURITY: Store the private key securely. Never transmit it over the wire.
49
+ *
50
+ * @returns Ed25519 key pair in JWK format
51
+ */
52
+ static async generateKeyPair() {
53
+ const keyPair = await crypto.subtle.generateKey(
54
+ "Ed25519",
55
+ true,
56
+ // extractable
57
+ ["sign", "verify"]
58
+ );
59
+ const publicJwk = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
60
+ const privateJwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
61
+ return {
62
+ publicKeyJwk: {
63
+ kty: publicJwk.kty,
64
+ crv: publicJwk.crv,
65
+ x: publicJwk.x
66
+ },
67
+ privateKeyJwk: {
68
+ kty: privateJwk.kty,
69
+ crv: privateJwk.crv,
70
+ x: privateJwk.x,
71
+ d: privateJwk.d
72
+ }
73
+ };
74
+ }
75
+ /**
76
+ * Sign an authentication challenge nonce with an Ed25519 private key.
77
+ * The nonce is signed as UTF-8 encoded text (NOT hex-decoded bytes).
78
+ * Returns a base64url-encoded signature string.
79
+ *
80
+ * @param privateKeyJwk - The agent's private key in JWK format (must include `d`)
81
+ * @param nonce - The challenge nonce string returned by client.challenge()
82
+ * @returns base64url-encoded Ed25519 signature
83
+ */
84
+ static async signChallenge(privateKeyJwk, nonce) {
85
+ if (!privateKeyJwk?.d) {
86
+ throw new Error("privateKeyJwk must contain the 'd' (private key) parameter");
87
+ }
88
+ if (!nonce) {
89
+ throw new Error("nonce must not be empty");
90
+ }
91
+ const key = await crypto.subtle.importKey(
92
+ "jwk",
93
+ { ...privateKeyJwk, key_ops: ["sign"] },
94
+ "Ed25519",
95
+ false,
96
+ ["sign"]
97
+ );
98
+ const data = new TextEncoder().encode(nonce);
99
+ const signatureBuffer = await crypto.subtle.sign("Ed25519", key, data);
100
+ const bytes = new Uint8Array(signatureBuffer);
101
+ let binary = "";
102
+ for (let i = 0; i < bytes.length; i++) {
103
+ binary += String.fromCharCode(bytes[i]);
104
+ }
105
+ const base64 = btoa(binary);
106
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
31
107
  }
32
108
  // ---- Credential Verification (for websites) ----------------------------
33
109
  /**
34
110
  * Verify a VC-JWT credential issued by Agent Auth.
35
111
  *
36
112
  * @param credential - The VC-JWT string from the agent
37
- * @returns Verified agent identity or error details
113
+ * @returns Verified agent identity. If API returns 401, returns
114
+ * `{ valid: false, error, message }` instead of throwing.
38
115
  */
39
116
  async verify(credential) {
40
117
  const res = await fetch(`${this.baseUrl}/v1/credentials/verify`, {
41
118
  method: "POST",
42
119
  headers: { "Content-Type": "application/json" },
43
- body: JSON.stringify({ credential })
120
+ body: JSON.stringify({ credential }),
121
+ signal: AbortSignal.timeout(1e4)
44
122
  });
45
- return res.json();
123
+ const body = await res.json().catch(() => ({}));
124
+ if (res.status === 401) {
125
+ return {
126
+ valid: false,
127
+ error: body.error ?? "signature_invalid",
128
+ message: body.message ?? "Credential verification failed"
129
+ };
130
+ }
131
+ if (!res.ok) {
132
+ throw new Error(body.error ?? body.message ?? `Verify failed (${res.status})`);
133
+ }
134
+ return body;
46
135
  }
47
136
  // ---- Agent Registration ------------------------------------------------
48
137
  /**
49
- * Register a new agent identity. The server generates an Ed25519 keypair
50
- * and returns a DID, credential, and private key.
138
+ * Register a new agent identity. By default the server generates an Ed25519
139
+ * keypair and returns the private key. Pass `public_key_jwk` to use your own
140
+ * key (BYOK) — in that case no private key is returned.
51
141
  *
52
- * @param input - Agent metadata (name, model, provider, purpose)
53
- * @returns DID, credential, and private key (save securely!)
142
+ * @param input - Agent metadata and optional public key JWK for BYOK
143
+ * @returns DID, credential, key_origin, and private key if server-generated
54
144
  */
55
145
  async register(input) {
56
146
  const res = await fetch(`${this.baseUrl}/v1/identities`, {
57
147
  method: "POST",
58
148
  headers: { "Content-Type": "application/json" },
59
- body: JSON.stringify(input)
149
+ body: JSON.stringify(input),
150
+ signal: AbortSignal.timeout(1e4)
60
151
  });
61
152
  if (!res.ok) {
62
153
  const body = await res.json().catch(() => ({}));
@@ -71,13 +162,15 @@ var AuthAgents = class {
71
162
  * Request an authentication challenge nonce.
72
163
  *
73
164
  * @param did - The agent's DID
165
+ * @param site_id - Optional site scope for provisioned deployments
74
166
  * @returns Challenge ID and nonce to sign
75
167
  */
76
- async challenge(did) {
168
+ async challenge(did, site_id) {
77
169
  const res = await fetch(`${this.baseUrl}/v1/auth/challenge`, {
78
170
  method: "POST",
79
171
  headers: { "Content-Type": "application/json" },
80
- body: JSON.stringify({ did })
172
+ body: JSON.stringify(site_id ? { did, site_id } : { did }),
173
+ signal: AbortSignal.timeout(1e4)
81
174
  });
82
175
  if (!res.ok) {
83
176
  const body = await res.json().catch(() => ({}));
@@ -90,16 +183,29 @@ var AuthAgents = class {
90
183
  /**
91
184
  * Submit a signed challenge to authenticate and receive a fresh credential.
92
185
  *
93
- * @param input - challenge_id, did, and base64url signature
94
- * @returns Session token and fresh VC-JWT credential
186
+ * @param input - challenge_id, did, base64url signature, and optional credential_expires_in
187
+ * @returns Session token and fresh VC-JWT credential. If API returns 401,
188
+ * returns `{ valid: false, error, message }` instead of throwing.
95
189
  */
96
190
  async authenticate(input) {
97
191
  const res = await fetch(`${this.baseUrl}/v1/auth/verify`, {
98
192
  method: "POST",
99
193
  headers: { "Content-Type": "application/json" },
100
- body: JSON.stringify(input)
194
+ body: JSON.stringify(input),
195
+ signal: AbortSignal.timeout(1e4)
101
196
  });
102
- return res.json();
197
+ const body = await res.json().catch(() => ({}));
198
+ if (res.status === 401) {
199
+ return {
200
+ valid: false,
201
+ error: body.error ?? "signature_invalid",
202
+ message: body.message ?? "Authentication failed"
203
+ };
204
+ }
205
+ if (!res.ok) {
206
+ throw new Error(body.error ?? body.message ?? `Authentication failed (${res.status})`);
207
+ }
208
+ return body;
103
209
  }
104
210
  };
105
211
  async function verify(credential) {
package/dist/index.mjs CHANGED
@@ -1,37 +1,128 @@
1
1
  // src/index.ts
2
- var DEFAULT_BASE_URL = "https://auth.getagentauth.com";
2
+ var DEFAULT_BASE_URL = "https://auth.usevigil.dev";
3
3
  var AuthAgents = class {
4
4
  constructor(config) {
5
- this.baseUrl = (config?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
5
+ const url = (config?.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
6
+ try {
7
+ const parsed = new URL(url);
8
+ if (parsed.protocol !== "https:" && parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
9
+ throw new Error("AuthAgents: baseUrl must use HTTPS (http://localhost allowed for development)");
10
+ }
11
+ } catch (e) {
12
+ if (e instanceof Error && e.message.startsWith("AuthAgents:")) throw e;
13
+ throw new Error("AuthAgents: baseUrl must be a valid URL");
14
+ }
15
+ this.baseUrl = url;
16
+ }
17
+ // ---- Static Crypto Helpers ---------------------------------------------
18
+ /**
19
+ * Generate an Ed25519 key pair using the Web Crypto API.
20
+ * Use the returned public key JWK for BYOK registration and the private key
21
+ * JWK with signChallenge() to complete headless authentication.
22
+ *
23
+ * SECURITY: Store the private key securely. Never transmit it over the wire.
24
+ *
25
+ * @returns Ed25519 key pair in JWK format
26
+ */
27
+ static async generateKeyPair() {
28
+ const keyPair = await crypto.subtle.generateKey(
29
+ "Ed25519",
30
+ true,
31
+ // extractable
32
+ ["sign", "verify"]
33
+ );
34
+ const publicJwk = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
35
+ const privateJwk = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
36
+ return {
37
+ publicKeyJwk: {
38
+ kty: publicJwk.kty,
39
+ crv: publicJwk.crv,
40
+ x: publicJwk.x
41
+ },
42
+ privateKeyJwk: {
43
+ kty: privateJwk.kty,
44
+ crv: privateJwk.crv,
45
+ x: privateJwk.x,
46
+ d: privateJwk.d
47
+ }
48
+ };
49
+ }
50
+ /**
51
+ * Sign an authentication challenge nonce with an Ed25519 private key.
52
+ * The nonce is signed as UTF-8 encoded text (NOT hex-decoded bytes).
53
+ * Returns a base64url-encoded signature string.
54
+ *
55
+ * @param privateKeyJwk - The agent's private key in JWK format (must include `d`)
56
+ * @param nonce - The challenge nonce string returned by client.challenge()
57
+ * @returns base64url-encoded Ed25519 signature
58
+ */
59
+ static async signChallenge(privateKeyJwk, nonce) {
60
+ if (!privateKeyJwk?.d) {
61
+ throw new Error("privateKeyJwk must contain the 'd' (private key) parameter");
62
+ }
63
+ if (!nonce) {
64
+ throw new Error("nonce must not be empty");
65
+ }
66
+ const key = await crypto.subtle.importKey(
67
+ "jwk",
68
+ { ...privateKeyJwk, key_ops: ["sign"] },
69
+ "Ed25519",
70
+ false,
71
+ ["sign"]
72
+ );
73
+ const data = new TextEncoder().encode(nonce);
74
+ const signatureBuffer = await crypto.subtle.sign("Ed25519", key, data);
75
+ const bytes = new Uint8Array(signatureBuffer);
76
+ let binary = "";
77
+ for (let i = 0; i < bytes.length; i++) {
78
+ binary += String.fromCharCode(bytes[i]);
79
+ }
80
+ const base64 = btoa(binary);
81
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
6
82
  }
7
83
  // ---- Credential Verification (for websites) ----------------------------
8
84
  /**
9
85
  * Verify a VC-JWT credential issued by Agent Auth.
10
86
  *
11
87
  * @param credential - The VC-JWT string from the agent
12
- * @returns Verified agent identity or error details
88
+ * @returns Verified agent identity. If API returns 401, returns
89
+ * `{ valid: false, error, message }` instead of throwing.
13
90
  */
14
91
  async verify(credential) {
15
92
  const res = await fetch(`${this.baseUrl}/v1/credentials/verify`, {
16
93
  method: "POST",
17
94
  headers: { "Content-Type": "application/json" },
18
- body: JSON.stringify({ credential })
95
+ body: JSON.stringify({ credential }),
96
+ signal: AbortSignal.timeout(1e4)
19
97
  });
20
- return res.json();
98
+ const body = await res.json().catch(() => ({}));
99
+ if (res.status === 401) {
100
+ return {
101
+ valid: false,
102
+ error: body.error ?? "signature_invalid",
103
+ message: body.message ?? "Credential verification failed"
104
+ };
105
+ }
106
+ if (!res.ok) {
107
+ throw new Error(body.error ?? body.message ?? `Verify failed (${res.status})`);
108
+ }
109
+ return body;
21
110
  }
22
111
  // ---- Agent Registration ------------------------------------------------
23
112
  /**
24
- * Register a new agent identity. The server generates an Ed25519 keypair
25
- * and returns a DID, credential, and private key.
113
+ * Register a new agent identity. By default the server generates an Ed25519
114
+ * keypair and returns the private key. Pass `public_key_jwk` to use your own
115
+ * key (BYOK) — in that case no private key is returned.
26
116
  *
27
- * @param input - Agent metadata (name, model, provider, purpose)
28
- * @returns DID, credential, and private key (save securely!)
117
+ * @param input - Agent metadata and optional public key JWK for BYOK
118
+ * @returns DID, credential, key_origin, and private key if server-generated
29
119
  */
30
120
  async register(input) {
31
121
  const res = await fetch(`${this.baseUrl}/v1/identities`, {
32
122
  method: "POST",
33
123
  headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify(input)
124
+ body: JSON.stringify(input),
125
+ signal: AbortSignal.timeout(1e4)
35
126
  });
36
127
  if (!res.ok) {
37
128
  const body = await res.json().catch(() => ({}));
@@ -46,13 +137,15 @@ var AuthAgents = class {
46
137
  * Request an authentication challenge nonce.
47
138
  *
48
139
  * @param did - The agent's DID
140
+ * @param site_id - Optional site scope for provisioned deployments
49
141
  * @returns Challenge ID and nonce to sign
50
142
  */
51
- async challenge(did) {
143
+ async challenge(did, site_id) {
52
144
  const res = await fetch(`${this.baseUrl}/v1/auth/challenge`, {
53
145
  method: "POST",
54
146
  headers: { "Content-Type": "application/json" },
55
- body: JSON.stringify({ did })
147
+ body: JSON.stringify(site_id ? { did, site_id } : { did }),
148
+ signal: AbortSignal.timeout(1e4)
56
149
  });
57
150
  if (!res.ok) {
58
151
  const body = await res.json().catch(() => ({}));
@@ -65,16 +158,29 @@ var AuthAgents = class {
65
158
  /**
66
159
  * Submit a signed challenge to authenticate and receive a fresh credential.
67
160
  *
68
- * @param input - challenge_id, did, and base64url signature
69
- * @returns Session token and fresh VC-JWT credential
161
+ * @param input - challenge_id, did, base64url signature, and optional credential_expires_in
162
+ * @returns Session token and fresh VC-JWT credential. If API returns 401,
163
+ * returns `{ valid: false, error, message }` instead of throwing.
70
164
  */
71
165
  async authenticate(input) {
72
166
  const res = await fetch(`${this.baseUrl}/v1/auth/verify`, {
73
167
  method: "POST",
74
168
  headers: { "Content-Type": "application/json" },
75
- body: JSON.stringify(input)
169
+ body: JSON.stringify(input),
170
+ signal: AbortSignal.timeout(1e4)
76
171
  });
77
- return res.json();
172
+ const body = await res.json().catch(() => ({}));
173
+ if (res.status === 401) {
174
+ return {
175
+ valid: false,
176
+ error: body.error ?? "signature_invalid",
177
+ message: body.message ?? "Authentication failed"
178
+ };
179
+ }
180
+ if (!res.ok) {
181
+ throw new Error(body.error ?? body.message ?? `Authentication failed (${res.status})`);
182
+ }
183
+ return body;
78
184
  }
79
185
  };
80
186
  async function verify(credential) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth-agents",
3
- "version": "0.1.3",
3
+ "version": "0.4.2",
4
4
  "description": "Verify AI agent identities with Agent Auth. DID-based authentication using Ed25519 and Verifiable Credentials.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "keywords": [
24
24
  "agent-auth",
25
- "getagentauth",
25
+ "usevigil",
26
26
  "auth-agents",
27
27
  "ai-agent",
28
28
  "did",
@@ -38,7 +38,7 @@
38
38
  "type": "git",
39
39
  "url": "https://github.com/AgenthAgent/auth-agents-sdk-node"
40
40
  },
41
- "homepage": "https://getagentauth.com",
41
+ "homepage": "https://usevigil.dev",
42
42
  "devDependencies": {
43
43
  "tsup": "^8.0.0",
44
44
  "typescript": "^5.0.0"