@amodalai/runtime 0.3.70 → 0.3.72

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 (96) hide show
  1. package/dist/src/agent/local-server.js +7 -0
  2. package/dist/src/agent/local-server.js.map +1 -1
  3. package/dist/src/agent/local-server.test.js +96 -1
  4. package/dist/src/agent/local-server.test.js.map +1 -1
  5. package/dist/src/agent/routes/logs.d.ts +12 -0
  6. package/dist/src/agent/routes/logs.js +46 -0
  7. package/dist/src/agent/routes/logs.js.map +1 -0
  8. package/dist/src/agent/snapshot-server.js +4 -0
  9. package/dist/src/agent/snapshot-server.js.map +1 -1
  10. package/dist/src/auth/__fixtures__/custom-strategy.d.ts +8 -0
  11. package/dist/src/auth/__fixtures__/custom-strategy.js +27 -0
  12. package/dist/src/auth/__fixtures__/custom-strategy.js.map +1 -0
  13. package/dist/src/auth/compose.d.ts +55 -0
  14. package/dist/src/auth/compose.js +142 -0
  15. package/dist/src/auth/compose.js.map +1 -0
  16. package/dist/src/auth/compose.test.d.ts +6 -0
  17. package/dist/src/auth/compose.test.js +159 -0
  18. package/dist/src/auth/compose.test.js.map +1 -0
  19. package/dist/src/auth/config.d.ts +261 -0
  20. package/dist/src/auth/config.js +107 -0
  21. package/dist/src/auth/config.js.map +1 -0
  22. package/dist/src/auth/config.test.d.ts +6 -0
  23. package/dist/src/auth/config.test.js +85 -0
  24. package/dist/src/auth/config.test.js.map +1 -0
  25. package/dist/src/auth/factory.d.ts +19 -0
  26. package/dist/src/auth/factory.js +57 -0
  27. package/dist/src/auth/factory.js.map +1 -0
  28. package/dist/src/auth/factory.test.d.ts +6 -0
  29. package/dist/src/auth/factory.test.js +60 -0
  30. package/dist/src/auth/factory.test.js.map +1 -0
  31. package/dist/src/auth/index.d.ts +48 -0
  32. package/dist/src/auth/index.js +19 -0
  33. package/dist/src/auth/index.js.map +1 -0
  34. package/dist/src/auth/strategies/amodal.d.ts +36 -0
  35. package/dist/src/auth/strategies/amodal.js +28 -0
  36. package/dist/src/auth/strategies/amodal.js.map +1 -0
  37. package/dist/src/auth/strategies/api-key.d.ts +41 -0
  38. package/dist/src/auth/strategies/api-key.js +63 -0
  39. package/dist/src/auth/strategies/api-key.js.map +1 -0
  40. package/dist/src/auth/strategies/auth-modes.integration.test.d.ts +6 -0
  41. package/dist/src/auth/strategies/auth-modes.integration.test.js +363 -0
  42. package/dist/src/auth/strategies/auth-modes.integration.test.js.map +1 -0
  43. package/dist/src/auth/strategies/cookie.d.ts +41 -0
  44. package/dist/src/auth/strategies/cookie.js +84 -0
  45. package/dist/src/auth/strategies/cookie.js.map +1 -0
  46. package/dist/src/auth/strategies/custom-loader.d.ts +17 -0
  47. package/dist/src/auth/strategies/custom-loader.js +37 -0
  48. package/dist/src/auth/strategies/custom-loader.js.map +1 -0
  49. package/dist/src/auth/strategies/header.d.ts +35 -0
  50. package/dist/src/auth/strategies/header.js +42 -0
  51. package/dist/src/auth/strategies/header.js.map +1 -0
  52. package/dist/src/auth/strategies/jwks.d.ts +38 -0
  53. package/dist/src/auth/strategies/jwks.js +86 -0
  54. package/dist/src/auth/strategies/jwks.js.map +1 -0
  55. package/dist/src/auth/strategies/jwt-secret.d.ts +25 -0
  56. package/dist/src/auth/strategies/jwt-secret.js +82 -0
  57. package/dist/src/auth/strategies/jwt-secret.js.map +1 -0
  58. package/dist/src/auth/strategies/jwt-secret.test.d.ts +6 -0
  59. package/dist/src/auth/strategies/jwt-secret.test.js +75 -0
  60. package/dist/src/auth/strategies/jwt-secret.test.js.map +1 -0
  61. package/dist/src/auth/strategies/none.d.ts +37 -0
  62. package/dist/src/auth/strategies/none.js +31 -0
  63. package/dist/src/auth/strategies/none.js.map +1 -0
  64. package/dist/src/auth/strategies/oidc.d.ts +48 -0
  65. package/dist/src/auth/strategies/oidc.integration.test.d.ts +6 -0
  66. package/dist/src/auth/strategies/oidc.integration.test.js +290 -0
  67. package/dist/src/auth/strategies/oidc.integration.test.js.map +1 -0
  68. package/dist/src/auth/strategies/oidc.js +284 -0
  69. package/dist/src/auth/strategies/oidc.js.map +1 -0
  70. package/dist/src/auth/strategies/oidc.test.d.ts +6 -0
  71. package/dist/src/auth/strategies/oidc.test.js +111 -0
  72. package/dist/src/auth/strategies/oidc.test.js.map +1 -0
  73. package/dist/src/auth/strategies/strategies.test.d.ts +6 -0
  74. package/dist/src/auth/strategies/strategies.test.js +190 -0
  75. package/dist/src/auth/strategies/strategies.test.js.map +1 -0
  76. package/dist/src/auth/types.d.ts +95 -0
  77. package/dist/src/auth/types.js +7 -0
  78. package/dist/src/auth/types.js.map +1 -0
  79. package/dist/src/index.d.ts +4 -2
  80. package/dist/src/index.js +3 -1
  81. package/dist/src/index.js.map +1 -1
  82. package/dist/src/logger.d.ts +4 -3
  83. package/dist/src/logger.js +2 -1
  84. package/dist/src/logger.js.map +1 -1
  85. package/dist/src/logger.test.js +22 -1
  86. package/dist/src/logger.test.js.map +1 -1
  87. package/dist/src/runtime-log-store.d.ts +37 -0
  88. package/dist/src/runtime-log-store.js +132 -0
  89. package/dist/src/runtime-log-store.js.map +1 -0
  90. package/dist/src/server.js +7 -0
  91. package/dist/src/server.js.map +1 -1
  92. package/dist/src/session/manager.d.ts +1 -0
  93. package/dist/src/session/manager.js +18 -7
  94. package/dist/src/session/manager.js.map +1 -1
  95. package/dist/tsconfig.tsbuildinfo +1 -1
  96. package/package.json +9 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-strategy.js","sourceRoot":"","sources":["../../../../src/auth/__fixtures__/custom-strategy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,cAAc,GAAG,cAAc,CAAC;AAEtC,MAAM,QAAQ,GAAiB;IAC7B,IAAI,EAAE,aAAa;IACnB,KAAK,CAAC,GAAY;QAChB,OAAO,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,QAAQ,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,YAAY,CAAC,GAAY;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC1D,OAAO,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,EAAC,CAAC;QACxD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE;gBACP,IAAI,EAAE,EAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAC;gBAC9C,MAAM,EAAE,aAAa;aACtB;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Composable auth middleware + router factories.
8
+ *
9
+ * `authenticate(...strategies)` returns an Express middleware that runs
10
+ * the strategies in order, picks the first one whose `valid(req)` is
11
+ * true, awaits its `authenticate(req)`, and attaches the resulting
12
+ * session to `res.locals.session` (or 401s the request). Single-winner.
13
+ *
14
+ * `authRouter(...strategies)` returns a router carrying any strategy-
15
+ * supplied routes (OAuth callbacks, login redirects, etc.). Mount it
16
+ * once at the application root.
17
+ *
18
+ * Per-route mounting solves the Studio / end-user route bifurcation:
19
+ * different routes pass different strategy lists, so a request to
20
+ * `/api/files` can never be authenticated by an end-user-only strategy
21
+ * (it isn't in the list).
22
+ */
23
+ import { Router } from 'express';
24
+ import type { RequestHandler } from 'express';
25
+ import type { AuthStrategy, AuthSession } from './types.js';
26
+ /** Hook fired once per request after the chain runs. Useful for metrics. */
27
+ export interface AuthLogEvent {
28
+ /** Strategy that resolved the request, or `null` if no strategy matched. */
29
+ strategy: string | null;
30
+ outcome: 'authenticated' | 'rejected' | 'no-match';
31
+ reason?: string;
32
+ durationMs: number;
33
+ }
34
+ export interface AuthenticateOptions {
35
+ logger?: (event: AuthLogEvent) => void;
36
+ }
37
+ /**
38
+ * Build a middleware that authenticates requests against the given
39
+ * strategy chain. First strategy to match (`valid` && `authenticate →
40
+ * authenticated`) wins. Reject 401s the request. No matching strategy
41
+ * also 401s.
42
+ */
43
+ export declare function authenticate(...args: Array<AuthStrategy | AuthenticateOptions>): RequestHandler;
44
+ /**
45
+ * Build a router carrying any strategy-supplied routes. Strategies that
46
+ * own a login flow (OAuth callbacks, magic link, etc.) implement the
47
+ * optional `routes()` method; this factory mounts each one onto a single
48
+ * router for app-level wiring.
49
+ */
50
+ export declare function authRouter(...strategies: readonly AuthStrategy[]): Router;
51
+ /**
52
+ * Read the session attached by `authenticate(...)`. Returns `undefined`
53
+ * if no session is set (e.g. on routes that don't have auth middleware).
54
+ */
55
+ export declare function getSession(res: import('express').Response): AuthSession | undefined;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Composable auth middleware + router factories.
8
+ *
9
+ * `authenticate(...strategies)` returns an Express middleware that runs
10
+ * the strategies in order, picks the first one whose `valid(req)` is
11
+ * true, awaits its `authenticate(req)`, and attaches the resulting
12
+ * session to `res.locals.session` (or 401s the request). Single-winner.
13
+ *
14
+ * `authRouter(...strategies)` returns a router carrying any strategy-
15
+ * supplied routes (OAuth callbacks, login redirects, etc.). Mount it
16
+ * once at the application root.
17
+ *
18
+ * Per-route mounting solves the Studio / end-user route bifurcation:
19
+ * different routes pass different strategy lists, so a request to
20
+ * `/api/files` can never be authenticated by an end-user-only strategy
21
+ * (it isn't in the list).
22
+ */
23
+ import { Router } from 'express';
24
+ const SESSION_KEY = 'session';
25
+ /**
26
+ * Build a middleware that authenticates requests against the given
27
+ * strategy chain. First strategy to match (`valid` && `authenticate →
28
+ * authenticated`) wins. Reject 401s the request. No matching strategy
29
+ * also 401s.
30
+ */
31
+ export function authenticate(...args) {
32
+ const strategies = [];
33
+ let options = {};
34
+ for (const arg of args) {
35
+ if (isStrategy(arg)) {
36
+ strategies.push(arg);
37
+ }
38
+ else {
39
+ options = arg;
40
+ }
41
+ }
42
+ return (req, res, next) => {
43
+ const startedAt = Date.now();
44
+ void runChain(strategies, req)
45
+ .then((result) => {
46
+ const durationMs = Date.now() - startedAt;
47
+ if (result.kind === 'authenticated') {
48
+ res.locals[SESSION_KEY] = result.session;
49
+ options.logger?.({
50
+ strategy: result.session.method,
51
+ outcome: 'authenticated',
52
+ durationMs,
53
+ });
54
+ next();
55
+ return;
56
+ }
57
+ if (result.kind === 'rejected') {
58
+ options.logger?.({
59
+ strategy: result.strategy,
60
+ outcome: 'rejected',
61
+ reason: result.reason,
62
+ durationMs,
63
+ });
64
+ res.status(401).json({
65
+ error: { code: 'UNAUTHORIZED', message: result.reason },
66
+ });
67
+ return;
68
+ }
69
+ options.logger?.({
70
+ strategy: null,
71
+ outcome: 'no-match',
72
+ durationMs,
73
+ });
74
+ res.status(401).json({
75
+ error: { code: 'UNAUTHORIZED', message: 'Missing authentication' },
76
+ });
77
+ })
78
+ .catch((err) => {
79
+ // `runChain` itself never throws — this catch is defensive against
80
+ // strategy implementations that violate the contract by throwing.
81
+ const durationMs = Date.now() - startedAt;
82
+ const message = err instanceof Error ? err.message : 'Unauthorized';
83
+ options.logger?.({
84
+ strategy: null,
85
+ outcome: 'rejected',
86
+ reason: message,
87
+ durationMs,
88
+ });
89
+ res.status(401).json({
90
+ error: { code: 'UNAUTHORIZED', message },
91
+ });
92
+ });
93
+ };
94
+ }
95
+ async function runChain(strategies, req) {
96
+ for (const strategy of strategies) {
97
+ if (!strategy.valid(req))
98
+ continue;
99
+ const result = await strategy.authenticate(req);
100
+ if (result.kind === 'authenticated') {
101
+ return { kind: 'authenticated', session: result.session };
102
+ }
103
+ if (result.kind === 'rejected') {
104
+ return { kind: 'rejected', strategy: strategy.name, reason: result.reason };
105
+ }
106
+ }
107
+ return { kind: 'no-match' };
108
+ }
109
+ function isStrategy(value) {
110
+ if (typeof value !== 'object' || value === null)
111
+ return false;
112
+ if (!('name' in value) || !('valid' in value) || !('authenticate' in value)) {
113
+ return false;
114
+ }
115
+ const candidate = value;
116
+ return (typeof candidate['valid'] === 'function' &&
117
+ typeof candidate['authenticate'] === 'function');
118
+ }
119
+ /**
120
+ * Build a router carrying any strategy-supplied routes. Strategies that
121
+ * own a login flow (OAuth callbacks, magic link, etc.) implement the
122
+ * optional `routes()` method; this factory mounts each one onto a single
123
+ * router for app-level wiring.
124
+ */
125
+ export function authRouter(...strategies) {
126
+ const router = Router();
127
+ for (const strategy of strategies) {
128
+ if (strategy.routes) {
129
+ router.use(strategy.routes());
130
+ }
131
+ }
132
+ return router;
133
+ }
134
+ /**
135
+ * Read the session attached by `authenticate(...)`. Returns `undefined`
136
+ * if no session is set (e.g. on routes that don't have auth middleware).
137
+ */
138
+ export function getSession(res) {
139
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
140
+ return res.locals[SESSION_KEY];
141
+ }
142
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/auth/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ export {};
@@ -0,0 +1,159 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { describe, it, expect, vi } from 'vitest';
7
+ import express from 'express';
8
+ import supertest from 'supertest';
9
+ import { authenticate, authRouter, getSession } from './compose.js';
10
+ function strategy(name, apply, fn) {
11
+ return { name, valid: apply, authenticate: fn };
12
+ }
13
+ function buildApp(...mw) {
14
+ const app = express();
15
+ for (const m of mw)
16
+ app.use(m);
17
+ app.get('/protected', (_req, res) => {
18
+ res.json({ session: res.locals['session'] ?? null });
19
+ });
20
+ return app;
21
+ }
22
+ describe('authenticate', () => {
23
+ it('returns 401 when no strategy is valid', async () => {
24
+ const app = buildApp(authenticate(strategy('a', () => false, () => Promise.reject(new Error('unreachable'))), strategy('b', () => false, () => Promise.reject(new Error('unreachable')))));
25
+ const res = await supertest(app).get('/protected');
26
+ expect(res.status).toBe(401);
27
+ expect(res.body).toEqual({
28
+ error: { code: 'UNAUTHORIZED', message: 'Missing authentication' },
29
+ });
30
+ });
31
+ it('first strategy whose valid returns true and authenticates wins', async () => {
32
+ const wins = vi.fn(() => Promise.resolve({
33
+ kind: 'authenticated',
34
+ session: { user: { id: 'u1' }, method: 'first' },
35
+ }));
36
+ const skipped = vi.fn();
37
+ const app = buildApp(authenticate(strategy('first', () => true, wins), strategy('second', () => true, skipped)));
38
+ const res = await supertest(app).get('/protected');
39
+ expect(res.status).toBe(200);
40
+ expect(res.body.session.method).toBe('first');
41
+ expect(wins).toHaveBeenCalledTimes(1);
42
+ expect(skipped).not.toHaveBeenCalled();
43
+ });
44
+ it('skips strategies whose valid returns false (cheap fall-through)', async () => {
45
+ const skipped = vi.fn();
46
+ const fallback = vi.fn(() => Promise.resolve({
47
+ kind: 'authenticated',
48
+ session: { user: { id: 'u1' }, method: 'fallback' },
49
+ }));
50
+ const app = buildApp(authenticate(strategy('skip', () => false, skipped), strategy('fallback', () => true, fallback)));
51
+ const res = await supertest(app).get('/protected');
52
+ expect(res.status).toBe(200);
53
+ expect(res.body.session.method).toBe('fallback');
54
+ expect(skipped).not.toHaveBeenCalled();
55
+ });
56
+ it("rejected result short-circuits with 401 (chain doesn't fall through)", async () => {
57
+ const fallback = vi.fn();
58
+ const app = buildApp(authenticate(strategy('rejector', () => true, () => Promise.resolve({ kind: 'rejected', reason: 'bad token' })), strategy('fallback', () => true, fallback)));
59
+ const res = await supertest(app).get('/protected');
60
+ expect(res.status).toBe(401);
61
+ expect(res.body).toEqual({
62
+ error: { code: 'UNAUTHORIZED', message: 'bad token' },
63
+ });
64
+ expect(fallback).not.toHaveBeenCalled();
65
+ });
66
+ it('a strategy that throws is treated as a rejection (defensive)', async () => {
67
+ const app = buildApp(authenticate(strategy('thrower', () => true, () => Promise.reject(new Error('boom')))));
68
+ const res = await supertest(app).get('/protected');
69
+ expect(res.status).toBe(401);
70
+ expect(res.body.error.message).toBe('boom');
71
+ });
72
+ it('logger receives a structured event per request', async () => {
73
+ const events = [];
74
+ const app = buildApp(authenticate(strategy('s1', () => true, () => Promise.resolve({
75
+ kind: 'authenticated',
76
+ session: { user: { id: 'u1' }, method: 's1' },
77
+ })), { logger: (e) => events.push(e) }));
78
+ await supertest(app).get('/protected');
79
+ expect(events).toHaveLength(1);
80
+ const ev = events[0];
81
+ expect(ev.strategy).toBe('s1');
82
+ expect(ev.outcome).toBe('authenticated');
83
+ expect(typeof ev.durationMs).toBe('number');
84
+ });
85
+ it('logger sees no-match when nothing applies', async () => {
86
+ const events = [];
87
+ const app = buildApp(authenticate(strategy('s1', () => false, () => Promise.reject(new Error('unreachable'))), { logger: (e) => events.push(e) }));
88
+ await supertest(app).get('/protected');
89
+ expect(events).toHaveLength(1);
90
+ const ev = events[0];
91
+ expect(ev.strategy).toBeNull();
92
+ expect(ev.outcome).toBe('no-match');
93
+ });
94
+ it('different routes can use different strategy chains (Studio vs end-user)', async () => {
95
+ const platformJwt = strategy('platform-jwt', (req) => req.headers['authorization'] === 'Bearer platform', () => Promise.resolve({
96
+ kind: 'authenticated',
97
+ session: { user: { id: 'admin' }, method: 'platform-jwt' },
98
+ }));
99
+ const endUser = strategy('end-user', (req) => req.headers['authorization'] === 'Bearer end-user', () => Promise.resolve({
100
+ kind: 'authenticated',
101
+ session: { user: { id: 'enduser' }, method: 'end-user' },
102
+ }));
103
+ const app = express();
104
+ app.use('/api/files', authenticate(platformJwt));
105
+ app.use('/chat', authenticate(platformJwt, endUser));
106
+ app.get('/api/files', (_req, res) => res.json({ m: res.locals['session'].method }));
107
+ app.get('/chat', (_req, res) => res.json({ m: res.locals['session'].method }));
108
+ // Studio route accepts platform JWT
109
+ let res = await supertest(app).get('/api/files').set('Authorization', 'Bearer platform');
110
+ expect(res.status).toBe(200);
111
+ expect(res.body.m).toBe('platform-jwt');
112
+ // Studio route REJECTS end-user token (strategy not in list)
113
+ res = await supertest(app).get('/api/files').set('Authorization', 'Bearer end-user');
114
+ expect(res.status).toBe(401);
115
+ // Chat route accepts end-user token
116
+ res = await supertest(app).get('/chat').set('Authorization', 'Bearer end-user');
117
+ expect(res.status).toBe(200);
118
+ expect(res.body.m).toBe('end-user');
119
+ // Chat route also accepts platform JWT (Studio dev testing)
120
+ res = await supertest(app).get('/chat').set('Authorization', 'Bearer platform');
121
+ expect(res.status).toBe(200);
122
+ expect(res.body.m).toBe('platform-jwt');
123
+ });
124
+ });
125
+ describe('authRouter', () => {
126
+ it('mounts strategy-supplied login routes', async () => {
127
+ const oauthMock = {
128
+ name: 'oauth-mock',
129
+ valid: () => false,
130
+ authenticate: () => Promise.resolve({ kind: 'rejected', reason: 'never' }),
131
+ routes: () => {
132
+ const r = express.Router();
133
+ r.get('/auth/oauth-mock/start', (_req, res) => res.json({ redirected: true }));
134
+ return r;
135
+ },
136
+ };
137
+ const app = express();
138
+ app.use(authRouter(oauthMock));
139
+ const res = await supertest(app).get('/auth/oauth-mock/start');
140
+ expect(res.status).toBe(200);
141
+ expect(res.body).toEqual({ redirected: true });
142
+ });
143
+ });
144
+ describe('getSession', () => {
145
+ it('returns the session attached by authenticate', async () => {
146
+ const app = express();
147
+ app.use(authenticate(strategy('s1', () => true, () => Promise.resolve({
148
+ kind: 'authenticated',
149
+ session: { user: { id: 'u1' }, method: 's1' },
150
+ }))));
151
+ app.get('/x', (_req, res) => {
152
+ const s = getSession(res);
153
+ res.json({ id: s?.user.id });
154
+ });
155
+ const r = await supertest(app).get('/x');
156
+ expect(r.body).toEqual({ id: 'u1' });
157
+ });
158
+ });
159
+ //# sourceMappingURL=compose.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.test.js","sourceRoot":"","sources":["../../../src/auth/compose.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,261 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Config-driven auth — discriminated union the agent owner sets in
8
+ * `amodal.json` (or whatever surfaces the runtime through). The host
9
+ * resolves any `env:` references against its secrets store before
10
+ * passing the config to `createAuthStrategy(config)`.
11
+ *
12
+ * {
13
+ * "auth": {
14
+ * "type": "oidc",
15
+ * "issuer": "https://acme.okta.com",
16
+ * ...
17
+ * }
18
+ * }
19
+ */
20
+ import { z } from 'zod';
21
+ declare const NoneAuthConfigSchema: z.ZodObject<{
22
+ type: z.ZodLiteral<"none">;
23
+ /** Synthesized user id for the anonymous session. Defaults to `'anonymous'`. */
24
+ userId: z.ZodOptional<z.ZodString>;
25
+ }, "strip", z.ZodTypeAny, {
26
+ type: "none";
27
+ userId?: string | undefined;
28
+ }, {
29
+ type: "none";
30
+ userId?: string | undefined;
31
+ }>;
32
+ declare const JwtSecretAuthConfigSchema: z.ZodObject<{
33
+ type: z.ZodLiteral<"jwt_secret">;
34
+ /** Shared HMAC secret used to verify incoming JWTs. */
35
+ secret: z.ZodString;
36
+ /** HMAC algorithm. Defaults to HS256. */
37
+ algorithm: z.ZodOptional<z.ZodEnum<["HS256", "HS384", "HS512"]>>;
38
+ /** Expected `iss` claim. Optional. */
39
+ issuer: z.ZodOptional<z.ZodString>;
40
+ /** Expected `aud` claim. Optional. */
41
+ audience: z.ZodOptional<z.ZodString>;
42
+ }, "strip", z.ZodTypeAny, {
43
+ type: "jwt_secret";
44
+ secret: string;
45
+ issuer?: string | undefined;
46
+ audience?: string | undefined;
47
+ algorithm?: "HS256" | "HS384" | "HS512" | undefined;
48
+ }, {
49
+ type: "jwt_secret";
50
+ secret: string;
51
+ issuer?: string | undefined;
52
+ audience?: string | undefined;
53
+ algorithm?: "HS256" | "HS384" | "HS512" | undefined;
54
+ }>;
55
+ declare const OidcAuthConfigSchema: z.ZodObject<{
56
+ type: z.ZodLiteral<"oidc">;
57
+ /** OIDC issuer URL — the IdP's discovery base (`https://acme.okta.com`). */
58
+ issuer: z.ZodString;
59
+ clientId: z.ZodString;
60
+ clientSecret: z.ZodString;
61
+ /** Where the IdP redirects back after login. Must be registered with the IdP. */
62
+ callbackUrl: z.ZodString;
63
+ /** OAuth scopes to request. Defaults to `['openid', 'email', 'profile']`. */
64
+ scopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
65
+ /**
66
+ * HMAC secret used to sign the session JWT stored in the runtime's cookie.
67
+ * Distinct from the IdP — this is purely the runtime's session signing key.
68
+ */
69
+ sessionSecret: z.ZodString;
70
+ /** Cookie name for the session JWT. Defaults to `'amodal_session'`. */
71
+ cookieName: z.ZodOptional<z.ZodString>;
72
+ /** Session TTL in seconds. Defaults to 24h. */
73
+ sessionTtlSeconds: z.ZodOptional<z.ZodNumber>;
74
+ }, "strip", z.ZodTypeAny, {
75
+ type: "oidc";
76
+ issuer: string;
77
+ clientId: string;
78
+ clientSecret: string;
79
+ callbackUrl: string;
80
+ sessionSecret: string;
81
+ scopes?: string[] | undefined;
82
+ cookieName?: string | undefined;
83
+ sessionTtlSeconds?: number | undefined;
84
+ }, {
85
+ type: "oidc";
86
+ issuer: string;
87
+ clientId: string;
88
+ clientSecret: string;
89
+ callbackUrl: string;
90
+ sessionSecret: string;
91
+ scopes?: string[] | undefined;
92
+ cookieName?: string | undefined;
93
+ sessionTtlSeconds?: number | undefined;
94
+ }>;
95
+ declare const HeaderAuthConfigSchema: z.ZodObject<{
96
+ type: z.ZodLiteral<"header">;
97
+ headerName: z.ZodOptional<z.ZodString>;
98
+ }, "strip", z.ZodTypeAny, {
99
+ type: "header";
100
+ headerName?: string | undefined;
101
+ }, {
102
+ type: "header";
103
+ headerName?: string | undefined;
104
+ }>;
105
+ declare const CustomAuthConfigSchema: z.ZodObject<{
106
+ type: z.ZodLiteral<"custom">;
107
+ /**
108
+ * Module path (relative to the agent's runtime directory) whose default
109
+ * export implements the `AuthStrategy` interface. The runtime imports
110
+ * this file at startup and validates the shape.
111
+ */
112
+ module: z.ZodString;
113
+ }, "strip", z.ZodTypeAny, {
114
+ type: "custom";
115
+ module: string;
116
+ }, {
117
+ type: "custom";
118
+ module: string;
119
+ }>;
120
+ /**
121
+ * "Sign in with Amodal" — verifies platform JWTs minted by the Amodal
122
+ * cloud (or any self-hosted Amodal-equivalent platform). Agent owners on
123
+ * the Amodal cloud need zero config; the cloud's deploy pipeline injects
124
+ * the JWKS URL via the `AMODAL_JWKS_URL` env var. Self-hosters override
125
+ * via the optional `jwksUrl` field.
126
+ */
127
+ declare const AmodalAuthConfigSchema: z.ZodObject<{
128
+ type: z.ZodLiteral<"amodal">;
129
+ jwksUrl: z.ZodOptional<z.ZodString>;
130
+ issuer: z.ZodOptional<z.ZodString>;
131
+ }, "strip", z.ZodTypeAny, {
132
+ type: "amodal";
133
+ issuer?: string | undefined;
134
+ jwksUrl?: string | undefined;
135
+ }, {
136
+ type: "amodal";
137
+ issuer?: string | undefined;
138
+ jwksUrl?: string | undefined;
139
+ }>;
140
+ export declare const AuthConfigSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
141
+ type: z.ZodLiteral<"none">;
142
+ /** Synthesized user id for the anonymous session. Defaults to `'anonymous'`. */
143
+ userId: z.ZodOptional<z.ZodString>;
144
+ }, "strip", z.ZodTypeAny, {
145
+ type: "none";
146
+ userId?: string | undefined;
147
+ }, {
148
+ type: "none";
149
+ userId?: string | undefined;
150
+ }>, z.ZodObject<{
151
+ type: z.ZodLiteral<"jwt_secret">;
152
+ /** Shared HMAC secret used to verify incoming JWTs. */
153
+ secret: z.ZodString;
154
+ /** HMAC algorithm. Defaults to HS256. */
155
+ algorithm: z.ZodOptional<z.ZodEnum<["HS256", "HS384", "HS512"]>>;
156
+ /** Expected `iss` claim. Optional. */
157
+ issuer: z.ZodOptional<z.ZodString>;
158
+ /** Expected `aud` claim. Optional. */
159
+ audience: z.ZodOptional<z.ZodString>;
160
+ }, "strip", z.ZodTypeAny, {
161
+ type: "jwt_secret";
162
+ secret: string;
163
+ issuer?: string | undefined;
164
+ audience?: string | undefined;
165
+ algorithm?: "HS256" | "HS384" | "HS512" | undefined;
166
+ }, {
167
+ type: "jwt_secret";
168
+ secret: string;
169
+ issuer?: string | undefined;
170
+ audience?: string | undefined;
171
+ algorithm?: "HS256" | "HS384" | "HS512" | undefined;
172
+ }>, z.ZodObject<{
173
+ type: z.ZodLiteral<"oidc">;
174
+ /** OIDC issuer URL — the IdP's discovery base (`https://acme.okta.com`). */
175
+ issuer: z.ZodString;
176
+ clientId: z.ZodString;
177
+ clientSecret: z.ZodString;
178
+ /** Where the IdP redirects back after login. Must be registered with the IdP. */
179
+ callbackUrl: z.ZodString;
180
+ /** OAuth scopes to request. Defaults to `['openid', 'email', 'profile']`. */
181
+ scopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
182
+ /**
183
+ * HMAC secret used to sign the session JWT stored in the runtime's cookie.
184
+ * Distinct from the IdP — this is purely the runtime's session signing key.
185
+ */
186
+ sessionSecret: z.ZodString;
187
+ /** Cookie name for the session JWT. Defaults to `'amodal_session'`. */
188
+ cookieName: z.ZodOptional<z.ZodString>;
189
+ /** Session TTL in seconds. Defaults to 24h. */
190
+ sessionTtlSeconds: z.ZodOptional<z.ZodNumber>;
191
+ }, "strip", z.ZodTypeAny, {
192
+ type: "oidc";
193
+ issuer: string;
194
+ clientId: string;
195
+ clientSecret: string;
196
+ callbackUrl: string;
197
+ sessionSecret: string;
198
+ scopes?: string[] | undefined;
199
+ cookieName?: string | undefined;
200
+ sessionTtlSeconds?: number | undefined;
201
+ }, {
202
+ type: "oidc";
203
+ issuer: string;
204
+ clientId: string;
205
+ clientSecret: string;
206
+ callbackUrl: string;
207
+ sessionSecret: string;
208
+ scopes?: string[] | undefined;
209
+ cookieName?: string | undefined;
210
+ sessionTtlSeconds?: number | undefined;
211
+ }>, z.ZodObject<{
212
+ type: z.ZodLiteral<"header">;
213
+ headerName: z.ZodOptional<z.ZodString>;
214
+ }, "strip", z.ZodTypeAny, {
215
+ type: "header";
216
+ headerName?: string | undefined;
217
+ }, {
218
+ type: "header";
219
+ headerName?: string | undefined;
220
+ }>, z.ZodObject<{
221
+ type: z.ZodLiteral<"custom">;
222
+ /**
223
+ * Module path (relative to the agent's runtime directory) whose default
224
+ * export implements the `AuthStrategy` interface. The runtime imports
225
+ * this file at startup and validates the shape.
226
+ */
227
+ module: z.ZodString;
228
+ }, "strip", z.ZodTypeAny, {
229
+ type: "custom";
230
+ module: string;
231
+ }, {
232
+ type: "custom";
233
+ module: string;
234
+ }>, z.ZodObject<{
235
+ type: z.ZodLiteral<"amodal">;
236
+ jwksUrl: z.ZodOptional<z.ZodString>;
237
+ issuer: z.ZodOptional<z.ZodString>;
238
+ }, "strip", z.ZodTypeAny, {
239
+ type: "amodal";
240
+ issuer?: string | undefined;
241
+ jwksUrl?: string | undefined;
242
+ }, {
243
+ type: "amodal";
244
+ issuer?: string | undefined;
245
+ jwksUrl?: string | undefined;
246
+ }>]>;
247
+ export type AuthConfig = z.infer<typeof AuthConfigSchema>;
248
+ export type NoneAuthConfig = z.infer<typeof NoneAuthConfigSchema>;
249
+ export type JwtSecretAuthConfig = z.infer<typeof JwtSecretAuthConfigSchema>;
250
+ export type OidcAuthConfig = z.infer<typeof OidcAuthConfigSchema>;
251
+ export type HeaderAuthConfig = z.infer<typeof HeaderAuthConfigSchema>;
252
+ export type CustomAuthConfig = z.infer<typeof CustomAuthConfigSchema>;
253
+ export type AmodalAuthConfig = z.infer<typeof AmodalAuthConfigSchema>;
254
+ /**
255
+ * Parse + validate an `auth` block from agent config. Throws a clear
256
+ * error on any malformed shape so misconfiguration fails at startup
257
+ * rather than at first request. Pass `undefined` / missing config to
258
+ * default to `{type: 'none'}`.
259
+ */
260
+ export declare function parseAuthConfig(raw: unknown): AuthConfig;
261
+ export {};