@cotal-ai/auth 0.0.0 → 0.11.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.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/dist/callout.d.ts +108 -0
  3. package/dist/callout.d.ts.map +1 -0
  4. package/dist/callout.js +219 -0
  5. package/dist/callout.js.map +1 -0
  6. package/dist/commands.d.ts +2 -0
  7. package/dist/commands.d.ts.map +1 -0
  8. package/dist/commands.js +359 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/derive.d.ts +15 -0
  11. package/dist/derive.d.ts.map +1 -0
  12. package/dist/derive.js +72 -0
  13. package/dist/derive.js.map +1 -0
  14. package/dist/idp.d.ts +63 -0
  15. package/dist/idp.d.ts.map +1 -0
  16. package/dist/idp.js +125 -0
  17. package/dist/idp.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +13 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/issuer.d.ts +78 -0
  23. package/dist/issuer.d.ts.map +1 -0
  24. package/dist/issuer.js +137 -0
  25. package/dist/issuer.js.map +1 -0
  26. package/dist/ledger.d.ts +108 -0
  27. package/dist/ledger.d.ts.map +1 -0
  28. package/dist/ledger.js +399 -0
  29. package/dist/ledger.js.map +1 -0
  30. package/dist/login.d.ts +69 -0
  31. package/dist/login.d.ts.map +1 -0
  32. package/dist/login.js +338 -0
  33. package/dist/login.js.map +1 -0
  34. package/dist/permissions.d.ts +28 -0
  35. package/dist/permissions.d.ts.map +1 -0
  36. package/dist/permissions.js +50 -0
  37. package/dist/permissions.js.map +1 -0
  38. package/dist/provider.d.ts +18 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +213 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/service.d.ts +10 -0
  43. package/dist/service.d.ts.map +1 -0
  44. package/dist/service.js +288 -0
  45. package/dist/service.js.map +1 -0
  46. package/dist/store.d.ts +82 -0
  47. package/dist/store.d.ts.map +1 -0
  48. package/dist/store.js +208 -0
  49. package/dist/store.js.map +1 -0
  50. package/dist/token.d.ts +68 -0
  51. package/dist/token.d.ts.map +1 -0
  52. package/dist/token.js +128 -0
  53. package/dist/token.js.map +1 -0
  54. package/package.json +35 -5
  55. package/README.md +0 -4
package/dist/login.js ADDED
@@ -0,0 +1,338 @@
1
+ /**
2
+ * `cotal login` client — device-authorization sign-in (RFC 8628) plus the machine-local IdP
3
+ * session cache (plan §"How it lands in today's code" → Client).
4
+ *
5
+ * Flow: ask the IdP for a device code, show the human a verification URL + user code, poll until
6
+ * the sign-in is approved in a browser. What comes back — and the ONLY thing ever cached — is the
7
+ * IdP SESSION token. The short-lived IdP JWT the bridge exchanges is fetched fresh from `/token`
8
+ * per use ({@link fetchIdpJwt}): caching the JWT would let a revoked session keep minting mesh
9
+ * access until the JWT expired, so instead revocation at the IdP bites at the very next fetch —
10
+ * a 401 surfaces as a legible "run `cotal login` again", never a silent hang.
11
+ *
12
+ * Device code, not auth-code+PKCE: it works headless (agents, SSH) and needs nothing beyond the
13
+ * IdP's device endpoints — no OIDC provider metadata, no loopback redirect server (the plan
14
+ * explicitly avoids Better Auth's draft OIDC-provider surface). Better Auth's
15
+ * `deviceAuthorization` plugin is the reference IdP; the wire contract this client assumes is
16
+ * pinned by the login smoke against a real instance. One deliberate deviation from strict
17
+ * RFC 8628 §3.4: bodies are posted as JSON, because Better Auth's endpoint layer REJECTS
18
+ * `application/x-www-form-urlencoded` (415). A strict-RFC IdP plugs in via a thin adapter, not
19
+ * by bending the reference client.
20
+ *
21
+ * `idpUrl` throughout is the IdP's AUTH BASE URL — every endpoint resolves under it (Better
22
+ * Auth: `<origin>/api/auth`). Same origin posture as the JWKS pin: https, or loopback http for
23
+ * local dev; embedded `user:pass@` credentials are refused (the @-confusion host spoof).
24
+ *
25
+ * Every IdP request carries a hard per-request timeout ({@link idpFetch}) — Node's global fetch
26
+ * has none, and the poll deadline is only checked between polls, so a hung IdP would otherwise
27
+ * stall a login (or a non-interactive `requireIdpSession`→`fetchIdpJwt` on an agent connect)
28
+ * forever. Override the 30s default with `COTAL_IDP_TIMEOUT_MS` for a slow IdP.
29
+ */
30
+ import { existsSync, readFileSync } from "node:fs";
31
+ import { join } from "node:path";
32
+ import { decodeJwt } from "jose";
33
+ import { mkSecretDir, writeSecretFileAtomic } from "@cotal-ai/core";
34
+ /** Normalize + guard the IdP base URL: https (or loopback http for dev), no query/hash, no
35
+ * trailing slash — the normalized string is also the session-cache key, so `…/api/auth` and
36
+ * `…/api/auth/` must land on the same entry. */
37
+ export function normalizeIdpUrl(idpUrl) {
38
+ let u;
39
+ try {
40
+ u = new URL(idpUrl);
41
+ }
42
+ catch {
43
+ throw new Error(`idp url "${idpUrl}" is not a valid URL`);
44
+ }
45
+ // WHATWG URL keeps the brackets on an IPv6 hostname — "[::1]", not "::1" (same set as the
46
+ // issuer's pinned-JWKS origin guard).
47
+ const loopback = u.hostname === "127.0.0.1" || u.hostname === "[::1]" || u.hostname === "localhost";
48
+ if (u.protocol !== "https:" && !(u.protocol === "http:" && loopback))
49
+ throw new Error(`idp url must be https (or loopback http for local dev) - got "${idpUrl}"`);
50
+ // A query/hash would be silently DROPPED by the normalization below — and a silently altered
51
+ // auth base is a different IdP than the operator asked for. Refuse instead.
52
+ if (u.search !== "" || u.hash !== "")
53
+ throw new Error(`idp url must not carry a query or fragment - got "${idpUrl}"`);
54
+ // Same class: `--idp https://real-idp.example@evil.example/api/auth` parses to host
55
+ // evil.example, so an operator who eyeballed "real-idp.example" would sign in against evil.
56
+ // The normalization below drops the userinfo silently — refuse it (don't echo the password).
57
+ if (u.username !== "" || u.password !== "")
58
+ throw new Error(`idp url must not embed credentials before the host - the host it would actually contact is "${u.host}"`);
59
+ return u.origin + u.pathname.replace(/\/+$/, "");
60
+ }
61
+ async function oauthError(res) {
62
+ try {
63
+ return (await res.json());
64
+ }
65
+ catch {
66
+ return {};
67
+ }
68
+ }
69
+ /** Per-request timeout budget (ms). Default 30s; `COTAL_IDP_TIMEOUT_MS` overrides for a slow IdP
70
+ * (or a test). A malformed override fails loud rather than silently reverting to the default. */
71
+ function idpTimeoutMs() {
72
+ const raw = process.env.COTAL_IDP_TIMEOUT_MS;
73
+ if (raw === undefined)
74
+ return 30_000;
75
+ const n = Number(raw);
76
+ if (!Number.isFinite(n) || n <= 0)
77
+ throw new Error(`COTAL_IDP_TIMEOUT_MS must be a positive number of milliseconds - got "${raw}"`);
78
+ return n;
79
+ }
80
+ /** `fetch` with a hard per-request timeout, so a hung IdP connection can never stall the client
81
+ * (Node's global fetch has no default timeout). A timeout or transport failure surfaces as a
82
+ * legible sentence rather than a raw DOMException/TypeError. */
83
+ async function idpFetch(url, init) {
84
+ const ms = idpTimeoutMs();
85
+ try {
86
+ return await fetch(url, { ...init, signal: AbortSignal.timeout(ms) });
87
+ }
88
+ catch (e) {
89
+ if (e instanceof DOMException && e.name === "TimeoutError")
90
+ throw new Error(`idp request to ${url} timed out after ${ms}ms - the IdP is unreachable or not responding`);
91
+ throw new Error(`idp request to ${url} failed: ${e instanceof Error ? e.message : String(e)}`);
92
+ }
93
+ }
94
+ /** Read a JSON body under the SAME normalization as {@link idpFetch}. The request timeout can fire
95
+ * DURING the body read (an IdP that flushes headers then stalls or truncates the body), and that
96
+ * abort is raised from `res.json()` — outside idpFetch's catch — so without this it would leak a
97
+ * raw DOMException/parse error instead of the legible "idp request to …" sentence. */
98
+ async function idpJson(url, res) {
99
+ try {
100
+ return (await res.json());
101
+ }
102
+ catch (e) {
103
+ if (e instanceof DOMException && e.name === "TimeoutError")
104
+ throw new Error(`idp request to ${url} timed out reading the response body - the IdP flushed headers then stalled`);
105
+ throw new Error(`idp request to ${url} returned an unreadable response body: ${e instanceof Error ? e.message : String(e)}`);
106
+ }
107
+ }
108
+ /** Best-effort reachability + shape check of an IdP's JWKS. Used at the FIRST `--user-auth` enable
109
+ * so a dead or typo'd `--idp` fails loud BEFORE a space is provisioned around it, instead of only
110
+ * surfacing far away at the first user connect (the pin is written but the JWKS is fetched lazily).
111
+ * Normalizes the URL exactly as the pin does (`<base>/jwks`). Throws a legible sentence on an
112
+ * unreachable host, a non-2xx, or a body that is not a JWKS key set. */
113
+ export async function probeIdpJwks(idpUrl) {
114
+ const jwksUri = `${normalizeIdpUrl(idpUrl)}/jwks`; // a bad scheme throws its own message here
115
+ const res = await idpFetch(jwksUri);
116
+ if (!res.ok)
117
+ throw new Error(`the IdP at ${idpUrl} did not serve a JWKS: ${jwksUri} returned HTTP ${res.status}. Check --idp <auth base URL>.`);
118
+ const body = await idpJson(jwksUri, res);
119
+ if (!Array.isArray(body.keys))
120
+ throw new Error(`the IdP at ${idpUrl} did not return a JWKS key set at ${jwksUri}. Check --idp <auth base URL>; it must expose <base>/jwks.`);
121
+ }
122
+ /** True if `s` parses as a JWT — used to REJECT a JWT where an opaque session token is required.
123
+ * The revocation model depends on the cached token being revocable; a JWT stays valid until it
124
+ * expires regardless of revocation, so one must never be cached as the session handle. */
125
+ function looksLikeJwt(s) {
126
+ try {
127
+ decodeJwt(s);
128
+ return true;
129
+ }
130
+ catch {
131
+ return false;
132
+ }
133
+ }
134
+ /** Sign in via the RFC 8628 device flow and return the IdP session. Fail-loud on every non-happy
135
+ * path: a deny, an expiry, or an unknown poll error is a thrown human sentence, never a retry
136
+ * loop. Blocks (polling at the server's stated interval) until the human approves. */
137
+ export async function deviceLogin(opts) {
138
+ const base = normalizeIdpUrl(opts.idpUrl);
139
+ const res = await idpFetch(`${base}/device/code`, {
140
+ method: "POST",
141
+ headers: { "content-type": "application/json" },
142
+ body: JSON.stringify({ client_id: opts.clientId }),
143
+ });
144
+ if (!res.ok) {
145
+ const e = await oauthError(res);
146
+ throw new Error(`idp login: ${base} refused the device authorization (${e.error ?? `HTTP ${res.status}`}: ${e.error_description ?? "no detail"})`);
147
+ }
148
+ const grant = await idpJson(`${base}/device/code`, res);
149
+ // The WHOLE grant is shape-checked before anything is shown or polled — the timing fields
150
+ // especially: a malicious IdP handing back `interval: "abc"` or a non-finite/absurd
151
+ // `expires_in` would otherwise turn the poll loop into a tight (setTimeout coerces garbage to
152
+ // ~1ms) or unbounded one. Bounds: a device code living past 24h or a poll interval past 5min
153
+ // is not a sane grant — refuse, don't clamp.
154
+ const sane = (v, max) => typeof v === "number" && Number.isFinite(v) && v > 0 && v <= max;
155
+ if (typeof grant.device_code !== "string" || !grant.device_code ||
156
+ typeof grant.user_code !== "string" || !grant.user_code ||
157
+ typeof grant.verification_uri !== "string" || !grant.verification_uri ||
158
+ typeof grant.verification_uri_complete !== "string" || !grant.verification_uri_complete ||
159
+ !sane(grant.expires_in, 86_400) ||
160
+ (grant.interval !== undefined && !sane(grant.interval, 300)))
161
+ throw new Error(`idp login: ${base} returned a malformed device grant - refusing to poll on it`);
162
+ opts.onPrompt({
163
+ verificationUri: grant.verification_uri,
164
+ verificationUriComplete: grant.verification_uri_complete,
165
+ userCode: grant.user_code,
166
+ expiresInSec: grant.expires_in,
167
+ });
168
+ const deadline = Date.now() + grant.expires_in * 1000;
169
+ // RFC 8628 §3.5: poll at the server's interval; `slow_down` adds 5s to it, permanently.
170
+ let intervalSec = Math.max(1, grant.interval || 5);
171
+ for (;;) {
172
+ await new Promise((r) => setTimeout(r, intervalSec * 1000));
173
+ if (Date.now() > deadline)
174
+ throw new Error(`idp login: the device code expired after ${grant.expires_in}s without approval - run \`cotal login\` again`);
175
+ const poll = await idpFetch(`${base}/device/token`, {
176
+ method: "POST",
177
+ headers: { "content-type": "application/json" },
178
+ body: JSON.stringify({
179
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
180
+ device_code: grant.device_code,
181
+ client_id: opts.clientId,
182
+ }),
183
+ });
184
+ if (poll.ok) {
185
+ const tok = await idpJson(`${base}/device/token`, poll);
186
+ // Bound the session lifetime like the device grant's timing fields above (finite, positive,
187
+ // not past a year). `expiresAt` is advisory only — the server is the revocation authority —
188
+ // so this is a legibility/symmetry guard, not a security boundary: it refuses a hostile
189
+ // `expires_in: 1e12` rather than caching it and printing "Session cached … until <year 33000>".
190
+ if (typeof tok.access_token !== "string" || !tok.access_token || !sane(tok.expires_in, 31_536_000))
191
+ throw new Error(`idp login: ${base} returned a malformed token response`);
192
+ // Defense-in-depth for the revocation model: we cache the OPAQUE session handle precisely so
193
+ // that IdP revocation bites at the next /token fetch. A JWT outlives its session, so if a
194
+ // misconfigured IdP hands one back as the device token, refuse rather than silently cache a
195
+ // credential that can't be revoked.
196
+ if (looksLikeJwt(tok.access_token))
197
+ throw new Error(`idp login: ${base} returned a JWT as the device access token - the session token must be an opaque revocable handle, refusing to cache it; re-run \`cotal login\` after fixing the IdP`);
198
+ return { token: tok.access_token, expiresAt: Math.floor(Date.now() / 1000) + tok.expires_in };
199
+ }
200
+ const e = await oauthError(poll);
201
+ if (e.error === "authorization_pending")
202
+ continue;
203
+ if (e.error === "slow_down") {
204
+ intervalSec += 5;
205
+ continue;
206
+ }
207
+ if (e.error === "access_denied")
208
+ throw new Error("idp login: the sign-in was denied at the verification page");
209
+ if (e.error === "expired_token")
210
+ throw new Error("idp login: the device code expired before the sign-in was approved - run `cotal login` again");
211
+ throw new Error(`idp login: ${base} rejected the poll (${e.error ?? `HTTP ${poll.status}`}: ${e.error_description ?? "no detail"})`);
212
+ }
213
+ }
214
+ /** Fetch a fresh short-lived IdP user JWT for the cached session — the input to the bridge
215
+ * exchange. A 401 means the session was revoked or expired at the IdP: the error says exactly
216
+ * how to recover. The JWT is returned, never stored. */
217
+ export async function fetchIdpJwt(idpUrl, sessionToken) {
218
+ const base = normalizeIdpUrl(idpUrl);
219
+ const res = await idpFetch(`${base}/token`, { headers: { authorization: `Bearer ${sessionToken}` } });
220
+ if (res.status === 401)
221
+ throw new Error(`idp session: ${base} rejected the cached session (expired or revoked) - run \`cotal login --idp ${base}\` to sign in again`);
222
+ if (!res.ok)
223
+ throw new Error(`idp session: ${base}/token failed (HTTP ${res.status})`);
224
+ const body = await idpJson(`${base}/token`, res);
225
+ if (typeof body.token !== "string" || !body.token)
226
+ throw new Error(`idp session: ${base}/token returned no token`);
227
+ return body.token;
228
+ }
229
+ /** The whole login operation, in the only safe order: device sign-in, then PROVE the session
230
+ * mints a user JWT, and only then persist it. A session that can't produce a JWT must never
231
+ * land on disk — it would pass {@link requireIdpSession}'s no-fallback gate as a dud and defer
232
+ * the failure to some later connect. Returns the session plus the JWT's `sub`, and a human
233
+ * `label` (email/name/preferred_username when the IdP mints one) — BOTH display-only: the
234
+ * operator must be able to read WHICH human signed in, but verification is the bridge/callout's
235
+ * job, server-side. */
236
+ export async function establishIdpSession(opts) {
237
+ const session = await deviceLogin(opts);
238
+ const jwt = await fetchIdpJwt(opts.idpUrl, session.token);
239
+ let claims;
240
+ try {
241
+ claims = decodeJwt(jwt);
242
+ }
243
+ catch {
244
+ // fetchIdpJwt only guarantees a non-empty string; a hostile IdP returning /token 200 with a
245
+ // non-JWT body would otherwise surface jose's raw "Invalid JWT" here.
246
+ throw new Error(`idp login: ${normalizeIdpUrl(opts.idpUrl)} returned a /token value that is not a decodable JWT - refusing to cache the session; re-run \`cotal login\` after fixing the IdP`);
247
+ }
248
+ const sub = claims.sub;
249
+ if (typeof sub !== "string" || !sub)
250
+ throw new Error(`idp login: ${normalizeIdpUrl(opts.idpUrl)} minted a user JWT without a sub - refusing to cache the session; re-run \`cotal login\` after fixing the IdP`);
251
+ session.sub = sub; // proven above — cached so owner derivation (spawn paths) stays offline
252
+ saveIdpSession(opts.dir, opts.idpUrl, session);
253
+ const label = [claims.email, claims.name, claims.preferred_username].find((c) => typeof c === "string" && c.length > 0);
254
+ return { session, sub, ...(label ? { label } : {}) };
255
+ }
256
+ /** Revoke the session server-side (sign out). A 401 back means the session is already dead —
257
+ * the goal state, treated as success; any other failure is thrown because a still-live
258
+ * server-side session is a real leak the operator must hear about. */
259
+ export async function revokeIdpSession(idpUrl, sessionToken) {
260
+ const base = normalizeIdpUrl(idpUrl);
261
+ const res = await idpFetch(`${base}/sign-out`, {
262
+ method: "POST",
263
+ headers: { authorization: `Bearer ${sessionToken}`, "content-type": "application/json" },
264
+ body: "{}",
265
+ });
266
+ if (!res.ok && res.status !== 401)
267
+ throw new Error(`idp logout: ${base}/sign-out failed (HTTP ${res.status}) - the server-side session may still be alive`);
268
+ }
269
+ // ---- the machine-local session cache ----
270
+ // Explicit-dir APIs like the workspace auth-path helpers: the caller (the `login` command) picks
271
+ // the directory (`homeCotalDir()`); nothing here discovers paths ambiently. One file, all IdPs,
272
+ // keyed by normalized base URL; 0700 dir / 0600 file via core's secret-file helpers.
273
+ const SESSIONS_FILE = "idp-sessions.json";
274
+ const SESSIONS_VER = 1;
275
+ function readSessionsFile(dir) {
276
+ const f = join(dir, SESSIONS_FILE);
277
+ if (!existsSync(f))
278
+ return { ver: SESSIONS_VER, sessions: {} };
279
+ // A torn write or a hand-edit must not reach requireIdpSession (the no-fallback gate) as a raw
280
+ // "SyntaxError: Unexpected token" — say what's wrong and how to recover, like everywhere else.
281
+ let parsed;
282
+ try {
283
+ parsed = JSON.parse(readFileSync(f, "utf8"));
284
+ }
285
+ catch (e) {
286
+ throw new Error(`${f}: the session cache is not valid JSON (${e instanceof Error ? e.message : String(e)}) - delete it and run \`cotal login\` again`);
287
+ }
288
+ if (parsed === null || typeof parsed !== "object")
289
+ throw new Error(`${f}: the session cache is not a JSON object - delete it and run \`cotal login\` again`);
290
+ if (parsed.ver !== SESSIONS_VER)
291
+ throw new Error(`${f}: unknown version ${String(parsed.ver)} (expected ${SESSIONS_VER}) - refusing to guess at a credential file`);
292
+ if (parsed.sessions === null || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions))
293
+ throw new Error(`${f}: malformed sessions map`);
294
+ return parsed;
295
+ }
296
+ export function loadIdpSession(dir, idpUrl) {
297
+ const key = normalizeIdpUrl(idpUrl);
298
+ const s = readSessionsFile(dir).sessions[key];
299
+ if (s === undefined)
300
+ return undefined;
301
+ if (typeof s.token !== "string" || !s.token || typeof s.expiresAt !== "number")
302
+ throw new Error(`stored idp session for ${key} is malformed - run \`cotal login --idp ${key}\` again`);
303
+ // `sub` rides the cache round-trip (offline owner derivation for the spawn paths) — every
304
+ // consumer of ownerForLogin is a SEPARATE process re-reading this file, so dropping it here
305
+ // broke both user-mode spawn entry points while every in-process test passed.
306
+ return { token: s.token, expiresAt: s.expiresAt, ...(typeof s.sub === "string" && s.sub ? { sub: s.sub } : {}) };
307
+ }
308
+ export function saveIdpSession(dir, idpUrl, session) {
309
+ const file = readSessionsFile(dir);
310
+ file.sessions[normalizeIdpUrl(idpUrl)] = {
311
+ token: session.token,
312
+ expiresAt: session.expiresAt,
313
+ ...(session.sub ? { sub: session.sub } : {}),
314
+ };
315
+ mkSecretDir(dir); // harden the dir BEFORE the secret lands (0700 POSIX, private ACL win32)
316
+ writeSecretFileAtomic(join(dir, SESSIONS_FILE), JSON.stringify(file, null, 2));
317
+ }
318
+ /** Remove the cached session. Returns false when there was nothing to remove. */
319
+ export function deleteIdpSession(dir, idpUrl) {
320
+ const key = normalizeIdpUrl(idpUrl);
321
+ const file = readSessionsFile(dir);
322
+ if (!(key in file.sessions))
323
+ return false;
324
+ delete file.sessions[key];
325
+ mkSecretDir(dir);
326
+ writeSecretFileAtomic(join(dir, SESSIONS_FILE), JSON.stringify(file, null, 2));
327
+ return true;
328
+ }
329
+ /** The no-fallback gate the user-mode connect path consumes: a session or a thrown sentence that
330
+ * says exactly how to get one. There is no anonymous degradation when a space requires a user. */
331
+ export function requireIdpSession(dir, idpUrl) {
332
+ const key = normalizeIdpUrl(idpUrl);
333
+ const s = loadIdpSession(dir, key);
334
+ if (!s)
335
+ throw new Error(`not logged in to ${key} - this space requires a user identity and there is no anonymous fallback; run \`cotal login --idp ${key}\``);
336
+ return s;
337
+ }
338
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../src/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The callout's `permissionsFor` supplier — the thin `@cotal-ai/auth` adapter that turns a validated
3
+ * user token into a call to core's IdP-agnostic, principal-shaped `permissionsFor` builder. This is the
4
+ * boundary the flip's Q4 review pinned: core asserts only the generic owner+actor grammar; this adapter
5
+ * enforces the token-specific invariants (derived owner, `act.scope` as the single capability authority)
6
+ * and resolves the agent's channel ACL server-side, then hands core a `MintPrincipal`.
7
+ */
8
+ import { type MintOpts } from "@cotal-ai/core";
9
+ import type { ValidatedUserToken } from "./token.js";
10
+ /** The per-agent channel/role ACL a user-mode grant needs — resolved SERVER-SIDE (the spawn ledger /
11
+ * persona registry, keyed by the authenticated principal), because the user token carries the identity
12
+ * and capabilities but NOT the channel read/post ACL. Injected by the composition root that launches the
13
+ * callout (`cotal up`), so this package stays free of any ledger/persona storage concern. */
14
+ export type AclResolver = (t: ValidatedUserToken) => Pick<MintOpts, "allowSubscribe" | "allowPublish" | "role">;
15
+ /**
16
+ * Build the callout's `permissionsFor` hook. Maps `ValidatedUserToken` → `MintPrincipal` → core's
17
+ * `permissionsFor("agent", …)`. `connId` here is the CLIENT-CHOSEN inbox nonce the callout reads from
18
+ * `req.connect_opts.name` (NOT `req.user_nkey`, which the client cannot know pre-connect); it scopes the
19
+ * reply inbox `_INBOX_<connId>.>`.
20
+ *
21
+ * Invariants enforced HERE (not in core):
22
+ * - the owner is a DERIVED owner (`u_…`) — user mode never accepts the reserved dev `local` owner;
23
+ * - `act.scope` is the SINGLE capability authority: a token that ALSO carries a top-level `scope` must
24
+ * have them agree, else it is rejected (the confused-authority guard the Q4 review required closed the
25
+ * moment `permissionsFor(validated)` became production).
26
+ */
27
+ export declare function calloutPermissions(resolveAcl: AclResolver): (t: ValidatedUserToken, connId: string) => Record<string, unknown>;
28
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAmF,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEhI,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;8FAG8F;AAC9F,MAAM,MAAM,WAAW,GAAG,CACxB,CAAC,EAAE,kBAAkB,KAClB,IAAI,CAAC,QAAQ,EAAE,gBAAgB,GAAG,cAAc,GAAG,MAAM,CAAC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,WAAW,GACtB,CAAC,CAAC,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiCpE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The callout's `permissionsFor` supplier — the thin `@cotal-ai/auth` adapter that turns a validated
3
+ * user token into a call to core's IdP-agnostic, principal-shaped `permissionsFor` builder. This is the
4
+ * boundary the flip's Q4 review pinned: core asserts only the generic owner+actor grammar; this adapter
5
+ * enforces the token-specific invariants (derived owner, `act.scope` as the single capability authority)
6
+ * and resolves the agent's channel ACL server-side, then hands core a `MintPrincipal`.
7
+ */
8
+ import { permissionsFor, assertDerivedOwnerToken, CONTROL_PRIVILEGED } from "@cotal-ai/core";
9
+ import { VIEW_REQUIRED_SCOPE } from "./token.js";
10
+ /**
11
+ * Build the callout's `permissionsFor` hook. Maps `ValidatedUserToken` → `MintPrincipal` → core's
12
+ * `permissionsFor("agent", …)`. `connId` here is the CLIENT-CHOSEN inbox nonce the callout reads from
13
+ * `req.connect_opts.name` (NOT `req.user_nkey`, which the client cannot know pre-connect); it scopes the
14
+ * reply inbox `_INBOX_<connId>.>`.
15
+ *
16
+ * Invariants enforced HERE (not in core):
17
+ * - the owner is a DERIVED owner (`u_…`) — user mode never accepts the reserved dev `local` owner;
18
+ * - `act.scope` is the SINGLE capability authority: a token that ALSO carries a top-level `scope` must
19
+ * have them agree, else it is rejected (the confused-authority guard the Q4 review required closed the
20
+ * moment `permissionsFor(validated)` became production).
21
+ */
22
+ export function calloutPermissions(resolveAcl) {
23
+ return (t, connId) => {
24
+ assertDerivedOwnerToken(t.owner); // user-mode owners are derived — never `local`, never an nkey
25
+ const caps = t.act.scope ?? [];
26
+ // Single capability authority: if a top-level `scope` is present it must equal `act.scope` exactly —
27
+ // two independent capability lists is a confused-authority footgun (which one gates the spawn grant?).
28
+ const norm = (xs) => JSON.stringify([...xs].sort());
29
+ if (t.scope.length && norm(t.scope) !== norm(caps))
30
+ throw new Error("callout permissions: top-level scope != act.scope - act.scope is the single capability authority");
31
+ const principal = { owner: t.owner, actor: t.act.actor, connId };
32
+ if (t.act.view !== undefined) {
33
+ // ELEVATED VIEW: the exchange already ledger-authorized it, and `ledgerAuthorizeConnect`
34
+ // fresh-read the row again this connect (act.scope ⊆ current row enforced there) — here is
35
+ // the LAST defense-in-depth re-assert: the bearer's own capability list must carry the
36
+ // view's required scope, or nothing is minted. View names ARE profile names (a closed enum,
37
+ // never a client-chosen profile passthrough); channel ACLs don't apply to these profiles.
38
+ const need = VIEW_REQUIRED_SCOPE[t.act.view];
39
+ if (!caps.includes(need))
40
+ throw new Error(`callout permissions: view "${t.act.view}" without capability "${need}" in act.scope - refusing to mint`);
41
+ return permissionsFor(t.act.view, t.space, principal,
42
+ // The user-mode deployer's control calls ride the PRIVILEGED tier: the manager's
43
+ // owner-equality launch authorization governs, never the admin-tier bypass.
44
+ t.act.view === "deployer" ? { controlTier: CONTROL_PRIVILEGED } : {});
45
+ }
46
+ const acl = resolveAcl(t);
47
+ return permissionsFor("agent", t.space, principal, { ...acl, capabilities: caps });
48
+ };
49
+ }
50
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,kBAAkB,EAAqC,MAAM,gBAAgB,CAAC;AAChI,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAWjD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAuB;IAEvB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACnB,uBAAuB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,8DAA8D;QAChG,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/B,qGAAqG;QACrG,uGAAuG;QACvG,MAAM,IAAI,GAAG,CAAC,EAAY,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;QACJ,MAAM,SAAS,GAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAChF,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,yFAAyF;YACzF,2FAA2F;YAC3F,uFAAuF;YACvF,4FAA4F;YAC5F,0FAA0F;YAC1F,MAAM,IAAI,GAAG,mBAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,GAAG,CAAC,IAAI,yBAAyB,IAAI,mCAAmC,CAAC,CAAC;YAC5H,OAAO,cAAc,CACnB,CAAC,CAAC,GAAG,CAAC,IAAI,EACV,CAAC,CAAC,KAAK,EACP,SAAS;YACT,iFAAiF;YACjF,4EAA4E;YAC5E,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CACrE,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The core `auth-provider` extension — how a composition root gets user-mode auth WITHOUT importing
3
+ * this package from the CLI (`bin/cotal.ts` imports `@cotal-ai/auth`; `@cotal-ai/cli` resolves the
4
+ * provider from the registry, generically).
5
+ *
6
+ * `prepareServer` is the `cotal up --user-auth` hook. It receives the NARROW provisioning input
7
+ * (core's {@link AuthPrepareInput}: operator seed + data-account pub/signingSeed + the space-scoped
8
+ * state dir — never the whole space bundle), makes all persisted material exist, projects the ONE
9
+ * signing seed the daemon may hold into `service-keys.json`, and hands back:
10
+ * - the callout account for the broker config preload,
11
+ * - the non-secret client metadata (trust pins) the workstation registry records ({@link
12
+ * assertUserAuthInfo} shape — typed in workspace, opaque to core),
13
+ * - the service handle: the `auth-service` command name + the readiness contract (poll the
14
+ * discovery file the daemon writes only after BOTH planes are bound, then confirm /health).
15
+ */
16
+ import { type AuthProvider } from "@cotal-ai/core";
17
+ export declare const cotalAuthProvider: AuthProvider;
18
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAsD,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA4BvG,eAAO,MAAM,iBAAiB,EAAE,YA8L/B,CAAC"}