@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,290 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * End-to-end OIDC test driving the full auth-code+PKCE dance against an
8
+ * in-process `node-oidc-provider`. Boots a real IdP on a random port,
9
+ * mounts `OidcAuthStrategy` on a real Express app, and walks login →
10
+ * IdP /auth → interaction (auto-resolved) → callback → session cookie →
11
+ * protected route hit. Catches integration bugs that unit tests can't:
12
+ * cookie wiring, redirect handling, state/PKCE round-trip, callback URL
13
+ * matching.
14
+ */
15
+ import http from 'node:http';
16
+ import { randomBytes } from 'node:crypto';
17
+ import express from 'express';
18
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
19
+ import Provider from 'oidc-provider';
20
+ import { OidcAuthStrategy } from './oidc.js';
21
+ import { authenticate, authRouter, getSession } from '../compose.js';
22
+ const TEST_USER = {
23
+ sub: 'user-42',
24
+ email: 'alice@example.com',
25
+ name: 'Alice Example',
26
+ };
27
+ async function resolveInteraction(provider, req, res) {
28
+ const details = await provider.interactionDetails(req, res);
29
+ if (details.prompt.name === 'login') {
30
+ await provider.interactionFinished(req, res, { login: { accountId: TEST_USER.sub } }, { mergeWithLastSubmission: false });
31
+ return;
32
+ }
33
+ // consent prompt — grant everything requested
34
+ const grant = new provider.Grant({
35
+ accountId: TEST_USER.sub,
36
+ clientId: 'test-client',
37
+ });
38
+ const missingScopes = details.prompt.details['missingOIDCScope'];
39
+ if (Array.isArray(missingScopes)) {
40
+ for (const scope of missingScopes) {
41
+ if (typeof scope === 'string')
42
+ grant.addOIDCScope(scope);
43
+ }
44
+ }
45
+ const grantId = await grant.save();
46
+ await provider.interactionFinished(req, res, { consent: { grantId } }, { mergeWithLastSubmission: true });
47
+ }
48
+ async function reservePort() {
49
+ return new Promise((resolve, reject) => {
50
+ const s = http.createServer();
51
+ s.on('error', reject);
52
+ s.listen(0, '127.0.0.1', () => {
53
+ const addr = s.address();
54
+ s.close(() => resolve(addr.port));
55
+ });
56
+ });
57
+ }
58
+ async function startIdp(issuerUrl, redirectUri) {
59
+ const provider = new Provider(issuerUrl, {
60
+ clients: [
61
+ {
62
+ client_id: 'test-client',
63
+ client_secret: 'test-secret',
64
+ redirect_uris: [redirectUri],
65
+ grant_types: ['authorization_code'],
66
+ response_types: ['code'],
67
+ token_endpoint_auth_method: 'client_secret_basic',
68
+ },
69
+ ],
70
+ pkce: { required: () => true },
71
+ findAccount: (_ctx, sub) => ({
72
+ accountId: sub,
73
+ claims: () => ({
74
+ sub,
75
+ email: TEST_USER.email,
76
+ name: TEST_USER.name,
77
+ }),
78
+ }),
79
+ cookies: {
80
+ keys: ['integration-test-cookie-key-please-rotate'],
81
+ },
82
+ // Most real IdPs (Okta, Auth0, Azure AD with optional claims) put
83
+ // scoped claims directly on the id_token. Match that behaviour so
84
+ // the strategy's `claims['email']` / `claims['name']` reads work.
85
+ conformIdTokenClaims: false,
86
+ claims: {
87
+ openid: ['sub'],
88
+ email: ['email', 'email_verified'],
89
+ profile: ['name', 'family_name', 'given_name', 'preferred_username'],
90
+ },
91
+ features: {
92
+ // The full IdP UI isn't relevant to us — we drive interactions via
93
+ // a custom resolver below.
94
+ devInteractions: { enabled: false },
95
+ },
96
+ // node-oidc-provider redirects to this URL when an interaction
97
+ // (login / consent) is required. The app provides routes there.
98
+ interactions: {
99
+ url: (_ctx, interaction) => `/interaction/${interaction.uid}`,
100
+ },
101
+ });
102
+ const app = express();
103
+ app.set('trust proxy', true);
104
+ // Auto-resolve every interaction as "this user has logged in and consented."
105
+ // Saves us from rendering or POSTing a real form.
106
+ app.get('/interaction/:uid', (req, res, next) => {
107
+ void resolveInteraction(provider, req, res).catch(next);
108
+ });
109
+ const oidcCallback = provider.callback();
110
+ app.use((req, res, next) => {
111
+ void oidcCallback(req, res).catch(next);
112
+ });
113
+ // Surface internal failures so the test can print them.
114
+ app.use((err, _req, res, _next) => {
115
+ const detail = err instanceof Error ? err.stack ?? err.message : String(err);
116
+ process.stderr.write(`[idp error] ${detail}\n`);
117
+ res.status(500).json({ error: detail });
118
+ });
119
+ const idpUrl = new URL(issuerUrl);
120
+ return new Promise((resolve, reject) => {
121
+ const server = http.createServer(app);
122
+ server.on('error', reject);
123
+ server.listen(Number(idpUrl.port), idpUrl.hostname, () => {
124
+ resolve({
125
+ url: issuerUrl,
126
+ close: () => new Promise((closeResolve, closeReject) => {
127
+ server.close((err) => (err ? closeReject(err) : closeResolve()));
128
+ }),
129
+ });
130
+ });
131
+ });
132
+ }
133
+ async function startApp(strategy, port) {
134
+ const app = express();
135
+ app.use(authRouter(strategy));
136
+ app.get('/protected', authenticate(strategy), (_req, res) => {
137
+ const session = getSession(res);
138
+ res.json({ user: session?.user, method: session?.method });
139
+ });
140
+ return new Promise((resolve, reject) => {
141
+ const server = http.createServer(app);
142
+ server.on('error', reject);
143
+ server.listen(port, '127.0.0.1', () => {
144
+ resolve({
145
+ url: `http://127.0.0.1:${port}`,
146
+ close: () => new Promise((closeResolve, closeReject) => {
147
+ server.close((err) => (err ? closeReject(err) : closeResolve()));
148
+ }),
149
+ });
150
+ });
151
+ });
152
+ }
153
+ function makeCookieJar() {
154
+ const store = new Map();
155
+ return {
156
+ store,
157
+ header: () => [...store.entries()].map(([name, value]) => `${name}=${value}`).join('; '),
158
+ ingest(setCookie) {
159
+ if (!setCookie)
160
+ return;
161
+ const list = Array.isArray(setCookie) ? setCookie : [setCookie];
162
+ for (const raw of list) {
163
+ const [pair] = raw.split(';');
164
+ if (!pair)
165
+ continue;
166
+ const eq = pair.indexOf('=');
167
+ if (eq === -1)
168
+ continue;
169
+ const name = pair.slice(0, eq).trim();
170
+ const value = pair.slice(eq + 1).trim();
171
+ if (value === '') {
172
+ store.delete(name);
173
+ }
174
+ else {
175
+ store.set(name, value);
176
+ }
177
+ }
178
+ },
179
+ };
180
+ }
181
+ async function followRedirects(start, jar, maxHops = 12) {
182
+ let current = start;
183
+ const trail = [];
184
+ for (let i = 0; i < maxHops; i++) {
185
+ const res = await fetch(current, {
186
+ redirect: 'manual',
187
+ headers: jar.store.size > 0 ? { cookie: jar.header() } : {},
188
+ });
189
+ const setCookie = res.headers.getSetCookie?.() ?? res.headers.get('set-cookie');
190
+ jar.ingest(setCookie ?? undefined);
191
+ trail.push(`${res.status} ${current}`);
192
+ if (res.status >= 300 && res.status < 400) {
193
+ const location = res.headers.get('location');
194
+ if (!location) {
195
+ return {
196
+ status: res.status,
197
+ url: current,
198
+ headers: headerObject(res.headers),
199
+ body: '',
200
+ trail,
201
+ };
202
+ }
203
+ current = new URL(location, current).toString();
204
+ continue;
205
+ }
206
+ const body = await res.text();
207
+ return { status: res.status, url: current, headers: headerObject(res.headers), body, trail };
208
+ }
209
+ throw new Error(`Too many redirects starting at ${start}\n${trail.join('\n')}`);
210
+ }
211
+ function headerObject(h) {
212
+ const out = {};
213
+ h.forEach((value, key) => {
214
+ out[key] = value;
215
+ });
216
+ return out;
217
+ }
218
+ describe('OidcAuthStrategy — integration', () => {
219
+ let idp;
220
+ let app;
221
+ let strategy;
222
+ const sessionSecret = randomBytes(32).toString('hex');
223
+ beforeAll(async () => {
224
+ // Reserve both ports up front so we can pin issuer + callback URLs
225
+ // into both the IdP config and the strategy config before either
226
+ // starts listening.
227
+ const idpPort = await reservePort();
228
+ const appPort = await reservePort();
229
+ const issuerUrl = `http://127.0.0.1:${idpPort}`;
230
+ const callbackUrl = `http://127.0.0.1:${appPort}/auth/callback`;
231
+ idp = await startIdp(issuerUrl, callbackUrl);
232
+ strategy = new OidcAuthStrategy({
233
+ issuer: issuerUrl,
234
+ clientId: 'test-client',
235
+ clientSecret: 'test-secret',
236
+ callbackUrl,
237
+ sessionSecret,
238
+ allowInsecureRequests: true,
239
+ });
240
+ app = await startApp(strategy, appPort);
241
+ }, 30_000);
242
+ afterAll(async () => {
243
+ if (app)
244
+ await app.close();
245
+ if (idp)
246
+ await idp.close();
247
+ });
248
+ it('rejects an unauthenticated request', async () => {
249
+ const res = await fetch(`${app.url}/protected`);
250
+ expect(res.status).toBe(401);
251
+ });
252
+ it('discovery doc is reachable on the IdP', async () => {
253
+ const res = await fetch(`${idp.url}/.well-known/openid-configuration`);
254
+ expect(res.status).toBe(200);
255
+ const body = (await res.json());
256
+ expect(body.issuer).toBe(idp.url);
257
+ });
258
+ it('walks the full authorization-code + PKCE dance and authenticates', async () => {
259
+ const jar = makeCookieJar();
260
+ // /auth/login → IdP /auth → interaction (auto-resolved twice: login + consent)
261
+ // → IdP /auth/<uid> → our /auth/callback?code=... → 302 to returnTo ('/').
262
+ // The trail terminates on the app's '/' (404 here only because the test
263
+ // app didn't register one — that's fine; what matters is we got back to
264
+ // the app's domain and the session cookie was issued in the callback hop.
265
+ const final = await followRedirects(`${app.url}/auth/login`, jar, 20);
266
+ if (final.status >= 500) {
267
+ process.stderr.write(`[trail]\n${final.trail.join('\n')}\n[final body] ${final.body}\n`);
268
+ throw new Error(`Unexpected 5xx during OIDC dance: ${final.status}`);
269
+ }
270
+ expect(final.url.startsWith(app.url)).toBe(true);
271
+ // session cookie should now be set
272
+ expect(jar.store.has('amodal_session')).toBe(true);
273
+ // protected route should work with the session cookie
274
+ const res = await fetch(`${app.url}/protected`, {
275
+ headers: { cookie: jar.header() },
276
+ });
277
+ expect(res.status).toBe(200);
278
+ const body = (await res.json());
279
+ expect(body.user.id).toBe(TEST_USER.sub);
280
+ expect(body.user.email).toBe(TEST_USER.email);
281
+ expect(body.method).toBe('oidc');
282
+ }, 30_000);
283
+ it('rejects a request with a tampered session cookie', async () => {
284
+ const res = await fetch(`${app.url}/protected`, {
285
+ headers: { cookie: 'amodal_session=garbage.token.here' },
286
+ });
287
+ expect(res.status).toBe(401);
288
+ });
289
+ });
290
+ //# sourceMappingURL=oidc.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.integration.test.js","sourceRoot":"","sources":["../../../../src/auth/strategies/oidc.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * OidcAuthStrategy — full OAuth2.1/OIDC dance via `openid-client`.
8
+ *
9
+ * Handles:
10
+ * - Discovery against the IdP's `.well-known/openid-configuration`
11
+ * - Authorization-code + PKCE login flow at `/auth/login`
12
+ * - Callback at `/auth/callback` — exchanges code for tokens, verifies
13
+ * the id_token, and issues an HMAC-signed session cookie
14
+ * - Per-request verification of the session cookie (via JWKS for the
15
+ * session JWT — actually HMAC since we sign it ourselves)
16
+ * - Optional `authorize(claims)` rule for group/role gating
17
+ *
18
+ * Covers Okta, Azure AD, Google Workspace, Auth0 OIDC, OneLogin,
19
+ * Authentik, Keycloak — anything OIDC-compliant.
20
+ */
21
+ import { Router } from 'express';
22
+ import rateLimit from 'express-rate-limit';
23
+ import * as oidc from 'openid-client';
24
+ import { SignJWT, jwtVerify } from 'jose';
25
+ const STATE_COOKIE = 'amodal_oauth_state';
26
+ const STATE_TTL_SECONDS = 5 * 60;
27
+ export class OidcAuthStrategy {
28
+ name = 'oidc';
29
+ opts;
30
+ sessionKey;
31
+ discoveryPromise = null;
32
+ constructor(options) {
33
+ this.opts = {
34
+ issuer: options.issuer,
35
+ clientId: options.clientId,
36
+ clientSecret: options.clientSecret,
37
+ callbackUrl: options.callbackUrl,
38
+ scopes: options.scopes ?? ['openid', 'email', 'profile'],
39
+ sessionSecret: options.sessionSecret,
40
+ cookieName: options.cookieName ?? 'amodal_session',
41
+ sessionTtlSeconds: options.sessionTtlSeconds ?? 24 * 3600,
42
+ authMethod: options.authMethod ?? 'oidc',
43
+ allowInsecureRequests: options.allowInsecureRequests ?? false,
44
+ };
45
+ if (options.authorize)
46
+ this.opts.authorize = options.authorize;
47
+ this.sessionKey = new TextEncoder().encode(options.sessionSecret);
48
+ }
49
+ valid(req) {
50
+ return readCookie(req, this.opts.cookieName) !== null;
51
+ }
52
+ async authenticate(req) {
53
+ const cookie = readCookie(req, this.opts.cookieName);
54
+ if (!cookie)
55
+ return { kind: 'rejected', reason: 'No session cookie' };
56
+ let payload;
57
+ try {
58
+ const result = await jwtVerify(cookie, this.sessionKey, {
59
+ algorithms: ['HS256'],
60
+ issuer: 'amodal-runtime',
61
+ });
62
+ payload = result.payload;
63
+ }
64
+ catch (err) {
65
+ const detail = err instanceof Error ? err.message : String(err);
66
+ return { kind: 'rejected', reason: `Session JWT invalid: ${detail}` };
67
+ }
68
+ const session = {
69
+ user: {
70
+ id: typeof payload['sub'] === 'string' ? payload['sub'] : '',
71
+ ...(typeof payload['email'] === 'string' ? { email: payload['email'] } : {}),
72
+ ...(typeof payload['name'] === 'string' ? { name: payload['name'] } : {}),
73
+ },
74
+ method: this.opts.authMethod,
75
+ claims: readClaims(payload['claims']),
76
+ };
77
+ return { kind: 'authenticated', session };
78
+ }
79
+ routes() {
80
+ const router = Router();
81
+ // Rate-limit the OAuth-dance endpoints. Both `/auth/login` and
82
+ // `/auth/callback` perform authorization side-effects (issuing
83
+ // signed cookies, exchanging codes with the IdP) and would
84
+ // otherwise be vulnerable to scrape / discovery flooding /
85
+ // credential-stuffing-adjacent abuse. Logout doesn't need to be
86
+ // rate-limited — it only clears a cookie.
87
+ const authLimiter = rateLimit({
88
+ windowMs: 15 * 60 * 1000,
89
+ max: 60,
90
+ standardHeaders: true,
91
+ legacyHeaders: false,
92
+ });
93
+ router.get('/auth/login', authLimiter, (req, res) => {
94
+ void this.handleLogin(req, res);
95
+ });
96
+ router.get('/auth/callback', authLimiter, (req, res) => {
97
+ void this.handleCallback(req, res);
98
+ });
99
+ router.post('/auth/logout', (_req, res) => {
100
+ clearCookie(res, this.opts.cookieName);
101
+ res.json({ ok: true });
102
+ });
103
+ return router;
104
+ }
105
+ // ---- internals ----------------------------------------------------------
106
+ async config() {
107
+ if (!this.discoveryPromise) {
108
+ this.discoveryPromise = oidc.discovery(new URL(this.opts.issuer), this.opts.clientId, this.opts.clientSecret, undefined, this.opts.allowInsecureRequests
109
+ ? { execute: [oidc.allowInsecureRequests] }
110
+ : undefined);
111
+ }
112
+ return this.discoveryPromise;
113
+ }
114
+ async handleLogin(req, res) {
115
+ try {
116
+ const config = await this.config();
117
+ const codeVerifier = oidc.randomPKCECodeVerifier();
118
+ const codeChallenge = await oidc.calculatePKCECodeChallenge(codeVerifier);
119
+ const state = oidc.randomState();
120
+ const returnTo = typeof req.query['return_to'] === 'string' ? req.query['return_to'] : '/';
121
+ // Stash state + codeVerifier + return_to in a short-lived signed cookie
122
+ // so the callback can recover them without server-side state.
123
+ const stateCookie = await new SignJWT({
124
+ state,
125
+ codeVerifier,
126
+ returnTo,
127
+ })
128
+ .setProtectedHeader({ alg: 'HS256' })
129
+ .setIssuer('amodal-runtime')
130
+ .setIssuedAt()
131
+ .setExpirationTime(`${STATE_TTL_SECONDS}s`)
132
+ .sign(this.sessionKey);
133
+ setCookie(res, STATE_COOKIE, stateCookie, STATE_TTL_SECONDS);
134
+ const url = oidc.buildAuthorizationUrl(config, {
135
+ redirect_uri: this.opts.callbackUrl,
136
+ scope: this.opts.scopes.join(' '),
137
+ code_challenge: codeChallenge,
138
+ code_challenge_method: 'S256',
139
+ state,
140
+ });
141
+ res.redirect(url.href);
142
+ }
143
+ catch (err) {
144
+ const message = err instanceof Error ? err.message : String(err);
145
+ res.status(500).json({ error: { code: 'OIDC_LOGIN_FAILED', message } });
146
+ }
147
+ }
148
+ async handleCallback(req, res) {
149
+ try {
150
+ const stateCookie = readCookie(req, STATE_COOKIE);
151
+ if (!stateCookie) {
152
+ res.status(400).json({ error: { code: 'NO_STATE', message: 'OAuth state cookie missing' } });
153
+ return;
154
+ }
155
+ let stateClaims;
156
+ try {
157
+ const verified = await jwtVerify(stateCookie, this.sessionKey, {
158
+ algorithms: ['HS256'],
159
+ issuer: 'amodal-runtime',
160
+ });
161
+ stateClaims = verified.payload;
162
+ }
163
+ catch (err) {
164
+ const detail = err instanceof Error ? err.message : String(err);
165
+ res.status(400).json({ error: { code: 'BAD_STATE', message: `OAuth state invalid: ${detail}` } });
166
+ return;
167
+ }
168
+ clearCookie(res, STATE_COOKIE);
169
+ const expectedState = typeof stateClaims['state'] === 'string' ? stateClaims['state'] : '';
170
+ const codeVerifier = typeof stateClaims['codeVerifier'] === 'string' ? stateClaims['codeVerifier'] : '';
171
+ const returnTo = typeof stateClaims['returnTo'] === 'string' ? stateClaims['returnTo'] : '/';
172
+ if (!expectedState || !codeVerifier) {
173
+ res.status(400).json({ error: { code: 'BAD_STATE', message: 'State payload malformed' } });
174
+ return;
175
+ }
176
+ const config = await this.config();
177
+ const fullUrl = new URL(req.originalUrl, `${req.protocol}://${req.get('host') ?? 'localhost'}`);
178
+ const tokens = await oidc.authorizationCodeGrant(config, fullUrl, {
179
+ pkceCodeVerifier: codeVerifier,
180
+ expectedState,
181
+ });
182
+ const claims = tokens.claims();
183
+ if (!claims) {
184
+ res.status(401).json({ error: { code: 'NO_CLAIMS', message: 'id_token missing claims' } });
185
+ return;
186
+ }
187
+ const user = await this.resolveUser(claims);
188
+ if (!user) {
189
+ res.status(403).json({ error: { code: 'FORBIDDEN', message: 'User not authorized for this agent' } });
190
+ return;
191
+ }
192
+ const sessionJwt = await new SignJWT({
193
+ sub: user.id,
194
+ ...(user.email ? { email: user.email } : {}),
195
+ ...(user.name ? { name: user.name } : {}),
196
+ claims: { ...claims },
197
+ })
198
+ .setProtectedHeader({ alg: 'HS256' })
199
+ .setIssuer('amodal-runtime')
200
+ .setSubject(user.id)
201
+ .setIssuedAt()
202
+ .setExpirationTime(`${this.opts.sessionTtlSeconds}s`)
203
+ .sign(this.sessionKey);
204
+ setCookie(res, this.opts.cookieName, sessionJwt, this.opts.sessionTtlSeconds);
205
+ res.redirect(returnTo);
206
+ }
207
+ catch (err) {
208
+ const message = err instanceof Error ? err.message : String(err);
209
+ res.status(500).json({ error: { code: 'OIDC_CALLBACK_FAILED', message } });
210
+ }
211
+ }
212
+ async resolveUser(claims) {
213
+ if (this.opts.authorize) {
214
+ const result = await this.opts.authorize(claims);
215
+ return result;
216
+ }
217
+ const id = typeof claims['sub'] === 'string' ? claims['sub'] : null;
218
+ if (!id)
219
+ return null;
220
+ const out = { id };
221
+ if (typeof claims['email'] === 'string')
222
+ out.email = claims['email'];
223
+ if (typeof claims['name'] === 'string')
224
+ out.name = claims['name'];
225
+ return out;
226
+ }
227
+ }
228
+ /**
229
+ * Defensive narrowing for the `claims` blob we round-trip through the
230
+ * session JWT. Anything other than a plain object yields an empty
231
+ * record; avoids a blanket `as Record<string, unknown>` cast on data
232
+ * that started life as JSON.
233
+ */
234
+ function readClaims(raw) {
235
+ if (raw == null || typeof raw !== 'object' || Array.isArray(raw))
236
+ return {};
237
+ const out = {};
238
+ for (const [k, v] of Object.entries(raw))
239
+ out[k] = v;
240
+ return out;
241
+ }
242
+ function readCookie(req, name) {
243
+ const header = req.headers['cookie'];
244
+ if (typeof header !== 'string' || header.length === 0)
245
+ return null;
246
+ for (const pair of header.split(';')) {
247
+ const trimmed = pair.trim();
248
+ const eq = trimmed.indexOf('=');
249
+ if (eq === -1)
250
+ continue;
251
+ if (trimmed.slice(0, eq) !== name)
252
+ continue;
253
+ const value = trimmed.slice(eq + 1);
254
+ return value.length > 0 ? decodeURIComponent(value) : null;
255
+ }
256
+ return null;
257
+ }
258
+ function setCookie(res, name, value, ttlSeconds) {
259
+ const parts = [
260
+ `${name}=${encodeURIComponent(value)}`,
261
+ 'Path=/',
262
+ `Max-Age=${ttlSeconds}`,
263
+ 'HttpOnly',
264
+ 'Secure',
265
+ 'SameSite=Lax',
266
+ ];
267
+ appendSetCookie(res, parts.join('; '));
268
+ }
269
+ function clearCookie(res, name) {
270
+ appendSetCookie(res, `${name}=; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=Lax`);
271
+ }
272
+ function appendSetCookie(res, value) {
273
+ const existing = res.getHeader('Set-Cookie');
274
+ if (existing == null) {
275
+ res.setHeader('Set-Cookie', value);
276
+ return;
277
+ }
278
+ if (Array.isArray(existing)) {
279
+ res.setHeader('Set-Cookie', [...existing, value]);
280
+ return;
281
+ }
282
+ res.setHeader('Set-Cookie', [String(existing), value]);
283
+ }
284
+ //# sourceMappingURL=oidc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../../../src/auth/strategies/oidc.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 {};