@keycardai/eve 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,12 +16,19 @@ Three adapters, each one plugging into an eve primitive instead of wrapping it:
16
16
  pnpm add @keycardai/eve
17
17
  ```
18
18
 
19
- `eve` is a peer dependency, pinned to `>=0.47.3 <0.48.0`. This package was
20
- built and verified against eve `0.47.3`. eve is in public beta and ships
19
+ `eve` is a peer dependency, pinned to `>=0.54.3 <0.55.0`. This package was
20
+ built and verified against eve `0.54.3`. eve is in public beta and ships
21
21
  releases most days, and its connection and auth surfaces are still moving, so
22
22
  the range deliberately stops at the next minor rather than tracking `^`. Widen
23
23
  it only after re-running this package's tests against the newer eve.
24
24
 
25
+ The narrowness is the point rather than an oversight, and so is keeping the
26
+ range current: a consumer who runs a newer eve and installs this anyway —
27
+ `--legacy-peer-deps` — silently loses the check, and the incompatibilities
28
+ surface at build time instead. Two of them are what the 0.47 → 0.54 bump had to
29
+ fix: an `auth` key list that `displayName` is not in, and a `requireAuth`
30
+ options type with no `reason` field.
31
+
25
32
  eve itself declares `engines.node: ">=24"` and is ESM only. This package
26
33
  imports eve for types only (`import type { ... } from "eve/connections"`), so
27
34
  nothing here pulls eve into the runtime and the package builds and tests on
@@ -139,25 +146,56 @@ export default defineMcpClientConnection({
139
146
  description: "Documents the user has authorized.",
140
147
  auth: Keycard.interactive({
141
148
  zoneUrl: process.env.KEYCARD_ZONE_URL!,
142
- clientId: process.env.KEYCARD_CLIENT_ID!,
143
149
  resource: "https://docs.example.com",
144
150
  requestScopes: ["documents.read"],
151
+ connectionName: "Docs",
145
152
  }),
146
153
  });
147
154
  ```
148
155
 
156
+ **There is no `clientId`, and adding one usually breaks the flow.** eve mints a
157
+ fresh callback URL for every attempt
158
+ (`/eve/v1/connections/:name/callback/:attemptId/:token`) while
159
+ [RFC 9700][rfc9700] requires the authorization server to match `redirect_uri`
160
+ against the client's registered list by exact string comparison. A client
161
+ registered ahead of time cannot name a URL that is generated later, so the
162
+ request is rejected with something like `Unauthorized redirect URI`. Leaving
163
+ `clientId` unset registers a client per attempt instead (RFC 7591), whose
164
+ single redirect URI is the callback that attempt will return to. Each client is
165
+ public and PKCE-bound, so nothing secret enters eve's durable resume state; if
166
+ the server issues a confidential client anyway, the attempt is abandoned rather
167
+ than journaling a secret.
168
+
169
+ Set `clientId` only when that client's registered redirect URIs already cover
170
+ eve's callback. Note that a Keycard application `identifier` is not a client id:
171
+ zones identify clients by [Client ID Metadata Document][cimd], an HTTPS URL
172
+ with a path serving the client's metadata, so a value like `urn:app:my-agent`
173
+ fails with `Unsupported client identifier prefix: urn`.
174
+
175
+ Per-attempt clients accumulate in the zone. They hold no secret and their
176
+ callback is single-use, but a server returning `registration_client_uri` and
177
+ `registration_access_token` allows RFC 7592 deletion once a grant settles.
178
+
179
+ [rfc9700]: https://datatracker.ietf.org/doc/html/rfc9700
180
+ [cimd]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-02
181
+
149
182
  The definition implements the same three-method form as eve's
150
183
  `defineInteractiveAuthorization`, over `@keycardai/oauth`'s v3 web-app flow:
151
184
 
152
- - `getToken` returns a token only when this package holds one for the
153
- principal. Otherwise it throws `ConnectionAuthorizationRequiredError`, so eve
154
- emits `authorization.required`, runs `startAuthorization` in a durable step,
155
- and parks the turn on a framework-owned callback.
156
- - `startAuthorization` calls `beginAuthorization` for the connection's resource
157
- list against eve's minted callback URL, and returns the challenge URL plus
158
- the `state` and PKCE verifier as JSON resume state.
185
+ - `getToken` looks up the principal's stored grants and returns the access
186
+ token of one whose resources cover this connection's `resource` and whose
187
+ scopes cover `requestScopes`. Within 60 seconds of expiry, a grant holding a
188
+ refresh token is refreshed first (see below). With no usable grant it throws
189
+ `ConnectionAuthorizationRequiredError`, so eve emits
190
+ `authorization.required`, runs `startAuthorization` in a durable step, and
191
+ parks the turn on a framework-owned callback.
192
+ - `startAuthorization` registers this attempt's client (unless `clientId` is
193
+ set), calls `beginAuthorization` for the connection's resource list against
194
+ eve's minted callback URL, and returns the challenge URL plus the `state`,
195
+ PKCE verifier and client id as JSON resume state.
159
196
  - `completeAuthorization` calls `completeAuthorization` with eve's callback
160
- params and the journaled resume state, and hands eve the token.
197
+ params and the journaled resume state, redeeming as the client that attempt's
198
+ authorization request ran as, and stores the resulting grant.
161
199
 
162
200
  **Resume without authorization cannot yield a credential.** `getToken` is the
163
201
  only path that returns a token, and it reads a store only
@@ -169,6 +207,72 @@ an authorization has settled ends the tool call. User denial is reported as
169
207
  `ConnectionAuthorizationFailedError` with `reason: "access_denied"` and
170
208
  `retryable: false`, so eve stops re-prompting.
171
209
 
210
+ ### Grants: what is stored, and for how long
211
+
212
+ `completeAuthorization` stores a grant, not a bare token: the access token and
213
+ its expiry, the refresh token the zone returned, the client id the grant was
214
+ issued to, and the resources and scopes granted. Grants are keyed by the
215
+ principal, never by `connectionName`, which is display and error text only;
216
+ two `Keycard.interactive()` definitions in one process with the same
217
+ `connectionName` fail at definition time with
218
+ `AuthProviderConfigurationError`. Because a grant is matched on what it covers,
219
+ a connection listing the agent's other resources in `additionalResources`
220
+ yields one sign-in that serves every connection whose resource is in the list.
221
+
222
+ When a grant is within 60 seconds of expiry and holds a refresh token,
223
+ `getToken` posts `grant_type=refresh_token` to the zone as the grant's own
224
+ client (public, no secret; a per-attempt client registers with
225
+ `refresh_token` among its grant types), stores the rotated access and refresh
226
+ token pair, and returns the new access token without parking. A refresh the
227
+ zone refuses with `invalid_grant` deletes the grant and throws `Required`, so
228
+ the turn parks for a fresh sign-in. A transport or 5xx failure keeps the grant
229
+ and fails the tool call with `retryable: true`. A grant without a refresh token
230
+ parks at expiry, as before.
231
+
232
+ `evict` removes every grant of the principal that covers this connection's
233
+ resource, leaving grants for other resources in place.
234
+
235
+ ### Plugging a durable grant store
236
+
237
+ The default store is process-local, so a restarted agent parks once more for
238
+ each user. To keep grants across processes, pass `tokens` an object of this
239
+ shape:
240
+
241
+ ```ts
242
+ import type { AuthorizedGrant, GrantStore } from "@keycardai/eve";
243
+
244
+ export function redisGrantStore(redis: {
245
+ hgetall(key: string): Promise<Record<string, string>>;
246
+ hset(key: string, field: string, value: string): Promise<unknown>;
247
+ hdel(key: string, field: string): Promise<unknown>;
248
+ }): GrantStore {
249
+ const key = (principal: string) => `keycard:grants:${principal}`;
250
+ return {
251
+ async list(principal) {
252
+ const rows = await redis.hgetall(key(principal));
253
+ return Object.values(rows).map((row) => JSON.parse(row) as AuthorizedGrant);
254
+ },
255
+ async put(principal, grant) {
256
+ await redis.hset(key(principal), grant.id, JSON.stringify(grant));
257
+ },
258
+ async remove(principal, grantId) {
259
+ await redis.hdel(key(principal), grantId);
260
+ },
261
+ };
262
+ }
263
+ ```
264
+
265
+ Every method is async, and `put` with an `id` already held replaces that grant
266
+ (a refresh rewrites the rotated pair under the same `id`). The same three
267
+ methods map onto Vercel KV's `hgetall`, `hset` and `hdel`, or any other
268
+ key-value backend.
269
+
270
+ Choosing a durable store puts refresh tokens at rest in that backend. Each one
271
+ is a long-lived credential for the user's resources, so the backend's access
272
+ control and encryption are yours to provide. This package never writes a grant
273
+ into eve's session attributes for that reason: a refresh token must not enter
274
+ eve's durable state.
275
+
172
276
  ## Parity with `@keycardai/langchain`
173
277
 
174
278
  The two packages implement the same Keycard access model against different
@@ -212,8 +316,12 @@ const auth = Keycard.onBehalfOf({
212
316
  `fakeZoneClient()` records every exchange, impersonation, and client
213
317
  credentials call, and can fail one resource or every request. `keycardAuth()`
214
318
  takes a `verify` seam in place of the JWKS-backed verifier, and
215
- `Keycard.interactive()` takes a `flow` seam in place of the two web-flow calls.
216
- An injected `client` or `flow` supersedes `zoneUrl`, so a test needs no zone.
319
+ `Keycard.interactive()` takes a `flow` seam in place of the web-flow calls:
320
+ `begin`, `complete`, `register` for per-attempt clients, and `refresh`. A flow
321
+ without `register` is rejected at construction unless a `clientId` is also
322
+ given, so a test cannot accidentally exercise a combination that cannot work in
323
+ production. An injected `client` or `flow` supersedes `zoneUrl`, so a test needs
324
+ no zone.
217
325
 
218
326
  ## Not included: the Keycard gateway MCP proxy
219
327
 
@@ -1,5 +1,17 @@
1
1
  import type { ConnectionPrincipal, NonInteractiveAuthorizationDefinition } from "eve/connections";
2
2
  import { type KeycardConnectionOptions } from "./config.js";
3
+ /**
4
+ * None of these definitions carry a `displayName`. eve validates an authored
5
+ * connection's `auth` against a closed key list (`completeAuthorization`,
6
+ * `evict`, `getToken`, `principalType`, `startAuthorization`, `vercelConnect`),
7
+ * and a definition carrying any other key fails the build with
8
+ * `The "auth" field Unknown key "displayName"`. The only consumer of the field
9
+ * is eve's `stampChallengeDisplayName`, which resolves
10
+ * `definition.displayName ?? challenge.displayName`, so an interactive
11
+ * definition names itself on the challenge instead (see {@link interactive}),
12
+ * a non-interactive one has no challenge to name, and tools name a provider
13
+ * through `ToolAuthOptions.displayName` at the call site.
14
+ */
3
15
  /** Options for {@link impersonate}. */
4
16
  export interface KeycardImpersonateOptions extends KeycardConnectionOptions {
5
17
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,mBAAmB,EACnB,qCAAqC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAsC,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAKhG,uCAAuC;AACvC,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,mBAAmB,KAAK,MAAM,CAAC,CAAC;CACvE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,wBAAwB,GAChC,qCAAqC,CAmBvC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,wBAAwB,GAChC,qCAAqC,CAmDvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,yBAAyB,GACjC,qCAAqC,CAuCvC"}
1
+ {"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,mBAAmB,EACnB,qCAAqC,EAEtC,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAsC,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAKhG;;;;;;;;;;;GAWG;AAEH,uCAAuC;AACvC,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACzE;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,EAAE,mBAAmB,KAAK,MAAM,CAAC,CAAC;CACvE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,wBAAwB,GAChC,qCAAqC,CAkBvC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,wBAAwB,GAChC,qCAAqC,CAkDvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,yBAAyB,GACjC,qCAAqC,CAsCvC"}
@@ -14,7 +14,6 @@ export function asSelf(options) {
14
14
  const config = resolveConnectionConfig(options, "asSelf");
15
15
  return {
16
16
  principalType: "app",
17
- displayName: config.connectionName,
18
17
  async getToken() {
19
18
  const request = {
20
19
  resource: config.resource,
@@ -44,7 +43,6 @@ export function onBehalfOf(options) {
44
43
  const config = resolveConnectionConfig(options, "onBehalfOf");
45
44
  return {
46
45
  principalType: "user",
47
- displayName: config.connectionName,
48
46
  async getToken({ principal }) {
49
47
  requireUser(principal, config.connectionName);
50
48
  const subjectToken = readSubjectToken(principal, config.subjectTokens);
@@ -103,7 +101,6 @@ export function impersonate(options) {
103
101
  }
104
102
  return {
105
103
  principalType: typeof identifier === "function" ? "user" : "app",
106
- displayName: config.connectionName,
107
104
  async getToken({ principal }) {
108
105
  let userIdentifier;
109
106
  if (typeof identifier === "function") {
@@ -1 +1 @@
1
- {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connections.js","sourceRoot":"","sources":["../../src/connections.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ export type { KeycardAuthOptions, SubjectTokenRetention } from "./auth.js";
19
19
  export { asSelf, impersonate, onBehalfOf } from "./connections.js";
20
20
  export type { KeycardImpersonateOptions } from "./connections.js";
21
21
  export { interactive, memoryAuthorizedTokenStore } from "./interactive.js";
22
- export type { AuthorizedToken, AuthorizedTokenStore, KeycardInteractiveOptions, KeycardResumeState, WebAppFlow, } from "./interactive.js";
22
+ export type { AuthorizedGrant, GrantStore, KeycardInteractiveOptions, KeycardResumeState, RegisterAttemptClientOptions, RegisteredClient, WebAppFlow, } from "./interactive.js";
23
23
  export type { KeycardConnectionOptions } from "./config.js";
24
24
  export { AuthorizationFailedError, AuthorizationRequiredError, FailureReason, RouteRejectedError, } from "./errors.js";
25
25
  export { decodeClaims, subjectTokenExpired } from "./expiry.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE3E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,EAClB,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;EAKlB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAE3E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC3E,YAAY,EACV,eAAe,EACf,UAAU,EACV,yBAAyB,EACzB,kBAAkB,EAClB,4BAA4B,EAC5B,gBAAgB,EAChB,UAAU,GACX,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;EAKlB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAW3E,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,MAAM;IACN,WAAW;IACX,WAAW;IACX,UAAU;CACX,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAa3E,OAAO,EACL,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,MAAM;IACN,WAAW;IACX,WAAW;IACX,UAAU;CACX,CAAC,CAAC"}
@@ -1,90 +1,188 @@
1
- import { type AuthorizationRedirect, type BeginAuthorizationOptions, type CompleteAuthorizationOptions, type TokenResponse } from "@keycardai/oauth";
1
+ import { type AuthorizationRedirect, type BeginAuthorizationOptions, type CompleteAuthorizationOptions, type RefreshAuthorizationOptions, type TokenResponse } from "@keycardai/oauth";
2
2
  import type { InteractiveAuthorizationDefinition, JsonValue } from "eve/connections";
3
3
  /**
4
4
  * The state journaled between eve's begin and callback steps.
5
5
  *
6
6
  * Every field is JSON, so the value satisfies eve's `JsonValue` bound and
7
- * survives the durable step boundary the park crosses.
7
+ * survives the durable step boundary the park crosses. `clientId` is journaled
8
+ * because each attempt may run as its own registered client, and only that
9
+ * client can redeem the code. Nothing secret is journaled: registered clients
10
+ * are public and PKCE-bound (see {@link interactive}).
8
11
  */
9
12
  export interface KeycardResumeState {
10
13
  readonly state: string;
11
14
  readonly codeVerifier: string;
12
15
  readonly resources: readonly string[];
13
16
  readonly callbackUrl: string;
17
+ readonly clientId: string;
14
18
  readonly [key: string]: JsonValue;
15
19
  }
16
- /** A token this package minted for one principal, plus its advisory expiry. */
17
- export interface AuthorizedToken {
18
- readonly token: string;
20
+ /**
21
+ * A completed authorization for one principal.
22
+ *
23
+ * `resources` and `scopes` are what the zone granted, and decide which
24
+ * connections the grant serves. `clientId` is the client the grant was issued
25
+ * to, and the client a refresh runs as. `id` is stable across refreshes, so a
26
+ * store can replace the rotated pair in place.
27
+ */
28
+ export interface AuthorizedGrant {
29
+ readonly id: string;
30
+ readonly accessToken: string;
19
31
  readonly expiresAt?: number;
20
- readonly providerSubject?: string;
32
+ readonly refreshToken?: string;
33
+ readonly clientId: string;
34
+ readonly resources: readonly string[];
35
+ readonly scopes: readonly string[];
21
36
  }
22
37
  /**
23
- * Where completed authorizations live between turns.
38
+ * Where completed grants live between turns, keyed by principal.
24
39
  *
25
- * eve caches a resolved bearer for the duration of a step, and reruns
26
- * `getToken` after that. This store is what makes a second step reuse the
27
- * grant a user already completed instead of parking the turn again. The
28
- * default is process-local, so a fresh process re-parks rather than
29
- * resurrecting a credential from durable state.
40
+ * eve caches a resolved bearer for the duration of a step and reruns
41
+ * `getToken` after that; this store is what makes a later step reuse the grant
42
+ * a user already completed instead of parking again. `put` with an `id` the
43
+ * store already holds replaces that grant. The default is process-local, so a
44
+ * fresh process re-parks. A durable implementation (Redis, Vercel KV) keeps the
45
+ * grant, refresh token included, at rest in that backend; eve's session
46
+ * attributes are never used, so no credential enters eve's durable state.
30
47
  */
31
- export interface AuthorizedTokenStore {
32
- get(key: string): AuthorizedToken | undefined;
33
- set(key: string, token: AuthorizedToken): void;
34
- delete(key: string): void;
48
+ export interface GrantStore {
49
+ list(principal: string): Promise<readonly AuthorizedGrant[]>;
50
+ put(principal: string, grant: AuthorizedGrant): Promise<void>;
51
+ remove(principal: string, grantId: string): Promise<void>;
35
52
  }
36
- /** The two web-flow calls this adapter makes, as one seam for tests. */
53
+ /** What one authorization attempt needs its own OAuth client for. */
54
+ export interface RegisterAttemptClientOptions {
55
+ /** eve's callback URL for this attempt. The client's only redirect URI. */
56
+ readonly redirectUri: string;
57
+ /** Name the zone shows on the consent screen. */
58
+ readonly clientName?: string;
59
+ readonly scopes?: readonly string[];
60
+ }
61
+ /** A client the authorization server issued for one attempt. */
62
+ export interface RegisteredClient {
63
+ readonly clientId: string;
64
+ /**
65
+ * Only ever set by a server that ignored the public-client registration
66
+ * request. {@link interactive} refuses such a client rather than journaling
67
+ * a secret into eve's durable state.
68
+ */
69
+ readonly clientSecret?: string;
70
+ }
71
+ /** The web-flow calls this adapter makes, as one seam for tests. */
37
72
  export interface WebAppFlow {
38
73
  begin(options: BeginAuthorizationOptions): Promise<AuthorizationRedirect>;
39
74
  complete(options: CompleteAuthorizationOptions): Promise<TokenResponse>;
75
+ /**
76
+ * Registers the client for one attempt (RFC 7591). Required when no static
77
+ * `clientId` is configured, and unused when one is.
78
+ */
79
+ register?(options: RegisterAttemptClientOptions): Promise<RegisteredClient>;
80
+ /**
81
+ * Refreshes a grant at the token endpoint. When absent, a grant near expiry
82
+ * parks for a fresh sign-in instead.
83
+ */
84
+ refresh?(options: RefreshAuthorizationOptions): Promise<TokenResponse>;
40
85
  }
41
86
  export interface KeycardInteractiveOptions {
42
87
  /** The resource URL the authorization is scoped to. */
43
88
  resource: string;
44
89
  /** Keycard zone URL (issuer). Required unless `flow` is given. */
45
90
  zoneUrl?: string;
46
- /** OAuth client the browser flow runs as. Required unless `flow` is given. */
91
+ /**
92
+ * OAuth client the browser flow runs as.
93
+ *
94
+ * Leave this unset unless the client's registered redirect URIs already
95
+ * cover eve's callback. eve mints a fresh callback URL per attempt
96
+ * (`/eve/v1/connections/:name/callback/:attemptId/:token`) while RFC 9700
97
+ * requires the authorization server to match `redirect_uri` by exact string
98
+ * comparison, so a statically registered client cannot name the URL the next
99
+ * attempt will use. Unset, the client is registered per attempt instead.
100
+ *
101
+ * A Keycard application `identifier` is not a client id. Zones identify
102
+ * clients by Client ID Metadata Document, an HTTPS URL with a path that
103
+ * serves the client's metadata, so a value like `urn:app:my-agent` fails
104
+ * with `Unsupported client identifier prefix: urn`.
105
+ */
47
106
  clientId?: string;
48
- /** Client secret, for a confidential client. Public clients omit it. */
107
+ /** Client secret, for a confidential static client. Public clients omit it. */
49
108
  clientSecret?: string;
50
- /** Scopes requested in the authorization request. */
109
+ /**
110
+ * Name shown on the zone's consent screen for a per-attempt client.
111
+ * Defaults to `connectionName`.
112
+ */
113
+ clientName?: string;
114
+ /**
115
+ * Initial access token (RFC 7591 section 3.1) for a zone whose registration
116
+ * endpoint is authenticated. Unset for an open registration endpoint.
117
+ */
118
+ initialAccessToken?: string;
119
+ /**
120
+ * Scopes requested in the authorization request, and the scopes a stored
121
+ * grant must cover to serve this connection.
122
+ */
51
123
  requestScopes?: string | readonly string[];
52
- /** Extra resources authorized alongside `resource`. */
124
+ /**
125
+ * Extra resources authorized alongside `resource`. The resulting grant
126
+ * serves every connection whose resource it covers.
127
+ */
53
128
  additionalResources?: readonly string[];
54
- /** Name used in error messages and eve's authorization events. */
129
+ /**
130
+ * Name used in the challenge, error messages and eve's authorization
131
+ * events. Display text only, never a store key; two definitions in one
132
+ * process may not share it.
133
+ */
55
134
  connectionName?: string;
56
- /** Store for completed authorizations. Defaults to a process-local store. */
57
- tokens?: AuthorizedTokenStore;
135
+ /** Store for completed grants. Defaults to a process-local store. */
136
+ tokens?: GrantStore;
58
137
  /** Web-flow seam. Replaces the zone calls, so tests take no network. */
59
138
  flow?: WebAppFlow;
60
139
  }
140
+ /** Forgets every defined connection name. For tests that define many. */
141
+ export declare function resetInteractiveDefinitions(): void;
61
142
  /**
62
143
  * Interactive connection auth backed by the zone's web authorization flow.
63
144
  *
64
145
  * The three callbacks eve drives:
65
146
  *
66
- * - `getToken` returns a token only when this package holds one for the
67
- * principal. Otherwise it throws `ConnectionAuthorizationRequiredError`, and
68
- * eve emits `authorization.required`, runs `startAuthorization` in a durable
69
- * step, and parks the turn on its own callback webhook.
70
- * - `startAuthorization` begins the authorization-code flow with PKCE for the
71
- * connection's resources, against eve's minted callback URL, and returns the
72
- * challenge URL plus the `state` and verifier as resume state.
73
- * - `completeAuthorization` redeems the callback against the journaled resume
74
- * state and hands eve the token.
147
+ * - `getToken` returns a token when the store holds a grant for the principal
148
+ * whose resources cover this connection's resource and whose scopes cover
149
+ * `requestScopes`, refreshing it first when it is near expiry and holds a
150
+ * refresh token. Otherwise it throws `ConnectionAuthorizationRequiredError`,
151
+ * and eve emits `authorization.required`, runs `startAuthorization` in a
152
+ * durable step, and parks the turn on its own callback webhook.
153
+ * - `startAuthorization` registers the attempt's client, begins the
154
+ * authorization-code flow with PKCE for the connection's resources against
155
+ * eve's minted callback URL, and returns the challenge URL plus the state,
156
+ * verifier, resources, callback URL and client id as resume state.
157
+ * - `completeAuthorization` redeems the callback as the journaled client and
158
+ * stores the resulting grant.
159
+ *
160
+ * Resuming without a completed authorization cannot yield a credential: only
161
+ * `completeAuthorization` writes the store, and a denied, mismatched, or failed
162
+ * callback writes nothing, so a resumed turn either finds a real grant or parks
163
+ * again. eve settles each parked authorization once, so that is terminal rather
164
+ * than a loop.
165
+ *
166
+ * The client is registered per attempt unless `clientId` is set. eve mints a
167
+ * fresh callback URL for every attempt, while RFC 9700 requires the
168
+ * authorization server to match `redirect_uri` against the client's registered
169
+ * list by exact string comparison, so no fixed registration can name a URL that
170
+ * is generated later. Each attempt therefore gets a public, PKCE-bound client
171
+ * whose one redirect URI is the callback that attempt returns to. Those client
172
+ * records accumulate in the zone; a server that returns
173
+ * `registration_client_uri` and `registration_access_token` allows RFC 7592
174
+ * deletion once a grant settles.
75
175
  *
76
- * Resuming without a completed authorization cannot yield a credential.
77
- * `getToken` is the only path that returns a token and it reads the store,
78
- * which only `completeAuthorization` writes; a denied, mismatched, or failed
79
- * callback writes nothing. So a resumed turn either finds a real grant or
80
- * throws `Required` again and parks. eve's own settlement makes that terminal
81
- * rather than an endless loop: it settles each parked authorization once, and
82
- * a `Required` thrown after a completed authorization ends the tool call.
176
+ * Grants are keyed by principal, never by `connectionName`, so two connections
177
+ * never share a token through a shared name and `evict` on one cannot clear the
178
+ * other's grant. A grant obtained with `additionalResources` serves every
179
+ * connection whose resource it covers, so listing the agent's resources on one
180
+ * connection yields one sign-in.
83
181
  *
84
- * Discovery is memoized per definition, so the first authorization pays the
85
- * metadata round trip and later ones do not.
182
+ * Discovery is memoized per definition. Registration is not, because the
183
+ * redirect URI it binds is different every time.
86
184
  */
87
185
  export declare function interactive(options: KeycardInteractiveOptions): InteractiveAuthorizationDefinition<KeycardResumeState>;
88
- /** A process-local store that drops entries at their advisory expiry. */
89
- export declare function memoryAuthorizedTokenStore(): AuthorizedTokenStore;
186
+ /** A process-local store that drops grants once they can no longer mint a token. */
187
+ export declare function memoryAuthorizedTokenStore(): GrantStore;
90
188
  //# sourceMappingURL=interactive.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/interactive.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EAEjC,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAEV,kCAAkC,EAClC,SAAS,EAEV,MAAM,iBAAiB,CAAC;AAUzB;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAC9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC1E,QAAQ,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC3C,uDAAuD;IACvD,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,yBAAyB,GACjC,kCAAkC,CAAC,kBAAkB,CAAC,CAiGxD;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,IAAI,oBAAoB,CAmBjE"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/interactive.ts"],"names":[],"mappings":"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"}
@@ -1,61 +1,218 @@
1
- import { AuthProviderConfigurationError, AuthorizationDeniedError, beginAuthorization, completeAuthorization, fetchAuthorizationServerMetadata, StateMismatchError, } from "@keycardai/oauth";
1
+ import { AuthProviderConfigurationError, AuthorizationDeniedError, beginAuthorization, completeAuthorization, fetchAuthorizationServerMetadata, refreshAuthorization, RefreshGrantError, registerClient, StateMismatchError, } from "@keycardai/oauth";
2
2
  import { expiresAt } from "./config.js";
3
3
  import { AuthorizationFailedError, AuthorizationRequiredError, FailureReason, } from "./errors.js";
4
4
  import { principalKey } from "./subjectTokens.js";
5
+ /** A grant is refreshed once it is this close to its advisory expiry. */
6
+ const REFRESH_SKEW_MS = 60_000;
7
+ /** Connection names taken by `interactive()` definitions in this process. */
8
+ const definedConnectionNames = new Set();
9
+ /** Forgets every defined connection name. For tests that define many. */
10
+ export function resetInteractiveDefinitions() {
11
+ definedConnectionNames.clear();
12
+ }
5
13
  /**
6
14
  * Interactive connection auth backed by the zone's web authorization flow.
7
15
  *
8
16
  * The three callbacks eve drives:
9
17
  *
10
- * - `getToken` returns a token only when this package holds one for the
11
- * principal. Otherwise it throws `ConnectionAuthorizationRequiredError`, and
12
- * eve emits `authorization.required`, runs `startAuthorization` in a durable
13
- * step, and parks the turn on its own callback webhook.
14
- * - `startAuthorization` begins the authorization-code flow with PKCE for the
15
- * connection's resources, against eve's minted callback URL, and returns the
16
- * challenge URL plus the `state` and verifier as resume state.
17
- * - `completeAuthorization` redeems the callback against the journaled resume
18
- * state and hands eve the token.
18
+ * - `getToken` returns a token when the store holds a grant for the principal
19
+ * whose resources cover this connection's resource and whose scopes cover
20
+ * `requestScopes`, refreshing it first when it is near expiry and holds a
21
+ * refresh token. Otherwise it throws `ConnectionAuthorizationRequiredError`,
22
+ * and eve emits `authorization.required`, runs `startAuthorization` in a
23
+ * durable step, and parks the turn on its own callback webhook.
24
+ * - `startAuthorization` registers the attempt's client, begins the
25
+ * authorization-code flow with PKCE for the connection's resources against
26
+ * eve's minted callback URL, and returns the challenge URL plus the state,
27
+ * verifier, resources, callback URL and client id as resume state.
28
+ * - `completeAuthorization` redeems the callback as the journaled client and
29
+ * stores the resulting grant.
30
+ *
31
+ * Resuming without a completed authorization cannot yield a credential: only
32
+ * `completeAuthorization` writes the store, and a denied, mismatched, or failed
33
+ * callback writes nothing, so a resumed turn either finds a real grant or parks
34
+ * again. eve settles each parked authorization once, so that is terminal rather
35
+ * than a loop.
36
+ *
37
+ * The client is registered per attempt unless `clientId` is set. eve mints a
38
+ * fresh callback URL for every attempt, while RFC 9700 requires the
39
+ * authorization server to match `redirect_uri` against the client's registered
40
+ * list by exact string comparison, so no fixed registration can name a URL that
41
+ * is generated later. Each attempt therefore gets a public, PKCE-bound client
42
+ * whose one redirect URI is the callback that attempt returns to. Those client
43
+ * records accumulate in the zone; a server that returns
44
+ * `registration_client_uri` and `registration_access_token` allows RFC 7592
45
+ * deletion once a grant settles.
19
46
  *
20
- * Resuming without a completed authorization cannot yield a credential.
21
- * `getToken` is the only path that returns a token and it reads the store,
22
- * which only `completeAuthorization` writes; a denied, mismatched, or failed
23
- * callback writes nothing. So a resumed turn either finds a real grant or
24
- * throws `Required` again and parks. eve's own settlement makes that terminal
25
- * rather than an endless loop: it settles each parked authorization once, and
26
- * a `Required` thrown after a completed authorization ends the tool call.
47
+ * Grants are keyed by principal, never by `connectionName`, so two connections
48
+ * never share a token through a shared name and `evict` on one cannot clear the
49
+ * other's grant. A grant obtained with `additionalResources` serves every
50
+ * connection whose resource it covers, so listing the agent's resources on one
51
+ * connection yields one sign-in.
27
52
  *
28
- * Discovery is memoized per definition, so the first authorization pays the
29
- * metadata round trip and later ones do not.
53
+ * Discovery is memoized per definition. Registration is not, because the
54
+ * redirect URI it binds is different every time.
30
55
  */
31
56
  export function interactive(options) {
32
57
  if (!options.resource || !options.resource.trim()) {
33
58
  throw new AuthProviderConfigurationError("interactive requires a resource URL");
34
59
  }
35
- if (!options.flow && (!options.zoneUrl || !options.clientId)) {
36
- throw new AuthProviderConfigurationError("interactive requires zoneUrl and clientId, or an injected flow");
60
+ if (!options.flow && !options.zoneUrl) {
61
+ throw new AuthProviderConfigurationError("interactive requires zoneUrl, or an injected flow");
37
62
  }
38
63
  const connectionName = options.connectionName ?? options.resource;
64
+ if (definedConnectionNames.has(connectionName)) {
65
+ throw new AuthProviderConfigurationError(`interactive connectionName "${connectionName}" is already defined in this process. ` +
66
+ "The name is display text, not a token key, so give each connection its own.");
67
+ }
39
68
  const tokens = options.tokens ?? memoryAuthorizedTokenStore();
40
- const flow = options.flow ?? zoneWebAppFlow(options.zoneUrl);
41
- const clientId = options.clientId ?? "";
69
+ const flow = options.flow ??
70
+ zoneWebAppFlow(options.zoneUrl, {
71
+ ...(options.initialAccessToken !== undefined
72
+ ? { initialAccessToken: options.initialAccessToken }
73
+ : {}),
74
+ });
75
+ const staticClientId = options.clientId ?? "";
76
+ const clientName = options.clientName ?? connectionName;
42
77
  const resources = [options.resource, ...(options.additionalResources ?? [])];
43
78
  const scopes = splitScopes(options.requestScopes);
79
+ if (!staticClientId && !flow.register) {
80
+ throw new AuthProviderConfigurationError("interactive was given no clientId, so it registers a client per attempt, but the " +
81
+ "injected flow has no register()");
82
+ }
83
+ definedConnectionNames.add(connectionName);
84
+ const covers = (grant) => grant.resources.includes(options.resource) &&
85
+ scopes.every((scope) => grant.scopes.includes(scope));
86
+ /**
87
+ * The client this attempt runs as: the configured one, or a fresh public
88
+ * client whose single redirect URI is this attempt's callback.
89
+ */
90
+ const clientForAttempt = async (callbackUrl) => {
91
+ if (staticClientId)
92
+ return staticClientId;
93
+ let registered;
94
+ try {
95
+ registered = await flow.register({
96
+ redirectUri: callbackUrl,
97
+ clientName,
98
+ ...(scopes.length > 0 ? { scopes } : {}),
99
+ });
100
+ }
101
+ catch (cause) {
102
+ throw new AuthorizationFailedError(connectionName, {
103
+ message: cause instanceof Error
104
+ ? `Registering the OAuth client for this authorization failed: ${cause.message}`
105
+ : "Registering the OAuth client for this authorization failed",
106
+ reason: FailureReason.ACQUISITION_FAILED,
107
+ retryable: true,
108
+ });
109
+ }
110
+ if (registered.clientSecret) {
111
+ throw new AuthorizationFailedError(connectionName, {
112
+ message: "The authorization server issued a confidential client for an authorization " +
113
+ "registered as public. Redeeming it would mean journaling a client secret into " +
114
+ "eve's durable resume state, so the attempt was abandoned instead. Configure a " +
115
+ "static clientId for this connection.",
116
+ reason: FailureReason.ACQUISITION_FAILED,
117
+ retryable: false,
118
+ });
119
+ }
120
+ return registered.clientId;
121
+ };
122
+ /** A per-attempt client is public; only the static client can hold a secret. */
123
+ const secretFor = (clientId) => staticClientId && clientId === staticClientId && options.clientSecret
124
+ ? { clientSecret: options.clientSecret }
125
+ : {};
126
+ /**
127
+ * Refreshes a near-expiry grant as the client it was issued to. A refused
128
+ * refresh drops the grant so the caller parks for a fresh sign-in.
129
+ */
130
+ const refreshGrant = async (principal, grant) => {
131
+ let response;
132
+ try {
133
+ response = await flow.refresh({
134
+ refreshToken: grant.refreshToken,
135
+ clientId: grant.clientId,
136
+ resources: grant.resources,
137
+ ...(grant.scopes.length > 0 ? { scopes: grant.scopes } : {}),
138
+ ...secretFor(grant.clientId),
139
+ });
140
+ }
141
+ catch (cause) {
142
+ if (cause instanceof RefreshGrantError &&
143
+ cause.errorCode === "invalid_grant") {
144
+ // A concurrent step may have rotated this grant first, in which case
145
+ // the refresh token we sent is the stale one and the stored grant is
146
+ // the good one.
147
+ const current = (await tokens.list(principal)).find((candidate) => candidate.id === grant.id);
148
+ if (current && current.refreshToken !== grant.refreshToken) {
149
+ return current;
150
+ }
151
+ await tokens.remove(principal, grant.id);
152
+ throw new AuthorizationRequiredError(connectionName, {
153
+ message: `Connection "${connectionName}" requires authorization: the zone refused to ` +
154
+ `refresh the grant (${cause.message}).`,
155
+ });
156
+ }
157
+ throw new AuthorizationFailedError(connectionName, {
158
+ message: cause instanceof Error
159
+ ? `Refreshing the grant failed: ${cause.message}`
160
+ : "Refreshing the grant failed",
161
+ reason: FailureReason.ACQUISITION_FAILED,
162
+ retryable: cause instanceof RefreshGrantError ? cause.retryable : true,
163
+ });
164
+ }
165
+ const rotated = grantFromResponse(response, {
166
+ id: grant.id,
167
+ clientId: grant.clientId,
168
+ resources: grant.resources,
169
+ scopes: grant.scopes,
170
+ // A server that rotates nothing leaves the old refresh token valid.
171
+ refreshToken: grant.refreshToken,
172
+ });
173
+ await tokens.put(principal, rotated);
174
+ return rotated;
175
+ };
44
176
  return {
45
177
  principalType: "user",
46
- displayName: connectionName,
178
+ /**
179
+ * No definition-level `displayName`: eve validates an authored connection's
180
+ * `auth` against a closed key list (`completeAuthorization`, `evict`,
181
+ * `getToken`, `principalType`, `startAuthorization`, `vercelConnect`) that
182
+ * omits it. The name rides on the challenge instead, where eve's
183
+ * `stampChallengeDisplayName` falls back to it.
184
+ */
47
185
  async getToken({ principal }) {
48
- const key = storeKey(principal, connectionName);
49
- const held = tokens.get(key);
50
- if (!held) {
186
+ const key = storeKey(principal);
187
+ const now = Date.now();
188
+ const held = (await tokens.list(key)).filter(covers);
189
+ // A grant is usable while its access token is live or while it holds a
190
+ // refresh token that can mint a new one; an expired grant with no refresh
191
+ // token is dead.
192
+ const usable = held.filter((candidate) => !dead(candidate, now));
193
+ let grant = usable.find((candidate) => !expired(candidate, now)) ?? usable[0];
194
+ if (!grant) {
195
+ await Promise.all(held.map((stale) => tokens.remove(key, stale.id)));
196
+ throw new AuthorizationRequiredError(connectionName);
197
+ }
198
+ if (nearExpiry(grant, now) && grant.refreshToken && flow.refresh) {
199
+ grant = await refreshGrant(key, {
200
+ ...grant,
201
+ refreshToken: grant.refreshToken,
202
+ });
203
+ }
204
+ else if (expired(grant, now)) {
205
+ // Expired, and this flow cannot refresh it.
206
+ await tokens.remove(key, grant.id);
51
207
  throw new AuthorizationRequiredError(connectionName);
52
208
  }
53
- return toTokenResult(held);
209
+ return toTokenResult(grant);
54
210
  },
55
211
  async startAuthorization({ principal, callbackUrl }) {
56
212
  requireUser(principal, connectionName);
213
+ const attemptClientId = await clientForAttempt(callbackUrl);
57
214
  const redirect = await flow.begin({
58
- clientId,
215
+ clientId: attemptClientId,
59
216
  redirectUri: callbackUrl,
60
217
  resources,
61
218
  ...(scopes.length > 0 ? { scopes } : {}),
@@ -67,6 +224,7 @@ export function interactive(options) {
67
224
  codeVerifier: redirect.codeVerifier,
68
225
  resources: redirect.resources,
69
226
  callbackUrl,
227
+ clientId: attemptClientId,
70
228
  },
71
229
  };
72
230
  },
@@ -80,6 +238,9 @@ export function interactive(options) {
80
238
  retryable: false,
81
239
  });
82
240
  }
241
+ // The attempt's own client, falling back to the configured one for a
242
+ // resume journaled before clientId existed.
243
+ const clientId = resume.clientId || staticClientId;
83
244
  let response;
84
245
  try {
85
246
  response = await flow.complete({
@@ -88,7 +249,7 @@ export function interactive(options) {
88
249
  codeVerifier: resume.codeVerifier,
89
250
  clientId,
90
251
  redirectUri: resume.callbackUrl || callbackUrl,
91
- ...(options.clientSecret ? { clientSecret: options.clientSecret } : {}),
252
+ ...secretFor(clientId),
92
253
  });
93
254
  }
94
255
  catch (cause) {
@@ -102,47 +263,59 @@ export function interactive(options) {
102
263
  retryable: false,
103
264
  });
104
265
  }
105
- const expiry = expiresAt(response.expiresIn);
106
- const token = {
107
- token: response.accessToken,
108
- ...(expiry !== undefined ? { expiresAt: expiry } : {}),
109
- };
110
- tokens.set(storeKey(principal, connectionName), token);
111
- return toTokenResult(token);
266
+ const grant = grantFromResponse(response, {
267
+ id: crypto.randomUUID(),
268
+ clientId,
269
+ resources: resume.resources.length > 0 ? resume.resources : resources,
270
+ scopes,
271
+ });
272
+ await tokens.put(storeKey(principal), grant);
273
+ return toTokenResult(grant);
112
274
  },
113
275
  /**
114
- * eve calls this after a rejected bearer, so the next `getToken` parks the
115
- * turn for a fresh grant instead of handing back the same dead token.
276
+ * eve calls this after a rejected bearer. Every grant covering this
277
+ * connection's resource goes, so the next `getToken` parks for a fresh one;
278
+ * grants for other resources stay.
116
279
  */
117
- evict({ principal }) {
118
- tokens.delete(storeKey(principal, connectionName));
280
+ async evict({ principal }) {
281
+ const key = storeKey(principal);
282
+ const held = await tokens.list(key);
283
+ await Promise.all(held
284
+ .filter((grant) => grant.resources.includes(options.resource))
285
+ .map((grant) => tokens.remove(key, grant.id)));
119
286
  },
120
287
  };
121
288
  }
122
- /** A process-local store that drops entries at their advisory expiry. */
289
+ /** A process-local store that drops grants once they can no longer mint a token. */
123
290
  export function memoryAuthorizedTokenStore() {
124
- const tokens = new Map();
291
+ const grants = new Map();
125
292
  return {
126
- get(key) {
127
- const held = tokens.get(key);
293
+ async list(principal) {
294
+ const held = grants.get(principal);
128
295
  if (!held)
129
- return undefined;
130
- if (held.expiresAt !== undefined && held.expiresAt <= Date.now()) {
131
- tokens.delete(key);
132
- return undefined;
296
+ return [];
297
+ const now = Date.now();
298
+ for (const [id, grant] of held) {
299
+ if (dead(grant, now))
300
+ held.delete(id);
133
301
  }
134
- return held;
302
+ return [...held.values()];
135
303
  },
136
- set(key, token) {
137
- tokens.set(key, token);
304
+ async put(principal, grant) {
305
+ let held = grants.get(principal);
306
+ if (!held) {
307
+ held = new Map();
308
+ grants.set(principal, held);
309
+ }
310
+ held.set(grant.id, grant);
138
311
  },
139
- delete(key) {
140
- tokens.delete(key);
312
+ async remove(principal, grantId) {
313
+ grants.get(principal)?.delete(grantId);
141
314
  },
142
315
  };
143
316
  }
144
317
  /** The default flow: the zone's web-app endpoints, with discovery memoized. */
145
- function zoneWebAppFlow(issuer) {
318
+ function zoneWebAppFlow(issuer, options = {}) {
146
319
  let metadata;
147
320
  const discover = () => {
148
321
  if (!metadata)
@@ -150,26 +323,88 @@ function zoneWebAppFlow(issuer) {
150
323
  return metadata;
151
324
  };
152
325
  return {
153
- async begin(options) {
154
- return beginAuthorization(issuer, { ...options, metadata: await discover() });
326
+ async begin(beginOptions) {
327
+ return beginAuthorization(issuer, {
328
+ ...beginOptions,
329
+ metadata: await discover(),
330
+ });
331
+ },
332
+ async complete(completeOptions) {
333
+ return completeAuthorization(issuer, {
334
+ ...completeOptions,
335
+ metadata: await discover(),
336
+ });
155
337
  },
156
- async complete(options) {
157
- return completeAuthorization(issuer, { ...options, metadata: await discover() });
338
+ async refresh(refreshOptions) {
339
+ return refreshAuthorization(issuer, {
340
+ ...refreshOptions,
341
+ metadata: await discover(),
342
+ });
343
+ },
344
+ /**
345
+ * One public, PKCE-bound client per authorization, holding the single
346
+ * redirect URI that attempt will come back to. `token_endpoint_auth_method:
347
+ * "none"` keeps the resume state free of credentials, and `refresh_token`
348
+ * is requested so a long-lived grant can be renewed without sending the
349
+ * user back through the browser.
350
+ */
351
+ async register({ redirectUri, clientName, scopes }) {
352
+ const registered = await registerClient(issuer, {
353
+ ...(clientName !== undefined ? { clientName } : {}),
354
+ redirectUris: [redirectUri],
355
+ grantTypes: ["authorization_code", "refresh_token"],
356
+ responseTypes: ["code"],
357
+ tokenEndpointAuthMethod: "none",
358
+ ...(scopes && scopes.length > 0 ? { scope: scopes.join(" ") } : {}),
359
+ }, options.initialAccessToken !== undefined
360
+ ? { initialAccessToken: options.initialAccessToken }
361
+ : undefined);
362
+ return {
363
+ clientId: registered.clientId,
364
+ ...(registered.clientSecret !== undefined
365
+ ? { clientSecret: registered.clientSecret }
366
+ : {}),
367
+ };
158
368
  },
159
369
  };
160
370
  }
161
- function storeKey(principal, connectionName) {
371
+ /**
372
+ * The grant a token response settles. The server's `scope` is authoritative
373
+ * when present; otherwise the scopes are the ones requested (RFC 6749 section
374
+ * 5.1 lets the server omit `scope` when it granted exactly those).
375
+ */
376
+ function grantFromResponse(response, issued) {
377
+ const expiry = expiresAt(response.expiresIn);
378
+ const refreshToken = response.refreshToken ?? issued.refreshToken;
379
+ return {
380
+ id: issued.id,
381
+ accessToken: response.accessToken,
382
+ ...(expiry !== undefined ? { expiresAt: expiry } : {}),
383
+ ...(refreshToken !== undefined ? { refreshToken } : {}),
384
+ clientId: issued.clientId,
385
+ resources: [...issued.resources],
386
+ scopes: response.scope ? splitScopes(response.scope) : [...issued.scopes],
387
+ };
388
+ }
389
+ function expired(grant, now) {
390
+ return grant.expiresAt !== undefined && grant.expiresAt <= now;
391
+ }
392
+ /** Expired with no refresh token: nothing can mint a new access token. */
393
+ function dead(grant, now) {
394
+ return expired(grant, now) && !grant.refreshToken;
395
+ }
396
+ function nearExpiry(grant, now) {
397
+ return (grant.expiresAt !== undefined && grant.expiresAt - REFRESH_SKEW_MS <= now);
398
+ }
399
+ function storeKey(principal) {
162
400
  if (principal.type !== "user")
163
- return `${connectionName}|app`;
164
- return `${connectionName}|${principalKey(principal)}`;
401
+ return "app";
402
+ return principalKey(principal);
165
403
  }
166
- function toTokenResult(token) {
404
+ function toTokenResult(grant) {
167
405
  return {
168
- token: token.token,
169
- ...(token.expiresAt !== undefined ? { expiresAt: token.expiresAt } : {}),
170
- ...(token.providerSubject !== undefined
171
- ? { providerSubject: token.providerSubject }
172
- : {}),
406
+ token: grant.accessToken,
407
+ ...(grant.expiresAt !== undefined ? { expiresAt: grant.expiresAt } : {}),
173
408
  };
174
409
  }
175
410
  function requireUser(principal, connectionName) {
@@ -185,6 +420,8 @@ function requireUser(principal, connectionName) {
185
420
  function splitScopes(scopes) {
186
421
  if (scopes === undefined)
187
422
  return [];
188
- return Array.isArray(scopes) ? [...scopes] : scopes.split(" ").filter(Boolean);
423
+ return Array.isArray(scopes)
424
+ ? [...scopes]
425
+ : scopes.split(" ").filter(Boolean);
189
426
  }
190
427
  //# sourceMappingURL=interactive.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/interactive.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"interactive.js","sourceRoot":"","sources":["../../src/interactive.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,19 @@
1
+ import type { ToolAuthOptions } from "eve/tools";
1
2
  /**
2
3
  * The part of eve's tool context this helper needs.
3
4
  *
4
5
  * Declared structurally rather than imported so nothing here loads eve at
5
6
  * runtime. `ctx.requireAuth` never returns: it aborts the tool call and hands
6
7
  * the connection's authorization strategy back to the runtime.
8
+ *
9
+ * The options are eve's own `ToolAuthOptions` — imported as a type, which is
10
+ * erased, so the structural property still holds. Taking the type from eve
11
+ * rather than restating it is deliberate: the previous hand-written shape
12
+ * accepted a `reason` field that eve does not have, and a call passing one
13
+ * failed to typecheck at the call site rather than here.
7
14
  */
8
15
  export interface RequireAuthContext<Provider> {
9
- requireAuth(provider: Provider, options?: {
10
- readonly reason?: string;
11
- }): never;
16
+ requireAuth(provider: Provider, options?: ToolAuthOptions): never;
12
17
  }
13
18
  /**
14
19
  * Maps a provider's rejection of the current credential onto
@@ -29,7 +34,5 @@ export interface RequireAuthContext<Provider> {
29
34
  */
30
35
  export declare function requireAuthOnUnauthorized<Provider>(response: {
31
36
  readonly status: number;
32
- }, ctx: RequireAuthContext<Provider>, provider: Provider, options?: {
33
- readonly reason?: string;
34
- }): void;
37
+ }, ctx: RequireAuthContext<Provider>, provider: Provider, options?: ToolAuthOptions): void;
35
38
  //# sourceMappingURL=requireAuth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/requireAuth.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB,CAAC,QAAQ;IAC1C,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;CAChF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAChD,QAAQ,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACrC,GAAG,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrC,IAAI,CAON"}
1
+ {"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/requireAuth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB,CAAC,QAAQ;IAC1C,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,KAAK,CAAC;CACnE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAChD,QAAQ,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACrC,GAAG,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,eAAe,GACxB,IAAI,CAGN"}
@@ -18,9 +18,6 @@
18
18
  export function requireAuthOnUnauthorized(response, ctx, provider, options) {
19
19
  if (response.status !== 401 && response.status !== 403)
20
20
  return;
21
- ctx.requireAuth(provider, {
22
- reason: options?.reason ??
23
- `The provider rejected the connection credential with ${response.status}.`,
24
- });
21
+ ctx.requireAuth(provider, options);
25
22
  }
26
23
  //# sourceMappingURL=requireAuth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"requireAuth.js","sourceRoot":"","sources":["../../src/requireAuth.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAqC,EACrC,GAAiC,EACjC,QAAkB,EAClB,OAAsC;IAEtC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO;IAC/D,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE;QACxB,MAAM,EACJ,OAAO,EAAE,MAAM;YACf,wDAAwD,QAAQ,CAAC,MAAM,GAAG;KAC7E,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"requireAuth.js","sourceRoot":"","sources":["../../src/requireAuth.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAqC,EACrC,GAAiC,EACjC,QAAkB,EAClB,OAAyB;IAEzB,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO;IAC/D,GAAG,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC"}
@@ -7,4 +7,5 @@
7
7
  */
8
8
  export { appPrincipal, bearerRequest, connectionContext, expiredJwt, fakeVerifier, fakeZoneClient, sessionAuthContext, unsignedJwt, userPrincipal, validJwt, } from "./testUtils.js";
9
9
  export type { FakeZoneClient, FakeZoneClientOptions, ZoneCalls } from "./testUtils.js";
10
+ export { resetInteractiveDefinitions } from "../interactive.js";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC"}
@@ -6,4 +6,5 @@
6
6
  * `@keycardai/eve/testing`.
7
7
  */
8
8
  export { appPrincipal, bearerRequest, connectionContext, expiredJwt, fakeVerifier, fakeZoneClient, sessionAuthContext, unsignedJwt, userPrincipal, validJwt, } from "./testUtils.js";
9
+ export { resetInteractiveDefinitions } from "../interactive.js";
9
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycardai/eve",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "[Preview] Keycard auth adapters for eve agents: a zone token verifier for the channel auth walk, Keycard-backed connection auth, and interactive authorization over the zone's web flow",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,15 +30,15 @@
30
30
  }
31
31
  },
32
32
  "dependencies": {
33
- "@keycardai/oauth": "0.22.0"
33
+ "@keycardai/oauth": "0.25.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "eve": ">=0.47.3 <0.48.0"
36
+ "eve": ">=0.54.3 <0.55.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@jest/globals": "^30.0.4",
40
40
  "@types/node": "^25.6.0",
41
- "eve": "0.47.3",
41
+ "eve": "0.54.3",
42
42
  "jest": "^30.0.4",
43
43
  "ts-jest": "^29.4.7",
44
44
  "typescript": "^5.8.3"