@agentproto/auth 0.1.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agentproto contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,162 @@
1
+ # @agentproto/auth
2
+
3
+ AIP-50 `AUTH.md` reference implementation. An auth-provider doctype: a markdown +
4
+ frontmatter (or TS-literal) manifest that declares, for one API server, **how** a
5
+ CLI tool or agent authenticates and **where** to call the AIP-19 provision
6
+ endpoints. It holds configuration, never credentials — the flow engine reads or
7
+ prompts credentials at runtime.
8
+
9
+ Aligned to the WorkOS [auth.md](https://github.com/workos/auth.md) open standard:
10
+ two-hop `.well-known` discovery, the `agent_auth` metadata block, and the
11
+ service-auth claim ceremony (`urn:workos:agent-auth:grant-type:claim`).
12
+
13
+ > **Status: 0.1.0-alpha.** Two flows specified in v1 (`pat`, `service-auth`);
14
+ > `id-jag` (agentproto-as-IdP) is reserved.
15
+
16
+ Spec: <https://agentproto.sh/docs/aip-50> · Standard: <https://github.com/workos/auth.md>
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pnpm add @agentproto/auth
22
+ ```
23
+
24
+ ## Two authoring paths, one validated shape
25
+
26
+ A single Zod schema (`./schema.ts`) backs both paths, so a malformed literal and
27
+ a malformed `.md` fail with the same diagnostic.
28
+
29
+ ### TS literal
30
+
31
+ ```ts
32
+ import { defineAuthProvider } from "@agentproto/auth"
33
+
34
+ export const guilde = defineAuthProvider({
35
+ id: "guilde",
36
+ description: "Guilde AI company platform — browser-approve, no key to paste.",
37
+ apiBase: "https://api.guilde.work",
38
+ auth: {
39
+ flow: "service-auth",
40
+ clientId: "agentproto-cli",
41
+ tokenStore: { keychain: "bureau-guilde", account: "{server}" },
42
+ },
43
+ install: {
44
+ sealKey: "/guilde/api/v1/connectors/seal-key",
45
+ secretBacked: "/guilde/api/v1/guilds/{guildId}/connectors/secret-backed",
46
+ },
47
+ })
48
+ ```
49
+
50
+ ### `.md` manifest
51
+
52
+ A host reads its own vendor-shipped `*.auth.md` and registers it without editing
53
+ this package — the manifest parser is the extension seam.
54
+
55
+ ```md
56
+ ---
57
+ id: acme
58
+ description: ACME API — paste a personal access token.
59
+ apiBase: https://api.acme.example
60
+ auth:
61
+ flow: pat
62
+ tokenStore:
63
+ keychain: acme-cli
64
+ account: "{server}"
65
+ ---
66
+
67
+ # ACME
68
+
69
+ Human/LLM-facing prose about how to obtain a token lives in the body.
70
+ ```
71
+
72
+ ```ts
73
+ import { parseAuthProviderManifest, registerAuthProvider } from "@agentproto/auth"
74
+
75
+ const acme = parseAuthProviderManifest(await readFile("acme.auth.md", "utf8"))
76
+ registerAuthProvider(acme)
77
+ ```
78
+
79
+ ## Running a flow
80
+
81
+ `runAuthFlow` resolves the provider, attempts discovery, and dispatches to the
82
+ engine selected by `provider.auth.flow` — no `if`/`switch` chains at call sites.
83
+
84
+ ```ts
85
+ import { getAuthProvider, runAuthFlow } from "@agentproto/auth"
86
+
87
+ const provider = getAuthProvider("guilde")!
88
+ const result = await runAuthFlow(provider, { server: "https://api.guilde.work" })
89
+ // → { accessToken: "oat_…", tokenKind: "oat" }
90
+ ```
91
+
92
+ ### `pat` flow
93
+
94
+ Read an existing token from the platform Keychain, or prompt for one
95
+ interactively. No browser, no ceremony — the legacy-compatible path for servers
96
+ that issue personal access keys.
97
+
98
+ ### `service-auth` flow
99
+
100
+ The auth.md claim ceremony:
101
+
102
+ 1. `POST {identity_endpoint}` with `{ type: "service_auth", client_id }` →
103
+ `claim_token` + `claim.{ user_code, verification_uri, expires_in }`.
104
+ 2. Open the browser at `verification_uri`; the user approves.
105
+ 3. Poll `POST {token_endpoint}` with
106
+ `grant_type=urn:workos:agent-auth:grant-type:claim` and `claim_token` until
107
+ `access_token` is returned (`authorization_pending` / `slow_down` /
108
+ `expired_token` / `access_denied` are handled per RFC 8628).
109
+ 4. On success, store the **`identity_assertion` JWT** in the Keychain — per
110
+ AIP-50, the assertion is the durable credential; the `access_token` is
111
+ ephemeral and is **never persisted**, and the `claim_token` stays in memory
112
+ only.
113
+
114
+ On a subsequent run the ceremony is skipped when possible: the stored assertion
115
+ is exchanged at the token endpoint via
116
+ `urn:ietf:params:oauth:grant-type:jwt-bearer` for a fresh `access_token` ("this
117
+ IS the refresh path" — no refresh token). Only when the assertion is expired or
118
+ rejected (`invalid_grant`) does a new browser ceremony start.
119
+
120
+ ## Discovery
121
+
122
+ `discoverEndpoints(apiBase)` performs the two-hop chain and **throws
123
+ `DiscoveryError`** when a server predates auth.md. Callers catch it and fall back
124
+ to the static manifest config — discovery failure must never block a static flow.
125
+
126
+ ```
127
+ GET {apiBase}/.well-known/oauth-protected-resource → authorization_servers[0]
128
+ GET {authServerBase}/.well-known/oauth-authorization-server
129
+ → token_endpoint + agent_auth.identity_endpoint
130
+ ```
131
+
132
+ Every request (discovery, ceremony, refresh) is bounded by a per-request
133
+ timeout (`DEFAULT_HTTP_TIMEOUT_MS`, override via `discoverEndpoints(base,
134
+ { timeoutMs })`) and honours a caller `AbortSignal` (`{ signal }` /
135
+ `FlowRunOptions.signal`), so a hung server can never stall the CLI. Responses
136
+ are Zod-validated at the boundary rather than trusted.
137
+
138
+ ## Token storage
139
+
140
+ `token-store.ts` wraps the macOS `security` CLI and **guards the platform** —
141
+ `readKeychainToken` / `writeKeychainToken` throw a clear error on non-macOS hosts
142
+ rather than silently returning `undefined` (which would re-prompt every run).
143
+ Swap this module for libsecret (Linux) / Credential Manager (Windows) to run
144
+ elsewhere. `resolveAccount(account, server)` expands the `{server}` template in a
145
+ `tokenStore.account` spec.
146
+
147
+ ## API surface
148
+
149
+ | Export | Purpose |
150
+ | --- | --- |
151
+ | `defineAuthProvider(def)` | TS-literal authoring → frozen handle |
152
+ | `parseAuthProviderManifest(src)` | `.md` authoring → frozen handle |
153
+ | `registerAuthProvider` / `getAuthProvider` / `listAuthProviders` / `listAuthProviderIds` | module-level registry (pre-seeded with builtins) |
154
+ | `discoverEndpoints` / `DiscoveryError` | two-hop `.well-known` discovery |
155
+ | `runAuthFlow` | resolve → discover → dispatch |
156
+ | `FLOW_ENGINES` | registered flow engines (`pat`, `service-auth`) |
157
+ | `readKeychainToken` / `writeKeychainToken` / `resolveAccount` | Keychain helpers |
158
+ | `guildeAuthProvider` / `BUILTIN_AUTH_PROVIDERS` | shipped builtins |
159
+
160
+ ## License
161
+
162
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,317 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * AIP-50 auth-provider doctype — types.
5
+ *
6
+ * An auth-provider declares, for one API server, HOW a CLI tool or agent
7
+ * authenticates (the `auth:` section) and WHERE to call the AIP-19 provision
8
+ * endpoints (the `install:` section). It holds configuration, never
9
+ * credentials — the flow engine reads/prompts credentials at runtime.
10
+ *
11
+ * Two flows are specified in v1:
12
+ * - `pat` — read an existing Keychain token or prompt for a PAT
13
+ * - `service-auth` — auth.md claim ceremony (RFC 8628 + JWT-bearer)
14
+ *
15
+ * `id-jag` is reserved for a future agentproto-as-IdP scenario.
16
+ */
17
+ /** Discriminated union of all supported auth flow ids. */
18
+ type FlowId = "pat" | "service-auth";
19
+ /** Where a credential is stored in the platform Keychain. */
20
+ interface TokenStoreSpec {
21
+ /** macOS Keychain service name (or equivalent on other platforms). */
22
+ keychain: string;
23
+ /** Keychain account. The literal `{server}` is substituted with the resolved
24
+ * server URL. Defaults to the resolved server URL when omitted. */
25
+ account?: string;
26
+ }
27
+ /** PAT flow: read existing Keychain token or prompt for one interactively. */
28
+ interface PATAuthConfig {
29
+ flow: "pat";
30
+ tokenStore: TokenStoreSpec;
31
+ }
32
+ /** service-auth flow: auth.md claim ceremony.
33
+ * Discovery (/.well-known/) determines the actual endpoints at runtime;
34
+ * the static fields here are used when discovery fails. */
35
+ interface ServiceAuthConfig {
36
+ flow: "service-auth";
37
+ /** OAuth client id sent to /agent/identity. Default: "agentproto-cli". */
38
+ clientId?: string;
39
+ /** Optional login_hint (email) passed to /agent/identity. */
40
+ loginHint?: string;
41
+ /** Keychain destination for the durable credential. Per AIP-50 the stored
42
+ * credential is the `identity_assertion` JWT — re-exchanged via the
43
+ * jwt-bearer grant for a fresh access token. The access token is ephemeral
44
+ * and MUST NOT be persisted; the claim_token is held in memory only. */
45
+ tokenStore: TokenStoreSpec;
46
+ }
47
+ type AuthConfig = PATAuthConfig | ServiceAuthConfig;
48
+ /** AIP-19 companion: the provision endpoints on the server side. */
49
+ interface InstallConfig {
50
+ /** URL path for the seal-key endpoint (e.g. "/api/v1/connectors/seal-key"). */
51
+ sealKey: string;
52
+ /** URL path template for the secret-backed install endpoint.
53
+ * May contain `{guildId}` which the caller substitutes at provision time. */
54
+ secretBacked: string;
55
+ }
56
+ interface AuthProviderDefinition {
57
+ /** Provider id — kebab/dot, 2–80 chars (AIP cross-id pattern). */
58
+ id: string;
59
+ /** Human/LLM-facing prose; ≤2000 chars. */
60
+ description: string;
61
+ /** Canonical API base URL (no trailing slash). */
62
+ apiBase: string;
63
+ /** Authentication configuration. */
64
+ auth: AuthConfig;
65
+ /** Optional AIP-19 provision target. */
66
+ install?: InstallConfig;
67
+ }
68
+ type AuthProviderHandle = Readonly<AuthProviderDefinition>;
69
+ /** Resolved endpoint set from the two-hop auth.md discovery chain. */
70
+ interface DiscoveredEndpoints {
71
+ /** Canonical API resource URL from PRM. */
72
+ resource: string;
73
+ /** Human name from PRM, if present. */
74
+ resourceName?: string;
75
+ /** Authorization server base URL. */
76
+ authServerBase: string;
77
+ /** Full token endpoint URL. */
78
+ tokenEndpoint: string;
79
+ /** Full revocation endpoint URL, if present. */
80
+ revocationEndpoint?: string;
81
+ /** Full identity endpoint URL (POST /agent/identity). */
82
+ identityEndpoint: string;
83
+ /** Full claim endpoint URL (POST /agent/identity/claim), if present. */
84
+ claimEndpoint?: string;
85
+ /** Supported identity types from agent_auth.identity_types_supported. */
86
+ identityTypesSupported: string[];
87
+ /** Supported grant types from AS metadata. */
88
+ grantTypesSupported: string[];
89
+ }
90
+ /** Result of a completed auth flow. The engine has already persisted the
91
+ * durable credential to the Keychain (for service-auth, the identity_assertion
92
+ * in the primary slot); these fields are the in-memory values the caller uses
93
+ * for the current invocation. */
94
+ interface FlowResult {
95
+ /** Service-signed identity assertion JWT (service-auth flow), when the server
96
+ * issues one. The engine has stored it in the Keychain (primary slot) as the
97
+ * durable credential for jwt-bearer re-exchange; surfaced here too. */
98
+ identityAssertion?: string;
99
+ /** ISO 8601 expiry of the identity assertion. */
100
+ assertionExpires?: string;
101
+ /** Access token to use for this invocation — `pat` returns the stored/typed
102
+ * key; `service-auth` returns the freshly minted/refreshed `oat_*`. */
103
+ accessToken?: string;
104
+ /** What `accessToken` is: `pat` (personal access key), `oat` (service-auth
105
+ * access token). `assertion` is reserved for a future flow that returns a
106
+ * bare assertion without an access token. */
107
+ tokenKind: "pat" | "assertion" | "oat";
108
+ }
109
+ interface FlowRunOptions {
110
+ /** Resolved server URL for this invocation. May differ from provider.apiBase
111
+ * when the user passes --server explicitly. */
112
+ server: string;
113
+ /** Force re-authentication even if a stored credential is found. */
114
+ force?: boolean;
115
+ signal?: AbortSignal;
116
+ }
117
+ /** A flow engine implements one auth protocol. Dispatch by provider.auth.flow —
118
+ * no if/switch chains at call sites. */
119
+ interface FlowEngine {
120
+ readonly id: FlowId;
121
+ run(provider: AuthProviderHandle, discovered: DiscoveredEndpoints | null, opts: FlowRunOptions): Promise<FlowResult>;
122
+ }
123
+
124
+ /**
125
+ * `defineAuthProvider` — TS-literal authoring path for an auth-provider.
126
+ *
127
+ * Built on `createDoctype` so it gets the cross-AIP invariants: id-pattern,
128
+ * ≤2000-char description, frozen handle, canonical error prefix. Field-level
129
+ * validation uses the shared Zod from `./schema.ts`, so a malformed literal
130
+ * fails with the same diagnostic as a malformed `.md`.
131
+ */
132
+
133
+ declare const defineAuthProvider: (def: AuthProviderDefinition) => Readonly<AuthProviderDefinition>;
134
+
135
+ /**
136
+ * AIP-50 auth-provider frontmatter / literal Zod schema.
137
+ *
138
+ * Single source of truth for both authoring paths: `defineAuthProvider`
139
+ * (TS literal) and `parseAuthProviderManifest` (.md) run this same schema,
140
+ * so a malformed literal and a malformed manifest fail identically.
141
+ */
142
+
143
+ declare const tokenStoreSpecSchema: z.ZodObject<{
144
+ keychain: z.ZodString;
145
+ account: z.ZodOptional<z.ZodString>;
146
+ }, z.core.$strict>;
147
+ declare const authConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
148
+ flow: z.ZodLiteral<"pat">;
149
+ tokenStore: z.ZodObject<{
150
+ keychain: z.ZodString;
151
+ account: z.ZodOptional<z.ZodString>;
152
+ }, z.core.$strict>;
153
+ }, z.core.$strict>, z.ZodObject<{
154
+ flow: z.ZodLiteral<"service-auth">;
155
+ clientId: z.ZodOptional<z.ZodString>;
156
+ loginHint: z.ZodOptional<z.ZodString>;
157
+ tokenStore: z.ZodObject<{
158
+ keychain: z.ZodString;
159
+ account: z.ZodOptional<z.ZodString>;
160
+ }, z.core.$strict>;
161
+ }, z.core.$strict>], "flow">;
162
+ declare const installConfigSchema: z.ZodObject<{
163
+ sealKey: z.ZodString;
164
+ secretBacked: z.ZodString;
165
+ }, z.core.$strict>;
166
+ declare const authProviderFrontmatterSchema: z.ZodObject<{
167
+ id: z.ZodString;
168
+ description: z.ZodString;
169
+ apiBase: z.ZodString;
170
+ auth: z.ZodDiscriminatedUnion<[z.ZodObject<{
171
+ flow: z.ZodLiteral<"pat">;
172
+ tokenStore: z.ZodObject<{
173
+ keychain: z.ZodString;
174
+ account: z.ZodOptional<z.ZodString>;
175
+ }, z.core.$strict>;
176
+ }, z.core.$strict>, z.ZodObject<{
177
+ flow: z.ZodLiteral<"service-auth">;
178
+ clientId: z.ZodOptional<z.ZodString>;
179
+ loginHint: z.ZodOptional<z.ZodString>;
180
+ tokenStore: z.ZodObject<{
181
+ keychain: z.ZodString;
182
+ account: z.ZodOptional<z.ZodString>;
183
+ }, z.core.$strict>;
184
+ }, z.core.$strict>], "flow">;
185
+ install: z.ZodOptional<z.ZodObject<{
186
+ sealKey: z.ZodString;
187
+ secretBacked: z.ZodString;
188
+ }, z.core.$strict>>;
189
+ }, z.core.$strict>;
190
+ type AuthProviderFrontmatter = z.infer<typeof authProviderFrontmatterSchema>;
191
+
192
+ /**
193
+ * `.md` authoring path for an auth-provider — parse a frontmatter manifest
194
+ * into a frozen handle. Mirrors `parseRecipeManifest` from AIP-19: gray-matter
195
+ * splits the frontmatter, the shared Zod validates it, `defineAuthProvider`
196
+ * builds the handle so both authoring paths converge on one validated shape.
197
+ *
198
+ * This parser is the extension seam: a host reads its own external `.md`
199
+ * manifests (e.g. vendor-shipped `guilde.auth.md`) and registers them without
200
+ * editing this package.
201
+ */
202
+
203
+ interface AuthProviderManifest {
204
+ frontmatter: AuthProviderFrontmatter;
205
+ body: string;
206
+ }
207
+ declare function parseAuthProviderManifestRaw(source: string): AuthProviderManifest;
208
+ /** Parse an auth-provider `.md` straight to a frozen handle. */
209
+ declare function parseAuthProviderManifest(source: string): AuthProviderHandle;
210
+
211
+ /**
212
+ * Auth-provider registry — id → handle lookup, pre-seeded with builtins.
213
+ *
214
+ * Mirrors the provision-recipe registry shape (AIP-19). Re-registering an id
215
+ * overrides it (last write wins), so a host can shadow a builtin.
216
+ */
217
+
218
+ declare function registerAuthProvider(provider: AuthProviderHandle): void;
219
+ declare function getAuthProvider(id: string): AuthProviderHandle | undefined;
220
+ declare function listAuthProviders(): AuthProviderHandle[];
221
+ declare function listAuthProviderIds(): string[];
222
+
223
+ /**
224
+ * Two-hop discovery per the auth.md standard (AIP-50 §Discovery algorithm).
225
+ *
226
+ * Hop 1: GET {apiBase}/.well-known/oauth-protected-resource (PRM, RFC 8414)
227
+ * → extract authorization_servers[0] as authServerBase
228
+ * Hop 2: GET {authServerBase}/.well-known/oauth-authorization-server (AS metadata)
229
+ * → extract token_endpoint + agent_auth block
230
+ *
231
+ * Callers MUST catch DiscoveryError and fall back to static manifest config —
232
+ * discovery failures are common (server predates auth.md) and MUST NOT prevent
233
+ * the PAT or other static flows from working.
234
+ */
235
+
236
+ declare class DiscoveryError extends Error {
237
+ readonly serverUrl: string;
238
+ constructor(message: string, serverUrl: string);
239
+ }
240
+ interface DiscoverOptions {
241
+ /** Abort the discovery fetches (e.g. on user Ctrl-C). */
242
+ signal?: AbortSignal;
243
+ /** Per-hop timeout. Defaults to DEFAULT_HTTP_TIMEOUT_MS. */
244
+ timeoutMs?: number;
245
+ }
246
+ declare function discoverEndpoints(apiBase: string, opts?: DiscoverOptions): Promise<DiscoveredEndpoints>;
247
+
248
+ /**
249
+ * `runAuthFlow` — resolve provider → discover endpoints → dispatch to engine.
250
+ *
251
+ * Discovery is attempted silently; callers receive the result without knowing
252
+ * whether live or static endpoint config was used. The flow engine receives
253
+ * `null` for `discovered` if discovery failed or was not attempted.
254
+ */
255
+
256
+ interface RunFlowOptions extends FlowRunOptions {
257
+ /** Pre-resolved endpoints (skip discovery). Pass null to force re-discovery. */
258
+ discovered?: DiscoveredEndpoints | null;
259
+ /** Suppress debug output. Default: false. */
260
+ quiet?: boolean;
261
+ }
262
+ declare function runAuthFlow(provider: AuthProviderHandle, opts: RunFlowOptions): Promise<FlowResult>;
263
+
264
+ /**
265
+ * Flow engine registry — dispatch by `provider.auth.flow`.
266
+ *
267
+ * Add a new engine:
268
+ * 1. Implement `FlowEngine` in a sibling file
269
+ * 2. Add it here — no if/switch chains at call sites
270
+ */
271
+
272
+ declare const FLOW_ENGINES: Readonly<Record<string, FlowEngine>>;
273
+
274
+ /**
275
+ * Keychain helpers — read/write a token in the platform Keychain.
276
+ *
277
+ * Uses the macOS `security` CLI. On non-macOS hosts, callers should swap this
278
+ * module for a platform-appropriate equivalent (libsecret on Linux, Credential
279
+ * Manager on Windows).
280
+ */
281
+ /** Substitute `{server}` template in a tokenStore account spec. */
282
+ declare function resolveAccount(account: string | undefined, server: string): string;
283
+ /** Read a token from the Keychain. Returns undefined if not found. */
284
+ declare function readKeychainToken(service: string, account: string): Promise<string | undefined>;
285
+ /** Write a token to the Keychain (-U updates in place). */
286
+ declare function writeKeychainToken(service: string, account: string, token: string): Promise<void>;
287
+
288
+ /** Guilde AI company platform.
289
+ *
290
+ * Authenticates via the AIP-50 service-auth claim ceremony: browser opens,
291
+ * user clicks Approve, bureau stores the oat_* access token.
292
+ * Discovery from /.well-known/oauth-protected-resource supplies live endpoints;
293
+ * the static fields below are the offline fallback. */
294
+ declare const guildeAuthProvider: Readonly<AuthProviderDefinition>;
295
+ declare const BUILTIN_AUTH_PROVIDERS: readonly AuthProviderHandle[];
296
+
297
+ /**
298
+ * @agentproto/auth — AIP-50 AUTH.md reference implementation.
299
+ *
300
+ * Public API:
301
+ * defineAuthProvider(def) — TS-literal authoring (frozen handle)
302
+ * parseAuthProviderManifest(source) — .md authoring (gray-matter + Zod)
303
+ * registerAuthProvider(handle) — extend the module-level registry
304
+ * getAuthProvider(id) — lookup by vendor id
305
+ * listAuthProviderIds() — enumerate known providers
306
+ * discoverEndpoints(apiBase) — two-hop PRM → AS metadata discovery
307
+ * runAuthFlow(provider, opts) — resolve + discover + dispatch to engine
308
+ * FLOW_ENGINES — registered flow engines (pat, …)
309
+ * writeKeychainToken(svc, acct, t) — persist a credential to Keychain
310
+ * readKeychainToken(svc, acct) — read a credential from Keychain
311
+ * resolveAccount(acct, server) — expand {server} template
312
+ */
313
+
314
+ declare const SPEC_NAME = "agentauth";
315
+ declare const SPEC_VERSION = "v1";
316
+
317
+ export { type AuthConfig, type AuthProviderDefinition, type AuthProviderFrontmatter, type AuthProviderHandle, type AuthProviderManifest, BUILTIN_AUTH_PROVIDERS, type DiscoveredEndpoints, DiscoveryError, FLOW_ENGINES, type FlowEngine, type FlowId, type FlowResult, type FlowRunOptions, type InstallConfig, type PATAuthConfig, type RunFlowOptions, SPEC_NAME, SPEC_VERSION, type ServiceAuthConfig, type TokenStoreSpec, authConfigSchema, authProviderFrontmatterSchema, defineAuthProvider, discoverEndpoints, getAuthProvider, guildeAuthProvider, installConfigSchema, listAuthProviderIds, listAuthProviders, parseAuthProviderManifest, parseAuthProviderManifestRaw, readKeychainToken, registerAuthProvider, resolveAccount, runAuthFlow, tokenStoreSpecSchema, writeKeychainToken };
package/dist/index.mjs ADDED
@@ -0,0 +1,634 @@
1
+ import { createDoctype } from '@agentproto/define-doctype';
2
+ import { z } from 'zod';
3
+ import matter from 'gray-matter';
4
+ import { createInterface } from 'readline';
5
+ import { execFile } from 'child_process';
6
+ import { promisify } from 'util';
7
+
8
+ /**
9
+ * @agentproto/auth v0.1.0-alpha
10
+ * AIP-50 AUTH.md `defineAuthProvider` reference implementation.
11
+ */
12
+
13
+ var tokenStoreSpecSchema = z.object({
14
+ keychain: z.string().min(1),
15
+ account: z.string().min(1).optional()
16
+ }).strict();
17
+ var patAuthConfigSchema = z.object({
18
+ flow: z.literal("pat"),
19
+ tokenStore: tokenStoreSpecSchema
20
+ }).strict();
21
+ var serviceAuthConfigSchema = z.object({
22
+ flow: z.literal("service-auth"),
23
+ clientId: z.string().min(1).optional(),
24
+ loginHint: z.string().min(1).optional(),
25
+ tokenStore: tokenStoreSpecSchema
26
+ }).strict();
27
+ var authConfigSchema = z.discriminatedUnion("flow", [
28
+ patAuthConfigSchema,
29
+ serviceAuthConfigSchema
30
+ ]);
31
+ var installConfigSchema = z.object({
32
+ sealKey: z.string().min(1),
33
+ secretBacked: z.string().min(1)
34
+ }).strict();
35
+ var authProviderFrontmatterSchema = z.object({
36
+ id: z.string().min(2).max(80),
37
+ description: z.string().min(1).max(2e3),
38
+ apiBase: z.string().url(),
39
+ auth: authConfigSchema,
40
+ install: installConfigSchema.optional()
41
+ }).strict().describe(
42
+ "AIP-50 auth-provider manifest: how a CLI tool authenticates to an API server and (optionally) where to call the AIP-19 provision endpoints."
43
+ );
44
+
45
+ // src/define-auth-provider.ts
46
+ var defineAuthProvider = createDoctype({
47
+ aip: 50,
48
+ name: "authProvider",
49
+ validate(def) {
50
+ const result = authProviderFrontmatterSchema.safeParse(def);
51
+ if (!result.success) {
52
+ throw new Error(
53
+ `defineAuthProvider (AIP-50): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
54
+ );
55
+ }
56
+ },
57
+ build(def) {
58
+ return {
59
+ ...def,
60
+ auth: Object.freeze({ ...def.auth }),
61
+ install: def.install ? Object.freeze({ ...def.install }) : void 0
62
+ };
63
+ }
64
+ });
65
+ function parseAuthProviderManifestRaw(source) {
66
+ const parsed = matter(source);
67
+ if (Object.keys(parsed.data).length === 0) {
68
+ throw new Error("parseAuthProviderManifest: missing or empty frontmatter");
69
+ }
70
+ const result = authProviderFrontmatterSchema.safeParse(parsed.data);
71
+ if (!result.success) {
72
+ throw new Error(
73
+ `parseAuthProviderManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
74
+ );
75
+ }
76
+ return { frontmatter: result.data, body: parsed.content };
77
+ }
78
+ function parseAuthProviderManifest(source) {
79
+ const { frontmatter } = parseAuthProviderManifestRaw(source);
80
+ return defineAuthProvider(frontmatter);
81
+ }
82
+
83
+ // src/builtins.ts
84
+ var guildeAuthProvider = defineAuthProvider({
85
+ id: "guilde",
86
+ description: "Guilde AI company platform. Authenticate via a browser-approve flow \u2014 no key to paste.",
87
+ apiBase: "https://api.guilde.work",
88
+ auth: {
89
+ flow: "service-auth",
90
+ clientId: "agentproto-cli",
91
+ tokenStore: {
92
+ keychain: "bureau-guilde",
93
+ account: "{server}"
94
+ }
95
+ },
96
+ install: {
97
+ sealKey: "/guilde/api/v1/connectors/seal-key",
98
+ secretBacked: "/guilde/api/v1/guilds/{guildId}/connectors/secret-backed"
99
+ }
100
+ });
101
+ var BUILTIN_AUTH_PROVIDERS = [
102
+ guildeAuthProvider
103
+ ];
104
+
105
+ // src/registry.ts
106
+ var _registry = /* @__PURE__ */ new Map();
107
+ for (const p of BUILTIN_AUTH_PROVIDERS) _registry.set(p.id, p);
108
+ function registerAuthProvider(provider) {
109
+ _registry.set(provider.id, provider);
110
+ }
111
+ function getAuthProvider(id) {
112
+ return _registry.get(id);
113
+ }
114
+ function listAuthProviders() {
115
+ return [..._registry.values()];
116
+ }
117
+ function listAuthProviderIds() {
118
+ return [..._registry.keys()];
119
+ }
120
+
121
+ // src/http.ts
122
+ var DEFAULT_HTTP_TIMEOUT_MS = 1e4;
123
+ function assertSecureUrl(url) {
124
+ let u;
125
+ try {
126
+ u = new URL(url);
127
+ } catch {
128
+ throw new Error(`invalid URL: ${url}`);
129
+ }
130
+ if (u.protocol === "https:") return;
131
+ const host = u.hostname;
132
+ const isLoopback = host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || host.endsWith(".localhost");
133
+ if (u.protocol === "http:" && isLoopback) return;
134
+ throw new Error(
135
+ `insecure URL '${url}' \u2014 AIP-50 requires HTTPS for discovery/auth (loopback http is allowed for local development).`
136
+ );
137
+ }
138
+ function deadlineSignal(timeoutMs, outer) {
139
+ const ctrl = new AbortController();
140
+ const onAbort = () => ctrl.abort(outer?.reason);
141
+ if (outer) {
142
+ if (outer.aborted) ctrl.abort(outer.reason);
143
+ else outer.addEventListener("abort", onAbort, { once: true });
144
+ }
145
+ const timer = setTimeout(
146
+ () => ctrl.abort(new Error(`request timed out after ${timeoutMs}ms`)),
147
+ timeoutMs
148
+ );
149
+ if (typeof timer !== "number") timer.unref();
150
+ return {
151
+ signal: ctrl.signal,
152
+ clear: () => {
153
+ clearTimeout(timer);
154
+ outer?.removeEventListener("abort", onAbort);
155
+ }
156
+ };
157
+ }
158
+ async function fetchWithDeadline(url, init = {}, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
159
+ const { signal, clear } = deadlineSignal(timeoutMs, init.signal ?? void 0);
160
+ try {
161
+ return await fetch(url, { ...init, signal });
162
+ } finally {
163
+ clear();
164
+ }
165
+ }
166
+
167
+ // src/discover.ts
168
+ var DiscoveryError = class extends Error {
169
+ serverUrl;
170
+ constructor(message, serverUrl) {
171
+ super(`DiscoveryError (${serverUrl}): ${message}`);
172
+ this.name = "DiscoveryError";
173
+ this.serverUrl = serverUrl;
174
+ }
175
+ };
176
+ var prmSchema = z.object({
177
+ resource: z.string().optional(),
178
+ resource_name: z.string().optional(),
179
+ authorization_servers: z.array(z.string()).optional(),
180
+ scopes_supported: z.array(z.string()).optional(),
181
+ bearer_methods_supported: z.array(z.string()).optional()
182
+ }).loose();
183
+ var asMetaSchema = z.object({
184
+ issuer: z.string().optional(),
185
+ token_endpoint: z.string().optional(),
186
+ revocation_endpoint: z.string().optional(),
187
+ grant_types_supported: z.array(z.string()).optional(),
188
+ agent_auth: z.object({
189
+ skill: z.string().optional(),
190
+ identity_endpoint: z.string().optional(),
191
+ claim_endpoint: z.string().optional(),
192
+ events_endpoint: z.string().optional(),
193
+ identity_types_supported: z.array(z.string()).optional(),
194
+ identity_assertion: z.object({ assertion_types_supported: z.array(z.string()).optional() }).loose().optional()
195
+ }).loose().optional()
196
+ }).loose();
197
+ async function discoverEndpoints(apiBase, opts = {}) {
198
+ const base = apiBase.replace(/\/$/, "");
199
+ const fetchOpts = {
200
+ signal: opts.signal,
201
+ redirect: "manual"
202
+ };
203
+ const prmUrl = `${base}/.well-known/oauth-protected-resource`;
204
+ let prm;
205
+ try {
206
+ assertSecureUrl(prmUrl);
207
+ const res = await fetchWithDeadline(prmUrl, fetchOpts, opts.timeoutMs);
208
+ if (!res.ok) {
209
+ throw new DiscoveryError(
210
+ `PRM returned ${res.status} ${res.statusText}`,
211
+ apiBase
212
+ );
213
+ }
214
+ prm = prmSchema.parse(await res.json());
215
+ } catch (err) {
216
+ if (err instanceof DiscoveryError) throw err;
217
+ throw new DiscoveryError(`PRM fetch failed: ${err}`, apiBase);
218
+ }
219
+ const authServerBase = prm.authorization_servers?.[0]?.replace(/\/$/, "");
220
+ if (!authServerBase) {
221
+ throw new DiscoveryError(
222
+ "PRM missing authorization_servers[0]",
223
+ apiBase
224
+ );
225
+ }
226
+ const asUrl = `${authServerBase}/.well-known/oauth-authorization-server`;
227
+ let as;
228
+ try {
229
+ assertSecureUrl(asUrl);
230
+ const res = await fetchWithDeadline(asUrl, fetchOpts, opts.timeoutMs);
231
+ if (!res.ok) {
232
+ throw new DiscoveryError(
233
+ `AS metadata returned ${res.status} ${res.statusText}`,
234
+ apiBase
235
+ );
236
+ }
237
+ as = asMetaSchema.parse(await res.json());
238
+ } catch (err) {
239
+ if (err instanceof DiscoveryError) throw err;
240
+ throw new DiscoveryError(`AS metadata fetch failed: ${err}`, apiBase);
241
+ }
242
+ const tokenEndpoint = as.token_endpoint;
243
+ const identityEndpoint = as.agent_auth?.identity_endpoint;
244
+ if (!tokenEndpoint) {
245
+ throw new DiscoveryError("AS metadata missing token_endpoint", apiBase);
246
+ }
247
+ if (!identityEndpoint) {
248
+ throw new DiscoveryError(
249
+ "AS metadata missing agent_auth.identity_endpoint",
250
+ apiBase
251
+ );
252
+ }
253
+ return {
254
+ resource: prm.resource ?? base,
255
+ resourceName: prm.resource_name,
256
+ authServerBase,
257
+ tokenEndpoint,
258
+ revocationEndpoint: as.revocation_endpoint,
259
+ identityEndpoint,
260
+ claimEndpoint: as.agent_auth?.claim_endpoint,
261
+ identityTypesSupported: as.agent_auth?.identity_types_supported ?? [],
262
+ grantTypesSupported: as.grant_types_supported ?? []
263
+ };
264
+ }
265
+ var exec = promisify(execFile);
266
+ function assertKeychainSupported() {
267
+ if (process.platform !== "darwin") {
268
+ throw new Error(
269
+ `@agentproto/auth token-store: the Keychain backend only supports macOS (got platform "${process.platform}"). Provide a libsecret (Linux) or Credential Manager (Windows) implementation to run here.`
270
+ );
271
+ }
272
+ }
273
+ function resolveAccount(account, server) {
274
+ if (!account) return server;
275
+ return account.replace("{server}", server);
276
+ }
277
+ async function readKeychainToken(service, account) {
278
+ assertKeychainSupported();
279
+ try {
280
+ const { stdout } = await exec("security", [
281
+ "find-generic-password",
282
+ "-s",
283
+ service,
284
+ "-a",
285
+ account,
286
+ "-w"
287
+ ]);
288
+ const t = stdout.replace(/\n$/, "");
289
+ return t || void 0;
290
+ } catch {
291
+ return void 0;
292
+ }
293
+ }
294
+ async function writeKeychainToken(service, account, token) {
295
+ assertKeychainSupported();
296
+ await exec("security", [
297
+ "add-generic-password",
298
+ "-U",
299
+ "-s",
300
+ service,
301
+ "-a",
302
+ account,
303
+ "-w",
304
+ token,
305
+ "-D",
306
+ "agentproto auth token"
307
+ ]);
308
+ }
309
+
310
+ // src/flow-engines/pat.ts
311
+ async function promptToken(label) {
312
+ const input = process.stdin;
313
+ const out = process.stderr;
314
+ if (!input.isTTY) {
315
+ return new Promise((resolve) => {
316
+ const rl = createInterface({ input, output: out });
317
+ out.write(`${label}: `);
318
+ rl.question("", (answer) => {
319
+ rl.close();
320
+ resolve(answer.trim());
321
+ });
322
+ });
323
+ }
324
+ return new Promise((resolve, reject) => {
325
+ out.write(`${label}: `);
326
+ const chars = [];
327
+ input.setRawMode(true);
328
+ input.resume();
329
+ input.setEncoding("utf8");
330
+ const cleanup = () => {
331
+ input.setRawMode(false);
332
+ input.pause();
333
+ input.removeListener("data", onData);
334
+ };
335
+ const onData = (chunk) => {
336
+ for (const ch of chunk) {
337
+ switch (ch) {
338
+ case "\r":
339
+ case "\n":
340
+ out.write("\n");
341
+ cleanup();
342
+ resolve(chars.join("").trim());
343
+ return;
344
+ case "":
345
+ out.write("\n");
346
+ cleanup();
347
+ reject(new Error("auth cancelled"));
348
+ return;
349
+ case "":
350
+ out.write("\n");
351
+ cleanup();
352
+ resolve(chars.join("").trim());
353
+ return;
354
+ case "\x7F":
355
+ // Backspace / Delete
356
+ case "\b":
357
+ if (chars.length > 0) {
358
+ chars.pop();
359
+ out.write("\b \b");
360
+ }
361
+ break;
362
+ default:
363
+ if (ch >= " ") {
364
+ chars.push(ch);
365
+ out.write("*");
366
+ }
367
+ }
368
+ }
369
+ };
370
+ input.on("data", onData);
371
+ });
372
+ }
373
+ var patFlowEngine = {
374
+ id: "pat",
375
+ async run(provider, _discovered, opts) {
376
+ const { auth } = provider;
377
+ if (auth.flow !== "pat") {
378
+ throw new Error(`patFlowEngine: invoked with flow="${auth.flow}"`);
379
+ }
380
+ const account = resolveAccount(auth.tokenStore.account, opts.server);
381
+ if (!opts.force) {
382
+ const existing = await readKeychainToken(auth.tokenStore.keychain, account);
383
+ if (existing) {
384
+ return { accessToken: existing, tokenKind: "pat" };
385
+ }
386
+ }
387
+ const token = await promptToken(`${provider.id} personal access token`);
388
+ if (!token) throw new Error("no token provided");
389
+ return { accessToken: token, tokenKind: "pat" };
390
+ }
391
+ };
392
+ var execAsync = promisify(execFile);
393
+ var CLAIM_GRANT_TYPE = "urn:workos:agent-auth:grant-type:claim";
394
+ var JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
395
+ var DEFAULT_POLL_INTERVAL_S = 5;
396
+ var identityResponseSchema = z.object({
397
+ registration_id: z.string(),
398
+ claim_token: z.string(),
399
+ claim: z.object({
400
+ user_code: z.string(),
401
+ verification_uri: z.string(),
402
+ expires_in: z.number(),
403
+ interval: z.number().optional()
404
+ }).loose()
405
+ }).loose();
406
+ var tokenSuccessSchema = z.object({
407
+ access_token: z.string(),
408
+ token_type: z.string().optional(),
409
+ refresh_token: z.string().optional(),
410
+ identity_assertion: z.string().optional(),
411
+ assertion_expires_in: z.number().optional(),
412
+ assertion_expires: z.string().optional()
413
+ }).loose();
414
+ var tokenErrorSchema = z.object({
415
+ error: z.string(),
416
+ error_description: z.string().optional()
417
+ }).loose();
418
+ var tokenResponseSchema = z.union([tokenSuccessSchema, tokenErrorSchema]);
419
+ async function openBrowser(url) {
420
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", url]] : ["xdg-open", [url]];
421
+ try {
422
+ await execAsync(cmd, args);
423
+ } catch {
424
+ }
425
+ }
426
+ async function postJson(url, body, schema, signal) {
427
+ const res = await fetchWithDeadline(url, {
428
+ method: "POST",
429
+ headers: { "Content-Type": "application/json" },
430
+ body: JSON.stringify(body),
431
+ signal
432
+ });
433
+ if (!res.ok) {
434
+ const text = await res.text();
435
+ throw new Error(`POST ${url} \u2192 ${res.status}: ${text}`);
436
+ }
437
+ return schema.parse(await res.json());
438
+ }
439
+ async function postForm(url, params, schema, signal) {
440
+ const res = await fetchWithDeadline(url, {
441
+ method: "POST",
442
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
443
+ body: new URLSearchParams(params).toString(),
444
+ signal
445
+ });
446
+ return schema.parse(await res.json());
447
+ }
448
+ async function pollForToken(tokenEndpoint, claimToken, clientId, intervalS, expiresIn, signal) {
449
+ const deadline = Date.now() + expiresIn * 1e3;
450
+ let pollMs = intervalS * 1e3;
451
+ while (Date.now() < deadline) {
452
+ if (signal?.aborted) throw new Error("auth cancelled");
453
+ await new Promise((r) => setTimeout(r, pollMs));
454
+ const data = await postForm(
455
+ tokenEndpoint,
456
+ {
457
+ grant_type: CLAIM_GRANT_TYPE,
458
+ claim_token: claimToken,
459
+ client_id: clientId
460
+ },
461
+ tokenResponseSchema,
462
+ signal
463
+ );
464
+ if (!("error" in data)) return data;
465
+ const pollErr = data.error;
466
+ if (pollErr === "authorization_pending") continue;
467
+ if (pollErr === "slow_down") {
468
+ pollMs += 5e3;
469
+ continue;
470
+ }
471
+ if (pollErr === "expired_token") {
472
+ throw new Error("auth timeout \u2014 claim expired before user approved");
473
+ }
474
+ if (pollErr === "access_denied") {
475
+ throw new Error("access denied \u2014 user rejected the authorisation request");
476
+ }
477
+ const desc = data.error_description;
478
+ throw new Error(`token endpoint error: ${pollErr}${desc ? ` \u2014 ${desc}` : ""}`);
479
+ }
480
+ throw new Error("auth timeout \u2014 approval window closed");
481
+ }
482
+ async function exchangeAssertion(tokenEndpoint, assertion, clientId, signal) {
483
+ try {
484
+ const data = await postForm(
485
+ tokenEndpoint,
486
+ {
487
+ grant_type: JWT_BEARER_GRANT_TYPE,
488
+ assertion,
489
+ client_id: clientId
490
+ },
491
+ tokenResponseSchema,
492
+ signal
493
+ );
494
+ if ("error" in data) return null;
495
+ return data;
496
+ } catch {
497
+ return null;
498
+ }
499
+ }
500
+ var serviceAuthFlowEngine = {
501
+ id: "service-auth",
502
+ async run(provider, discovered, opts) {
503
+ const { auth } = provider;
504
+ if (auth.flow !== "service-auth") {
505
+ throw new Error(`serviceAuthFlowEngine: invoked with flow="${auth.flow}"`);
506
+ }
507
+ const config = auth;
508
+ const clientId = config.clientId ?? "agentproto-cli";
509
+ const server = opts.server.replace(/\/$/, "");
510
+ const identityEndpoint = discovered?.identityEndpoint ?? `${server}/agent/identity`;
511
+ const tokenEndpoint = discovered?.tokenEndpoint ?? `${server}/oauth/token`;
512
+ assertSecureUrl(identityEndpoint);
513
+ assertSecureUrl(tokenEndpoint);
514
+ const primarySlot = config.tokenStore.keychain;
515
+ const account = resolveAccount(config.tokenStore.account, server);
516
+ if (!opts.force) {
517
+ const storedAssertion = await readKeychainToken(primarySlot, account);
518
+ if (storedAssertion) {
519
+ const exchanged = await exchangeAssertion(
520
+ tokenEndpoint,
521
+ storedAssertion,
522
+ clientId,
523
+ opts.signal
524
+ );
525
+ if (exchanged) {
526
+ if (exchanged.identity_assertion) {
527
+ await writeKeychainToken(
528
+ primarySlot,
529
+ account,
530
+ exchanged.identity_assertion
531
+ );
532
+ }
533
+ return {
534
+ accessToken: exchanged.access_token,
535
+ ...exchanged.identity_assertion ? { identityAssertion: exchanged.identity_assertion } : {},
536
+ tokenKind: "oat"
537
+ };
538
+ }
539
+ }
540
+ }
541
+ const identity = await postJson(
542
+ identityEndpoint,
543
+ {
544
+ type: "service_auth",
545
+ client_id: clientId,
546
+ ...config.loginHint ? { login_hint: config.loginHint } : {}
547
+ },
548
+ identityResponseSchema,
549
+ opts.signal
550
+ );
551
+ const { claim_token, claim } = identity;
552
+ const intervalS = claim.interval ?? DEFAULT_POLL_INTERVAL_S;
553
+ const windowMin = Math.round(claim.expires_in / 60);
554
+ process.stderr.write(`
555
+ `);
556
+ process.stderr.write(` Approve ${provider.id} access in your browser
557
+
558
+ `);
559
+ process.stderr.write(` Code: ${claim.user_code}
560
+ `);
561
+ process.stderr.write(` URL: ${claim.verification_uri}
562
+
563
+ `);
564
+ await openBrowser(claim.verification_uri);
565
+ process.stderr.write(` Waiting for approval (${windowMin} min)\u2026
566
+
567
+ `);
568
+ const tokenResult = await pollForToken(
569
+ tokenEndpoint,
570
+ claim_token,
571
+ clientId,
572
+ intervalS,
573
+ claim.expires_in,
574
+ opts.signal
575
+ );
576
+ const accessToken = tokenResult.access_token;
577
+ const assertion = tokenResult.identity_assertion;
578
+ if (assertion) {
579
+ await writeKeychainToken(primarySlot, account, assertion);
580
+ }
581
+ let assertionExpires;
582
+ if (tokenResult.assertion_expires) {
583
+ assertionExpires = tokenResult.assertion_expires;
584
+ } else if (tokenResult.assertion_expires_in) {
585
+ assertionExpires = new Date(
586
+ Date.now() + tokenResult.assertion_expires_in * 1e3
587
+ ).toISOString();
588
+ }
589
+ return {
590
+ accessToken,
591
+ identityAssertion: assertion,
592
+ assertionExpires,
593
+ tokenKind: "oat"
594
+ };
595
+ }
596
+ };
597
+
598
+ // src/flow-engines/index.ts
599
+ var FLOW_ENGINES = {
600
+ [patFlowEngine.id]: patFlowEngine,
601
+ [serviceAuthFlowEngine.id]: serviceAuthFlowEngine
602
+ };
603
+
604
+ // src/run-flow.ts
605
+ async function runAuthFlow(provider, opts) {
606
+ const flowId = provider.auth.flow;
607
+ const engine = FLOW_ENGINES[flowId];
608
+ if (!engine) {
609
+ throw new Error(
610
+ `runAuthFlow: unknown flow "${flowId}" \u2014 registered: ${Object.keys(FLOW_ENGINES).join(", ")}`
611
+ );
612
+ }
613
+ let discovered = opts.discovered !== void 0 ? opts.discovered : null;
614
+ if (discovered === null && flowId === "service-auth") {
615
+ try {
616
+ discovered = await discoverEndpoints(opts.server, { signal: opts.signal });
617
+ } catch (err) {
618
+ if (!opts.quiet) {
619
+ const msg = err instanceof DiscoveryError ? err.message : `discovery failed: ${err}`;
620
+ process.stderr.write(`[auth] ${msg} \u2014 using static config
621
+ `);
622
+ }
623
+ }
624
+ }
625
+ return engine.run(provider, discovered, opts);
626
+ }
627
+
628
+ // src/index.ts
629
+ var SPEC_NAME = "agentauth";
630
+ var SPEC_VERSION = "v1";
631
+
632
+ export { BUILTIN_AUTH_PROVIDERS, DiscoveryError, FLOW_ENGINES, SPEC_NAME, SPEC_VERSION, authConfigSchema, authProviderFrontmatterSchema, defineAuthProvider, discoverEndpoints, getAuthProvider, guildeAuthProvider, installConfigSchema, listAuthProviderIds, listAuthProviders, parseAuthProviderManifest, parseAuthProviderManifestRaw, readKeychainToken, registerAuthProvider, resolveAccount, runAuthFlow, tokenStoreSpecSchema, writeKeychainToken };
633
+ //# sourceMappingURL=index.mjs.map
634
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts","../src/define-auth-provider.ts","../src/manifest.ts","../src/builtins.ts","../src/registry.ts","../src/http.ts","../src/discover.ts","../src/token-store.ts","../src/flow-engines/pat.ts","../src/flow-engines/service-auth.ts","../src/flow-engines/index.ts","../src/run-flow.ts","../src/index.ts"],"names":["z","promisify","execFile"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * AIP-50 auth-provider frontmatter / literal Zod schema.\n *\n * Single source of truth for both authoring paths: `defineAuthProvider`\n * (TS literal) and `parseAuthProviderManifest` (.md) run this same schema,\n * so a malformed literal and a malformed manifest fail identically.\n */\n\nimport { z } from \"zod\"\n\nexport const tokenStoreSpecSchema = z\n .object({\n keychain: z.string().min(1),\n account: z.string().min(1).optional(),\n })\n .strict()\n\nexport const patAuthConfigSchema = z\n .object({\n flow: z.literal(\"pat\"),\n tokenStore: tokenStoreSpecSchema,\n })\n .strict()\n\nexport const serviceAuthConfigSchema = z\n .object({\n flow: z.literal(\"service-auth\"),\n clientId: z.string().min(1).optional(),\n loginHint: z.string().min(1).optional(),\n tokenStore: tokenStoreSpecSchema,\n })\n .strict()\n\nexport const authConfigSchema = z.discriminatedUnion(\"flow\", [\n patAuthConfigSchema,\n serviceAuthConfigSchema,\n])\n\nexport const installConfigSchema = z\n .object({\n sealKey: z.string().min(1),\n secretBacked: z.string().min(1),\n })\n .strict()\n\nexport const authProviderFrontmatterSchema = z\n .object({\n id: z.string().min(2).max(80),\n description: z.string().min(1).max(2000),\n apiBase: z.string().url(),\n auth: authConfigSchema,\n install: installConfigSchema.optional(),\n })\n .strict()\n .describe(\n \"AIP-50 auth-provider manifest: how a CLI tool authenticates to an API server and (optionally) where to call the AIP-19 provision endpoints.\",\n )\n\nexport type AuthProviderFrontmatter = z.infer<typeof authProviderFrontmatterSchema>\n","/**\n * `defineAuthProvider` — TS-literal authoring path for an auth-provider.\n *\n * Built on `createDoctype` so it gets the cross-AIP invariants: id-pattern,\n * ≤2000-char description, frozen handle, canonical error prefix. Field-level\n * validation uses the shared Zod from `./schema.ts`, so a malformed literal\n * fails with the same diagnostic as a malformed `.md`.\n */\n\nimport { createDoctype } from \"@agentproto/define-doctype\"\nimport { authProviderFrontmatterSchema } from \"./schema.js\"\nimport type { AuthProviderDefinition, AuthProviderHandle } from \"./types.js\"\n\nexport const defineAuthProvider = createDoctype<\n AuthProviderDefinition,\n AuthProviderHandle\n>({\n aip: 50,\n name: \"authProvider\",\n validate(def) {\n const result = authProviderFrontmatterSchema.safeParse(def)\n if (!result.success) {\n throw new Error(\n `defineAuthProvider (AIP-50): ${result.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \")}`,\n )\n }\n },\n build(def) {\n return {\n ...def,\n auth: Object.freeze({ ...def.auth }),\n install: def.install ? Object.freeze({ ...def.install }) : undefined,\n }\n },\n})\n","/**\n * `.md` authoring path for an auth-provider — parse a frontmatter manifest\n * into a frozen handle. Mirrors `parseRecipeManifest` from AIP-19: gray-matter\n * splits the frontmatter, the shared Zod validates it, `defineAuthProvider`\n * builds the handle so both authoring paths converge on one validated shape.\n *\n * This parser is the extension seam: a host reads its own external `.md`\n * manifests (e.g. vendor-shipped `guilde.auth.md`) and registers them without\n * editing this package.\n */\n\nimport matter from \"gray-matter\"\nimport {\n authProviderFrontmatterSchema,\n type AuthProviderFrontmatter,\n} from \"./schema.js\"\nimport { defineAuthProvider } from \"./define-auth-provider.js\"\nimport type { AuthProviderHandle } from \"./types.js\"\n\nexport interface AuthProviderManifest {\n frontmatter: AuthProviderFrontmatter\n body: string\n}\n\nexport function parseAuthProviderManifestRaw(\n source: string,\n): AuthProviderManifest {\n const parsed = matter(source)\n if (Object.keys(parsed.data).length === 0) {\n throw new Error(\"parseAuthProviderManifest: missing or empty frontmatter\")\n }\n const result = authProviderFrontmatterSchema.safeParse(parsed.data)\n if (!result.success) {\n throw new Error(\n `parseAuthProviderManifest: invalid frontmatter — ${result.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \")}`,\n )\n }\n return { frontmatter: result.data, body: parsed.content }\n}\n\n/** Parse an auth-provider `.md` straight to a frozen handle. */\nexport function parseAuthProviderManifest(source: string): AuthProviderHandle {\n const { frontmatter } = parseAuthProviderManifestRaw(source)\n return defineAuthProvider(frontmatter)\n}\n","/**\n * Builtin auth-provider manifests — shipped as TS literals so they bundle\n * into the tsup output with zero filesystem reads at runtime. A host adds more\n * by parsing its own `.md` with `parseAuthProviderManifest` and registering\n * the result via `registerAuthProvider`.\n */\n\nimport { defineAuthProvider } from \"./define-auth-provider.js\"\nimport type { AuthProviderHandle } from \"./types.js\"\n\n/** Guilde AI company platform.\n *\n * Authenticates via the AIP-50 service-auth claim ceremony: browser opens,\n * user clicks Approve, bureau stores the oat_* access token.\n * Discovery from /.well-known/oauth-protected-resource supplies live endpoints;\n * the static fields below are the offline fallback. */\nexport const guildeAuthProvider = defineAuthProvider({\n id: \"guilde\",\n description:\n \"Guilde AI company platform. Authenticate via a browser-approve flow — no key to paste.\",\n apiBase: \"https://api.guilde.work\",\n auth: {\n flow: \"service-auth\",\n clientId: \"agentproto-cli\",\n tokenStore: {\n keychain: \"bureau-guilde\",\n account: \"{server}\",\n },\n },\n install: {\n sealKey: \"/guilde/api/v1/connectors/seal-key\",\n secretBacked: \"/guilde/api/v1/guilds/{guildId}/connectors/secret-backed\",\n },\n})\n\nexport const BUILTIN_AUTH_PROVIDERS: readonly AuthProviderHandle[] = [\n guildeAuthProvider,\n]\n","/**\n * Auth-provider registry — id → handle lookup, pre-seeded with builtins.\n *\n * Mirrors the provision-recipe registry shape (AIP-19). Re-registering an id\n * overrides it (last write wins), so a host can shadow a builtin.\n */\n\nimport { BUILTIN_AUTH_PROVIDERS } from \"./builtins.js\"\nimport type { AuthProviderHandle } from \"./types.js\"\n\nconst _registry = new Map<string, AuthProviderHandle>()\nfor (const p of BUILTIN_AUTH_PROVIDERS) _registry.set(p.id, p)\n\nexport function registerAuthProvider(provider: AuthProviderHandle): void {\n _registry.set(provider.id, provider)\n}\n\nexport function getAuthProvider(id: string): AuthProviderHandle | undefined {\n return _registry.get(id)\n}\n\nexport function listAuthProviders(): AuthProviderHandle[] {\n return [..._registry.values()]\n}\n\nexport function listAuthProviderIds(): string[] {\n return [..._registry.keys()]\n}\n","/**\n * Internal HTTP helpers — not part of the public API.\n *\n * Every network call in this package (discovery, the claim ceremony, token\n * refresh) must be bounded: a hung or slow server MUST NOT stall the CLI\n * indefinitely. `deadlineSignal` combines an optional caller `AbortSignal`\n * with a timeout into one signal to hand to `fetch`.\n *\n * Implemented with a plain `AbortController` + `setTimeout` rather than\n * `AbortSignal.any`/`AbortSignal.timeout` so it works on every runtime that\n * ships `fetch`, without assuming a specific Node minor.\n */\n\n/** Default per-request timeout. Discovery and token calls are small JSON\n * round-trips; 10s is generous without hanging an interactive CLI. */\nexport const DEFAULT_HTTP_TIMEOUT_MS = 10_000\n\n/**\n * AIP-50 §Security: discovery and auth requests MUST use HTTPS, to defeat\n * MITM/TOCTOU on the `.well-known` and token endpoints. Loopback http is\n * allowed — it isn't network-exposed — so local development against a dev\n * server keeps working. Throws on any other insecure URL.\n */\nexport function assertSecureUrl(url: string): void {\n let u: URL\n try {\n u = new URL(url)\n } catch {\n throw new Error(`invalid URL: ${url}`)\n }\n if (u.protocol === \"https:\") return\n const host = u.hostname\n const isLoopback =\n host === \"localhost\" ||\n host === \"127.0.0.1\" ||\n host === \"::1\" ||\n host === \"[::1]\" ||\n host.endsWith(\".localhost\")\n if (u.protocol === \"http:\" && isLoopback) return\n throw new Error(\n `insecure URL '${url}' — AIP-50 requires HTTPS for discovery/auth ` +\n `(loopback http is allowed for local development).`,\n )\n}\n\nexport interface DeadlineHandle {\n /** Signal to pass to `fetch(url, { signal })`. */\n signal: AbortSignal\n /** Cancel the timer + detach the outer listener once the request settles.\n * ALWAYS call this in a `finally` to avoid a dangling timer/listener. */\n clear: () => void\n}\n\n/**\n * Build an `AbortSignal` that fires when EITHER the timeout elapses or the\n * caller's `outer` signal aborts.\n */\nexport function deadlineSignal(\n timeoutMs: number,\n outer?: AbortSignal,\n): DeadlineHandle {\n const ctrl = new AbortController()\n const onAbort = () => ctrl.abort(outer?.reason)\n\n if (outer) {\n if (outer.aborted) ctrl.abort(outer.reason)\n else outer.addEventListener(\"abort\", onAbort, { once: true })\n }\n\n const timer = setTimeout(\n () => ctrl.abort(new Error(`request timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n // Don't let a pending timeout keep the process alive on its own.\n if (typeof timer !== \"number\") timer.unref()\n\n return {\n signal: ctrl.signal,\n clear: () => {\n clearTimeout(timer)\n outer?.removeEventListener(\"abort\", onAbort)\n },\n }\n}\n\n/** `fetch` bounded by `deadlineSignal`. Merges any caller-supplied\n * `init.signal` with the timeout. */\nexport async function fetchWithDeadline(\n url: string,\n init: RequestInit = {},\n timeoutMs: number = DEFAULT_HTTP_TIMEOUT_MS,\n): Promise<Response> {\n const { signal, clear } = deadlineSignal(timeoutMs, init.signal ?? undefined)\n try {\n return await fetch(url, { ...init, signal })\n } finally {\n clear()\n }\n}\n","/**\n * Two-hop discovery per the auth.md standard (AIP-50 §Discovery algorithm).\n *\n * Hop 1: GET {apiBase}/.well-known/oauth-protected-resource (PRM, RFC 8414)\n * → extract authorization_servers[0] as authServerBase\n * Hop 2: GET {authServerBase}/.well-known/oauth-authorization-server (AS metadata)\n * → extract token_endpoint + agent_auth block\n *\n * Callers MUST catch DiscoveryError and fall back to static manifest config —\n * discovery failures are common (server predates auth.md) and MUST NOT prevent\n * the PAT or other static flows from working.\n */\n\nimport { z } from \"zod\"\nimport type { DiscoveredEndpoints } from \"./types.js\"\nimport { fetchWithDeadline, assertSecureUrl } from \"./http.js\"\n\nexport class DiscoveryError extends Error {\n readonly serverUrl: string\n constructor(message: string, serverUrl: string) {\n super(`DiscoveryError (${serverUrl}): ${message}`)\n this.name = \"DiscoveryError\"\n this.serverUrl = serverUrl\n }\n}\n\n// Discovery JSON is server-controlled, so it's validated rather than asserted.\n// Everything is optional + `.loose()`: the document evolves, and the required\n// fields are enforced explicitly below with precise DiscoveryError messages.\nconst prmSchema = z\n .object({\n resource: z.string().optional(),\n resource_name: z.string().optional(),\n authorization_servers: z.array(z.string()).optional(),\n scopes_supported: z.array(z.string()).optional(),\n bearer_methods_supported: z.array(z.string()).optional(),\n })\n .loose()\n\nconst asMetaSchema = z\n .object({\n issuer: z.string().optional(),\n token_endpoint: z.string().optional(),\n revocation_endpoint: z.string().optional(),\n grant_types_supported: z.array(z.string()).optional(),\n agent_auth: z\n .object({\n skill: z.string().optional(),\n identity_endpoint: z.string().optional(),\n claim_endpoint: z.string().optional(),\n events_endpoint: z.string().optional(),\n identity_types_supported: z.array(z.string()).optional(),\n identity_assertion: z\n .object({ assertion_types_supported: z.array(z.string()).optional() })\n .loose()\n .optional(),\n })\n .loose()\n .optional(),\n })\n .loose()\n\ntype PRMShape = z.infer<typeof prmSchema>\ntype ASMetaShape = z.infer<typeof asMetaSchema>\n\nexport interface DiscoverOptions {\n /** Abort the discovery fetches (e.g. on user Ctrl-C). */\n signal?: AbortSignal\n /** Per-hop timeout. Defaults to DEFAULT_HTTP_TIMEOUT_MS. */\n timeoutMs?: number\n}\n\nexport async function discoverEndpoints(\n apiBase: string,\n opts: DiscoverOptions = {},\n): Promise<DiscoveredEndpoints> {\n const base = apiBase.replace(/\\/$/, \"\")\n // AIP-50 §Security: HTTPS only (loopback exempt for dev), and never follow\n // redirects on discovery — a 3xx is treated as a failure, not chased to a\n // potentially-rogue endpoint.\n const fetchOpts: RequestInit = {\n signal: opts.signal,\n redirect: \"manual\",\n }\n\n // Hop 1 — PRM\n const prmUrl = `${base}/.well-known/oauth-protected-resource`\n let prm: PRMShape\n try {\n assertSecureUrl(prmUrl)\n const res = await fetchWithDeadline(prmUrl, fetchOpts, opts.timeoutMs)\n if (!res.ok) {\n throw new DiscoveryError(\n `PRM returned ${res.status} ${res.statusText}`,\n apiBase,\n )\n }\n prm = prmSchema.parse(await res.json())\n } catch (err) {\n if (err instanceof DiscoveryError) throw err\n throw new DiscoveryError(`PRM fetch failed: ${err}`, apiBase)\n }\n\n const authServerBase = prm.authorization_servers?.[0]?.replace(/\\/$/, \"\")\n if (!authServerBase) {\n throw new DiscoveryError(\n \"PRM missing authorization_servers[0]\",\n apiBase,\n )\n }\n\n // Hop 2 — AS metadata\n const asUrl = `${authServerBase}/.well-known/oauth-authorization-server`\n let as: ASMetaShape\n try {\n assertSecureUrl(asUrl)\n const res = await fetchWithDeadline(asUrl, fetchOpts, opts.timeoutMs)\n if (!res.ok) {\n throw new DiscoveryError(\n `AS metadata returned ${res.status} ${res.statusText}`,\n apiBase,\n )\n }\n as = asMetaSchema.parse(await res.json())\n } catch (err) {\n if (err instanceof DiscoveryError) throw err\n throw new DiscoveryError(`AS metadata fetch failed: ${err}`, apiBase)\n }\n\n const tokenEndpoint = as.token_endpoint\n const identityEndpoint = as.agent_auth?.identity_endpoint\n if (!tokenEndpoint) {\n throw new DiscoveryError(\"AS metadata missing token_endpoint\", apiBase)\n }\n if (!identityEndpoint) {\n throw new DiscoveryError(\n \"AS metadata missing agent_auth.identity_endpoint\",\n apiBase,\n )\n }\n\n return {\n resource: prm.resource ?? base,\n resourceName: prm.resource_name,\n authServerBase,\n tokenEndpoint,\n revocationEndpoint: as.revocation_endpoint,\n identityEndpoint,\n claimEndpoint: as.agent_auth?.claim_endpoint,\n identityTypesSupported: as.agent_auth?.identity_types_supported ?? [],\n grantTypesSupported: as.grant_types_supported ?? [],\n }\n}\n","/**\n * Keychain helpers — read/write a token in the platform Keychain.\n *\n * Uses the macOS `security` CLI. On non-macOS hosts, callers should swap this\n * module for a platform-appropriate equivalent (libsecret on Linux, Credential\n * Manager on Windows).\n */\n\nimport { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\n\nconst exec = promisify(execFile)\n\n/**\n * Guard the macOS-only backend. Without this, `readKeychainToken` would swallow\n * the missing-`security` error and return undefined on Linux/Windows — making a\n * stored credential look absent and re-prompting on every run — while\n * `writeKeychainToken` would throw an opaque ENOENT. Fail loudly and clearly\n * instead, until a libsecret / Credential Manager backend exists.\n */\nfunction assertKeychainSupported(): void {\n if (process.platform !== \"darwin\") {\n throw new Error(\n `@agentproto/auth token-store: the Keychain backend only supports macOS ` +\n `(got platform \"${process.platform}\"). Provide a libsecret (Linux) or ` +\n `Credential Manager (Windows) implementation to run here.`,\n )\n }\n}\n\n/** Substitute `{server}` template in a tokenStore account spec. */\nexport function resolveAccount(\n account: string | undefined,\n server: string,\n): string {\n if (!account) return server\n return account.replace(\"{server}\", server)\n}\n\n/** Read a token from the Keychain. Returns undefined if not found. */\nexport async function readKeychainToken(\n service: string,\n account: string,\n): Promise<string | undefined> {\n assertKeychainSupported()\n try {\n const { stdout } = await exec(\"security\", [\n \"find-generic-password\",\n \"-s\",\n service,\n \"-a\",\n account,\n \"-w\",\n ])\n const t = stdout.replace(/\\n$/, \"\")\n return t || undefined\n } catch {\n return undefined\n }\n}\n\n/** Write a token to the Keychain (-U updates in place). */\nexport async function writeKeychainToken(\n service: string,\n account: string,\n token: string,\n): Promise<void> {\n assertKeychainSupported()\n await exec(\"security\", [\n \"add-generic-password\",\n \"-U\",\n \"-s\",\n service,\n \"-a\",\n account,\n \"-w\",\n token,\n \"-D\",\n \"agentproto auth token\",\n ])\n}\n","/**\n * PAT flow engine — reads an existing token from the Keychain or prompts for one.\n *\n * This is the \"legacy-compatible\" flow: the user has a personal API key\n * (`gld_*`, `sk-*`, …) and the CLI needs to store/retrieve it. No browser\n * open, no ceremony. Use `service-auth` for the seamless browser-approve path.\n */\n\nimport { createInterface } from \"node:readline\"\nimport type {\n FlowEngine,\n FlowRunOptions,\n FlowResult,\n AuthProviderHandle,\n DiscoveredEndpoints,\n} from \"../types.js\"\nimport { resolveAccount, readKeychainToken } from \"../token-store.js\"\n\n/**\n * Read a secret from the terminal WITHOUT echoing it — the typed key must not\n * land on screen or in scrollback. On a TTY we drop to raw mode and render a `*`\n * mask per character. When stdin isn't a TTY (piped input, CI, tests) there's no\n * echo to suppress and no raw mode to enter, so we read a line normally.\n */\nasync function promptToken(label: string): Promise<string> {\n const input = process.stdin\n const out = process.stderr\n\n if (!input.isTTY) {\n return new Promise((resolve) => {\n const rl = createInterface({ input, output: out })\n out.write(`${label}: `)\n rl.question(\"\", (answer) => {\n rl.close()\n resolve(answer.trim())\n })\n })\n }\n\n return new Promise((resolve, reject) => {\n out.write(`${label}: `)\n const chars: string[] = []\n input.setRawMode(true)\n input.resume()\n input.setEncoding(\"utf8\")\n\n const cleanup = () => {\n input.setRawMode(false)\n input.pause()\n input.removeListener(\"data\", onData)\n }\n\n const onData = (chunk: string) => {\n for (const ch of chunk) {\n switch (ch) {\n case \"\\r\":\n case \"\\n\":\n out.write(\"\\n\")\n cleanup()\n resolve(chars.join(\"\").trim())\n return\n case \"\\u0003\": // Ctrl-C\n out.write(\"\\n\")\n cleanup()\n reject(new Error(\"auth cancelled\"))\n return\n case \"\\u0004\": // Ctrl-D — treat as end-of-input\n out.write(\"\\n\")\n cleanup()\n resolve(chars.join(\"\").trim())\n return\n case \"\\u007f\": // Backspace / Delete\n case \"\\b\":\n if (chars.length > 0) {\n chars.pop()\n out.write(\"\\b \\b\")\n }\n break\n default:\n // Mask printable input; ignore stray control characters.\n if (ch >= \" \") {\n chars.push(ch)\n out.write(\"*\")\n }\n }\n }\n }\n\n input.on(\"data\", onData)\n })\n}\n\nexport const patFlowEngine: FlowEngine = {\n id: \"pat\",\n\n async run(\n provider: AuthProviderHandle,\n _discovered: DiscoveredEndpoints | null,\n opts: FlowRunOptions,\n ): Promise<FlowResult> {\n const { auth } = provider\n if (auth.flow !== \"pat\") {\n throw new Error(`patFlowEngine: invoked with flow=\"${auth.flow}\"`)\n }\n\n const account = resolveAccount(auth.tokenStore.account, opts.server)\n\n if (!opts.force) {\n const existing = await readKeychainToken(auth.tokenStore.keychain, account)\n if (existing) {\n return { accessToken: existing, tokenKind: \"pat\" }\n }\n }\n\n const token = await promptToken(`${provider.id} personal access token`)\n if (!token) throw new Error(\"no token provided\")\n\n return { accessToken: token, tokenKind: \"pat\" }\n },\n}\n","/**\n * service-auth flow engine — auth.md claim ceremony.\n *\n * Protocol:\n * POST /agent/identity { type:\"service_auth\" }\n * → registration_id + claim_token + user_code + verification_uri\n * User approves at verification_uri in browser.\n * Poll POST {token_endpoint} with grant_type=urn:workos:agent-auth:grant-type:claim\n * until approved / denied / expired.\n * On success: store the identity_assertion JWT (AIP-50 §Token storage —\n * the assertion is the durable credential; the access_token MUST NOT be\n * persisted, and the claim_token stays in memory only).\n *\n * Subsequent runs skip the ceremony: the stored assertion is exchanged via the\n * jwt-bearer grant for a fresh access_token (\"this IS the refresh path\" — no\n * refresh_token). Only when the assertion is expired/rejected does a new\n * ceremony start.\n *\n * See: https://github.com/workos/auth.md / AIP-50\n */\n\nimport { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\nimport { z } from \"zod\"\nimport type {\n FlowEngine,\n FlowRunOptions,\n FlowResult,\n AuthProviderHandle,\n DiscoveredEndpoints,\n} from \"../types.js\"\nimport {\n resolveAccount,\n readKeychainToken,\n writeKeychainToken,\n} from \"../token-store.js\"\nimport { fetchWithDeadline, assertSecureUrl } from \"../http.js\"\n\nconst execAsync = promisify(execFile)\n\nconst CLAIM_GRANT_TYPE = \"urn:workos:agent-auth:grant-type:claim\"\nconst JWT_BEARER_GRANT_TYPE = \"urn:ietf:params:oauth:grant-type:jwt-bearer\"\nconst DEFAULT_POLL_INTERVAL_S = 5\n\n// ── response schemas ──────────────────────────────────────────────────────────\n//\n// JSON crosses a trust boundary here (the AS controls the bytes), so every\n// response is validated with Zod rather than asserted. `.loose()` keeps unknown\n// fields the spec may add. The inferred types feed the rest of the engine.\n\nconst identityResponseSchema = z\n .object({\n registration_id: z.string(),\n claim_token: z.string(),\n claim: z\n .object({\n user_code: z.string(),\n verification_uri: z.string(),\n expires_in: z.number(),\n interval: z.number().optional(),\n })\n .loose(),\n })\n .loose()\n\n/** Successful token/refresh/exchange response — the AS minted a credential. */\nconst tokenSuccessSchema = z\n .object({\n access_token: z.string(),\n token_type: z.string().optional(),\n refresh_token: z.string().optional(),\n identity_assertion: z.string().optional(),\n assertion_expires_in: z.number().optional(),\n assertion_expires: z.string().optional(),\n })\n .loose()\n\n/** OAuth error response (`authorization_pending`, `slow_down`, `access_denied`…). */\nconst tokenErrorSchema = z\n .object({\n error: z.string(),\n error_description: z.string().optional(),\n })\n .loose()\n\n/** A token endpoint reply is either a minted credential or an OAuth error.\n * Success is tried first so a body carrying an access_token never matches the\n * error arm. */\nconst tokenResponseSchema = z.union([tokenSuccessSchema, tokenErrorSchema])\n\ntype TokenSuccess = z.infer<typeof tokenSuccessSchema>\n\n// ── helpers ──────────────────────────────────────────────────────────────────\n\nasync function openBrowser(url: string): Promise<void> {\n const [cmd, args]: [string, string[]] =\n process.platform === \"darwin\"\n ? [\"open\", [url]]\n : process.platform === \"win32\"\n ? [\"cmd\", [\"/c\", \"start\", url]]\n : [\"xdg-open\", [url]]\n try {\n await execAsync(cmd, args)\n } catch {\n // best-effort — URL already printed to stderr\n }\n}\n\nasync function postJson<T>(\n url: string,\n body: unknown,\n schema: z.ZodType<T>,\n signal?: AbortSignal,\n): Promise<T> {\n const res = await fetchWithDeadline(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal,\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`POST ${url} → ${res.status}: ${text}`)\n }\n return schema.parse(await res.json())\n}\n\n// NOTE: deliberately does NOT check res.ok — the OAuth device/claim flow returns\n// HTTP 400 with a JSON `{ error }` body for authorization_pending / slow_down,\n// which the poll loop reads as data. Throwing on non-2xx would break polling.\nasync function postForm<T>(\n url: string,\n params: Record<string, string>,\n schema: z.ZodType<T>,\n signal?: AbortSignal,\n): Promise<T> {\n const res = await fetchWithDeadline(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(params).toString(),\n signal,\n })\n return schema.parse(await res.json())\n}\n\nasync function pollForToken(\n tokenEndpoint: string,\n claimToken: string,\n clientId: string,\n intervalS: number,\n expiresIn: number,\n signal?: AbortSignal,\n): Promise<TokenSuccess> {\n const deadline = Date.now() + expiresIn * 1_000\n let pollMs = intervalS * 1_000\n\n while (Date.now() < deadline) {\n if (signal?.aborted) throw new Error(\"auth cancelled\")\n await new Promise<void>((r) => setTimeout(r, pollMs))\n\n const data = await postForm(\n tokenEndpoint,\n {\n grant_type: CLAIM_GRANT_TYPE,\n claim_token: claimToken,\n client_id: clientId,\n },\n tokenResponseSchema,\n signal,\n )\n\n if (!(\"error\" in data)) return data\n\n const pollErr = data.error\n if (pollErr === \"authorization_pending\") continue\n if (pollErr === \"slow_down\") {\n pollMs += 5_000\n continue\n }\n if (pollErr === \"expired_token\") {\n throw new Error(\"auth timeout — claim expired before user approved\")\n }\n if (pollErr === \"access_denied\") {\n throw new Error(\"access denied — user rejected the authorisation request\")\n }\n\n const desc = data.error_description\n throw new Error(`token endpoint error: ${pollErr}${desc ? ` — ${desc}` : \"\"}`)\n }\n\n throw new Error(\"auth timeout — approval window closed\")\n}\n\n// ── Identity-assertion exchange (jwt-bearer, RFC 7523) ────────────────────────\n//\n// The stored credential IS the identity_assertion JWT. Exchange it at the token\n// endpoint for a fresh, short-lived access token — the AIP-50 refresh path, with\n// no refresh_token involved. Returns null on any error (expired / invalid_grant\n// / server doesn't support the grant) so the caller falls back to a ceremony.\nasync function exchangeAssertion(\n tokenEndpoint: string,\n assertion: string,\n clientId: string,\n signal?: AbortSignal,\n): Promise<TokenSuccess | null> {\n try {\n const data = await postForm(\n tokenEndpoint,\n {\n grant_type: JWT_BEARER_GRANT_TYPE,\n assertion,\n client_id: clientId,\n },\n tokenResponseSchema,\n signal,\n )\n if (\"error\" in data) return null\n return data\n } catch {\n return null\n }\n}\n\n// ── engine ───────────────────────────────────────────────────────────────────\n\nexport const serviceAuthFlowEngine: FlowEngine = {\n id: \"service-auth\",\n\n async run(\n provider: AuthProviderHandle,\n discovered: DiscoveredEndpoints | null,\n opts: FlowRunOptions,\n ): Promise<FlowResult> {\n const { auth } = provider\n if (auth.flow !== \"service-auth\") {\n throw new Error(`serviceAuthFlowEngine: invoked with flow=\"${auth.flow}\"`)\n }\n // `auth.flow === \"service-auth\"` is guaranteed by the guard above, so the\n // discriminated union has already narrowed `auth` to ServiceAuthConfig.\n const config = auth\n const clientId = config.clientId ?? \"agentproto-cli\"\n const server = opts.server.replace(/\\/$/, \"\")\n\n // Endpoint resolution — prefer live discovery, fall back to conventions\n const identityEndpoint =\n discovered?.identityEndpoint ?? `${server}/agent/identity`\n const tokenEndpoint =\n discovered?.tokenEndpoint ?? `${server}/oauth/token`\n\n // AIP-50 §Security: auth requests MUST use HTTPS (loopback exempt for dev).\n assertSecureUrl(identityEndpoint)\n assertSecureUrl(tokenEndpoint)\n\n const primarySlot = config.tokenStore.keychain\n const account = resolveAccount(config.tokenStore.account, server)\n\n // Cached path — the stored credential IS the identity_assertion JWT (AIP-50:\n // never the access token, never a refresh token). Exchange it via jwt-bearer\n // for a fresh access token; on failure (expired / invalid_grant) fall through\n // to a full browser ceremony.\n if (!opts.force) {\n const storedAssertion = await readKeychainToken(primarySlot, account)\n if (storedAssertion) {\n const exchanged = await exchangeAssertion(\n tokenEndpoint,\n storedAssertion,\n clientId,\n opts.signal,\n )\n if (exchanged) {\n // If the server rotated the assertion, persist the new one; otherwise\n // the existing assertion stays valid for the next exchange.\n if (exchanged.identity_assertion) {\n await writeKeychainToken(\n primarySlot,\n account,\n exchanged.identity_assertion,\n )\n }\n return {\n accessToken: exchanged.access_token,\n ...(exchanged.identity_assertion\n ? { identityAssertion: exchanged.identity_assertion }\n : {}),\n tokenKind: \"oat\",\n }\n }\n }\n }\n\n // 1 — POST /agent/identity to start the claim ceremony\n const identity = await postJson(\n identityEndpoint,\n {\n type: \"service_auth\",\n client_id: clientId,\n ...(config.loginHint ? { login_hint: config.loginHint } : {}),\n },\n identityResponseSchema,\n opts.signal,\n )\n\n const { claim_token, claim } = identity\n const intervalS = claim.interval ?? DEFAULT_POLL_INTERVAL_S\n const windowMin = Math.round(claim.expires_in / 60)\n\n // 2 — Print code + open browser\n process.stderr.write(`\\n`)\n process.stderr.write(` Approve ${provider.id} access in your browser\\n\\n`)\n process.stderr.write(` Code: ${claim.user_code}\\n`)\n process.stderr.write(` URL: ${claim.verification_uri}\\n\\n`)\n await openBrowser(claim.verification_uri)\n process.stderr.write(` Waiting for approval (${windowMin} min)…\\n\\n`)\n\n // 3 — Poll until approved / denied / expired\n const tokenResult = await pollForToken(\n tokenEndpoint,\n claim_token,\n clientId,\n intervalS,\n claim.expires_in,\n opts.signal,\n )\n\n // 4 — Store the identity_assertion as the durable credential (AIP-50: the\n // access_token is ephemeral and MUST NOT be persisted; the claim_token\n // was held in memory only). The assertion is re-exchanged via jwt-bearer\n // on the next run.\n const accessToken = tokenResult.access_token\n const assertion = tokenResult.identity_assertion\n\n if (assertion) {\n await writeKeychainToken(primarySlot, account, assertion)\n }\n\n // Resolve assertion expiry as ISO 8601\n let assertionExpires: string | undefined\n if (tokenResult.assertion_expires) {\n assertionExpires = tokenResult.assertion_expires\n } else if (tokenResult.assertion_expires_in) {\n assertionExpires = new Date(\n Date.now() + tokenResult.assertion_expires_in * 1_000,\n ).toISOString()\n }\n\n return {\n accessToken,\n identityAssertion: assertion,\n assertionExpires,\n tokenKind: \"oat\",\n }\n },\n}\n","/**\n * Flow engine registry — dispatch by `provider.auth.flow`.\n *\n * Add a new engine:\n * 1. Implement `FlowEngine` in a sibling file\n * 2. Add it here — no if/switch chains at call sites\n */\n\nimport type { FlowEngine } from \"../types.js\"\nimport { patFlowEngine } from \"./pat.js\"\nimport { serviceAuthFlowEngine } from \"./service-auth.js\"\n\nexport const FLOW_ENGINES: Readonly<Record<string, FlowEngine>> = {\n [patFlowEngine.id]: patFlowEngine,\n [serviceAuthFlowEngine.id]: serviceAuthFlowEngine,\n}\n","/**\n * `runAuthFlow` — resolve provider → discover endpoints → dispatch to engine.\n *\n * Discovery is attempted silently; callers receive the result without knowing\n * whether live or static endpoint config was used. The flow engine receives\n * `null` for `discovered` if discovery failed or was not attempted.\n */\n\nimport { FLOW_ENGINES } from \"./flow-engines/index.js\"\nimport { discoverEndpoints, DiscoveryError } from \"./discover.js\"\nimport type {\n AuthProviderHandle,\n DiscoveredEndpoints,\n FlowResult,\n FlowRunOptions,\n} from \"./types.js\"\n\nexport interface RunFlowOptions extends FlowRunOptions {\n /** Pre-resolved endpoints (skip discovery). Pass null to force re-discovery. */\n discovered?: DiscoveredEndpoints | null\n /** Suppress debug output. Default: false. */\n quiet?: boolean\n}\n\nexport async function runAuthFlow(\n provider: AuthProviderHandle,\n opts: RunFlowOptions,\n): Promise<FlowResult> {\n const flowId = provider.auth.flow\n const engine = FLOW_ENGINES[flowId]\n if (!engine) {\n throw new Error(\n `runAuthFlow: unknown flow \"${flowId}\" — registered: ${Object.keys(FLOW_ENGINES).join(\", \")}`,\n )\n }\n\n // Discovery: try unless caller already provided endpoints\n let discovered: DiscoveredEndpoints | null =\n opts.discovered !== undefined ? opts.discovered : null\n\n if (discovered === null && flowId === \"service-auth\") {\n try {\n discovered = await discoverEndpoints(opts.server, { signal: opts.signal })\n } catch (err) {\n if (!opts.quiet) {\n const msg =\n err instanceof DiscoveryError\n ? err.message\n : `discovery failed: ${err}`\n process.stderr.write(`[auth] ${msg} — using static config\\n`)\n }\n }\n }\n\n return engine.run(provider, discovered, opts)\n}\n","/**\n * @agentproto/auth — AIP-50 AUTH.md reference implementation.\n *\n * Public API:\n * defineAuthProvider(def) — TS-literal authoring (frozen handle)\n * parseAuthProviderManifest(source) — .md authoring (gray-matter + Zod)\n * registerAuthProvider(handle) — extend the module-level registry\n * getAuthProvider(id) — lookup by vendor id\n * listAuthProviderIds() — enumerate known providers\n * discoverEndpoints(apiBase) — two-hop PRM → AS metadata discovery\n * runAuthFlow(provider, opts) — resolve + discover + dispatch to engine\n * FLOW_ENGINES — registered flow engines (pat, …)\n * writeKeychainToken(svc, acct, t) — persist a credential to Keychain\n * readKeychainToken(svc, acct) — read a credential from Keychain\n * resolveAccount(acct, server) — expand {server} template\n */\n\nexport { defineAuthProvider } from \"./define-auth-provider.js\"\nexport {\n parseAuthProviderManifest,\n parseAuthProviderManifestRaw,\n type AuthProviderManifest,\n} from \"./manifest.js\"\nexport {\n registerAuthProvider,\n getAuthProvider,\n listAuthProviders,\n listAuthProviderIds,\n} from \"./registry.js\"\nexport {\n discoverEndpoints,\n DiscoveryError,\n} from \"./discover.js\"\nexport { runAuthFlow, type RunFlowOptions } from \"./run-flow.js\"\nexport { FLOW_ENGINES } from \"./flow-engines/index.js\"\nexport {\n readKeychainToken,\n writeKeychainToken,\n resolveAccount,\n} from \"./token-store.js\"\nexport { guildeAuthProvider, BUILTIN_AUTH_PROVIDERS } from \"./builtins.js\"\nexport {\n authProviderFrontmatterSchema,\n authConfigSchema,\n tokenStoreSpecSchema,\n installConfigSchema,\n type AuthProviderFrontmatter,\n} from \"./schema.js\"\nexport type {\n AuthProviderDefinition,\n AuthProviderHandle,\n AuthConfig,\n PATAuthConfig,\n ServiceAuthConfig,\n InstallConfig,\n TokenStoreSpec,\n FlowEngine,\n FlowId,\n FlowResult,\n FlowRunOptions,\n DiscoveredEndpoints,\n} from \"./types.js\"\n\nexport const SPEC_NAME = \"agentauth\"\nexport const SPEC_VERSION = \"v1\"\n"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@agentproto/auth",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "@agentproto/auth — AIP-50 AUTH.md reference implementation. Auth-provider doctype: how CLI tools and agents authenticate to API servers via a standardized discovery chain and pluggable flow engines, aligned to the WorkOS auth.md open standard.",
5
+ "keywords": [
6
+ "agentproto",
7
+ "aip-50",
8
+ "auth",
9
+ "defineAuthProvider",
10
+ "open-standard",
11
+ "agentic"
12
+ ],
13
+ "homepage": "https://agentproto.sh/docs/aip-50",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/agentproto/ts",
17
+ "directory": "packages/auth"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/agentproto/ts/issues"
21
+ },
22
+ "license": "MIT",
23
+ "type": "module",
24
+ "main": "dist/index.mjs",
25
+ "module": "dist/index.mjs",
26
+ "types": "dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.mjs",
31
+ "default": "./dist/index.mjs"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "dependencies": {
44
+ "gray-matter": "^4.0.3",
45
+ "zod": "^4.4.3",
46
+ "@agentproto/define-doctype": "0.1.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.6.2",
50
+ "tsup": "^8.5.1",
51
+ "typescript": "^5.9.3",
52
+ "vitest": "^3.2.4",
53
+ "@agentproto/tooling": "0.1.0-alpha.0"
54
+ },
55
+ "scripts": {
56
+ "dev": "tsup --watch",
57
+ "build": "tsup",
58
+ "clean": "rm -rf dist",
59
+ "check-types": "tsc --noEmit",
60
+ "test": "vitest run --passWithNoTests",
61
+ "test:watch": "vitest"
62
+ }
63
+ }