@theokit/sdk 1.5.0 → 1.6.1

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.
@@ -0,0 +1,176 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+
3
+ /**
4
+ * @theokit/sdk/server/auth — typed error classes
5
+ *
6
+ * Plan T1.2 + v1.1 EC-1 (AuthCancelledError for OAuth provider error response RFC 6749 §4.1.2.1).
7
+ */
8
+ /**
9
+ * Thrown at `defineAuth()` time when configuration is invalid
10
+ * (e.g., duplicate provider name, invalid email shape per EC-V1-12).
11
+ */
12
+ declare class AuthConfigError extends Error {
13
+ readonly name = "AuthConfigError";
14
+ readonly code: string;
15
+ constructor(code: string, message: string);
16
+ }
17
+ /**
18
+ * Thrown at `startSignIn(providerName, ...)` or `finishSignIn(providerName, ...)`
19
+ * when the named provider is not registered in `providers[]`.
20
+ */
21
+ declare class AuthProviderNotFoundError extends Error {
22
+ readonly name = "AuthProviderNotFoundError";
23
+ readonly providerName: string;
24
+ constructor(providerName: string);
25
+ }
26
+ /**
27
+ * Thrown during OAuth callback handling for state mismatches, expired
28
+ * transactions, missing query params, or provider 4xx/5xx errors.
29
+ *
30
+ * Typed `code` field lets consumers branch on cause:
31
+ * - 'oauth_transaction_expired' — cookie tx > 10min old (per ADR D5)
32
+ * - 'oauth_state_mismatch' — query state ≠ cookie state (CSRF defense per RFC 6749 §10.12)
33
+ * - 'oauth_provider_error' — non-access_denied error in callback URL
34
+ * - 'oauth_token_exchange_failed' — provider rejected code-for-tokens swap
35
+ * - 'oauth_userinfo_failed' — userinfo endpoint returned error
36
+ * - 'oauth_missing_code_or_state' — required query params absent
37
+ */
38
+ declare class AuthCallbackError extends Error {
39
+ readonly name: string;
40
+ readonly code: string;
41
+ constructor(code: string, message?: string);
42
+ }
43
+ /**
44
+ * Per v1.1 EC-1 MUST FIX — typed subclass of AuthCallbackError for the
45
+ * specific case where user declined consent at provider screen.
46
+ *
47
+ * OAuth 2.0 RFC 6749 §4.1.2.1: provider redirects with `?error=access_denied`.
48
+ * Apps can catch this distinctly from network/server errors to render
49
+ * "Login cancelled — try again" UX instead of opaque "callback failed".
50
+ */
51
+ declare class AuthCancelledError extends AuthCallbackError {
52
+ readonly name: string;
53
+ readonly errorDescription?: string;
54
+ constructor(errorDescription?: string);
55
+ }
56
+
57
+ /**
58
+ * @theokit/sdk/server/auth — orchestrator types (Caminho C Hybrid per G11)
59
+ *
60
+ * Plan: g11-auth-architecture-implementation v1.4 (sha256 4d381020...)
61
+ * Blueprint: g11-auth-architecture-decision v1.1 (SHIPPABLE 97.9)
62
+ * AUTH-DELEGATION lock (theokit/CLAUDE.md:217-225) — these types are the
63
+ * orchestrator contract; concrete OAuth/email providers ship in opt-in
64
+ * @theokit/auth-* packages (adapters layer per ADR D11).
65
+ */
66
+
67
+ /**
68
+ * SessionManager contract (matches theokit/packages/theo/src/server/auth/session.ts:49).
69
+ * Imported as type-only — runtime depends via peerDep `theokit@>=0.2.4`.
70
+ */
71
+ interface SessionManager<TSession> {
72
+ getSession(req: IncomingMessage): Promise<TSession | null>;
73
+ createSession(res: ServerResponse, data: TSession): Promise<void>;
74
+ destroySession(res: ServerResponse): void;
75
+ rotateSession(req: IncomingMessage, res: ServerResponse): Promise<TSession | null>;
76
+ }
77
+ /**
78
+ * Per ADR D5 — OAuth transaction state stored in encrypted HttpOnly cookie
79
+ * (cookie-state pattern). Expires within 10 minutes per invariant.
80
+ */
81
+ interface OAuthTransaction {
82
+ state: string;
83
+ pkceVerifier?: string;
84
+ returnTo?: string;
85
+ createdAt: number;
86
+ expiresAt: number;
87
+ }
88
+ /**
89
+ * Per ADR D9 — provider profile types are provider-specific (not unified).
90
+ * Each @theokit/auth-* package exports its own profile shape.
91
+ * Generic param TProfile lets consumers narrow via discriminated unions on providerName.
92
+ */
93
+ interface AuthResult<TProfile, TName extends string = string> {
94
+ profile: TProfile;
95
+ providerName: TName;
96
+ rawTokens?: {
97
+ accessToken: string;
98
+ refreshToken?: string;
99
+ idToken?: string;
100
+ expiresAt?: number;
101
+ };
102
+ }
103
+ /**
104
+ * Provider contract — each @theokit/auth-* package implements this.
105
+ * Per blueprint Q5 + ADR D11 (adapters layer).
106
+ */
107
+ interface AuthProvider<TProfile, TName extends string = string> {
108
+ name: TName;
109
+ createAuthorizationURL(tx: OAuthTransaction): URL | Promise<URL>;
110
+ handleCallback(req: IncomingMessage, tx: OAuthTransaction): Promise<AuthResult<TProfile, TName>>;
111
+ }
112
+ /**
113
+ * `defineAuth(opts)` configuration shape — Caminho C (Hybrid).
114
+ * `providers` optional: empty = Caminho A escape hatch (manual signIn only).
115
+ * `onSignIn` invoked after provider callback success; returns TSession to persist.
116
+ */
117
+ interface DefineAuthOptions<TSession> {
118
+ session: SessionManager<TSession>;
119
+ providers?: AuthProvider<unknown, string>[];
120
+ onSignIn?: <TProfile>(args: {
121
+ profile: TProfile;
122
+ provider: string;
123
+ }) => Promise<TSession>;
124
+ onSignOut?: (session: TSession | null) => Promise<void> | void;
125
+ }
126
+ /**
127
+ * Returned by `defineAuth<TSession>(opts)` — 5-method orchestrator surface.
128
+ *
129
+ * - startSignIn: returns Response.redirect to provider authorization URL with state cookie
130
+ * - finishSignIn: handles provider callback; verifies state; calls onSignIn; rotates session ID
131
+ * (OWASP A07:2021 per EC-10); creates session cookie; clears transaction cookie
132
+ * - signIn: Caminho A escape hatch — skip OAuth flow; directly persist session from external profile
133
+ * - signOut: destroys session cookie + invokes onSignOut callback
134
+ * - getSession: read-only passthrough to session.getSession
135
+ */
136
+ interface AuthOrchestrator<TSession> {
137
+ startSignIn(providerName: string, req: IncomingMessage, opts?: {
138
+ returnTo?: string;
139
+ }): Promise<Response>;
140
+ finishSignIn(providerName: string, req: IncomingMessage, res: ServerResponse): Promise<{
141
+ session: TSession;
142
+ returnTo?: string;
143
+ }>;
144
+ signIn<TProfile>(profile: TProfile, providerName: string, req: IncomingMessage, res: ServerResponse): Promise<TSession>;
145
+ signOut(res: ServerResponse): void | Promise<void>;
146
+ getSession(req: IncomingMessage): Promise<TSession | null>;
147
+ }
148
+
149
+ /**
150
+ * @theokit/sdk/server/auth — defineAuth orchestrator runtime (Caminho C Hybrid)
151
+ *
152
+ * Plan T1.2 implementation per blueprint Q5 § Caminho C signatures.
153
+ * Composes existing primitives + the v1.1 EC-1/EC-2/EC-10 fixes.
154
+ */
155
+
156
+ declare function defineAuth<TSession>(opts: DefineAuthOptions<TSession>): AuthOrchestrator<TSession>;
157
+
158
+ /**
159
+ * @theokit/sdk/server/auth — same-origin returnTo validator
160
+ *
161
+ * Per v1.1 EC-2 MUST FIX — OWASP A01:2021 open-redirect mitigation.
162
+ *
163
+ * Without this check, attacker craft `/login?returnTo=https://evil.com` would
164
+ * cause post-login redirect to attacker domain with authenticated session cookie.
165
+ *
166
+ * Rules:
167
+ * - undefined/empty returnTo → default '/'
168
+ * - protocol-relative `//evil.com` → default '/' (URL parser would resolve to baseUrl protocol)
169
+ * - absolute URL with origin ≠ baseUrl.origin → default '/' (cross-origin redirect)
170
+ * - absolute URL with origin === baseUrl.origin → keep (same-origin allowed)
171
+ * - relative path starting with '/' → keep (same-app navigation)
172
+ * - relative path not starting with '/' → default '/' (defensive)
173
+ */
174
+ declare function validateReturnTo(returnTo: string | undefined, baseUrl: URL): string;
175
+
176
+ export { AuthCallbackError, AuthCancelledError, AuthConfigError, type AuthOrchestrator, type AuthProvider, AuthProviderNotFoundError, type AuthResult, type DefineAuthOptions, type OAuthTransaction, type SessionManager, defineAuth, validateReturnTo };
@@ -0,0 +1,322 @@
1
+ import { Buffer as Buffer$1 } from 'buffer';
2
+ import { webcrypto } from 'crypto';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+
14
+ // src/server/auth/oauth-transaction-store.ts
15
+ var oauth_transaction_store_exports = {};
16
+ __export(oauth_transaction_store_exports, {
17
+ clearCookie: () => clearCookie,
18
+ clearTransaction: () => clearTransaction,
19
+ decodeTransaction: () => decodeTransaction,
20
+ encodeTransaction: () => encodeTransaction,
21
+ getCookie: () => getCookie,
22
+ getTransaction: () => getTransaction,
23
+ newTransaction: () => newTransaction,
24
+ setCookie: () => setCookie,
25
+ setTransaction: () => setTransaction
26
+ });
27
+ async function deriveKey(secret) {
28
+ const keyMaterial = await webcrypto.subtle.importKey(
29
+ "raw",
30
+ Buffer$1.from(secret).slice(0, 32).length === 32 ? Buffer$1.from(secret).slice(0, 32) : Buffer$1.concat([Buffer$1.from(secret), Buffer$1.alloc(32)]).slice(0, 32),
31
+ { name: "AES-GCM" },
32
+ false,
33
+ ["encrypt", "decrypt"]
34
+ );
35
+ return keyMaterial;
36
+ }
37
+ async function encodeTransaction(tx, secret) {
38
+ const key = await deriveKey(secret);
39
+ const iv = webcrypto.getRandomValues(new Uint8Array(12));
40
+ const plaintext = new TextEncoder().encode(JSON.stringify(tx));
41
+ const ciphertext = await webcrypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext);
42
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength);
43
+ combined.set(iv, 0);
44
+ combined.set(new Uint8Array(ciphertext), iv.length);
45
+ return Buffer$1.from(combined).toString("base64url");
46
+ }
47
+ async function decodeTransaction(encoded, secret) {
48
+ try {
49
+ const key = await deriveKey(secret);
50
+ const combined = Buffer$1.from(encoded, "base64url");
51
+ const iv = combined.slice(0, 12);
52
+ const ciphertext = combined.slice(12);
53
+ const plaintext = await webcrypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
54
+ const tx = JSON.parse(new TextDecoder().decode(plaintext));
55
+ return tx;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+ function getCookie(req, name) {
61
+ const header = req.headers.cookie;
62
+ if (!header) return null;
63
+ for (const part of header.split(";")) {
64
+ const [k, ...v] = part.trim().split("=");
65
+ if (k === name) return v.join("=");
66
+ }
67
+ return null;
68
+ }
69
+ function setCookie(res, name, value) {
70
+ const existing = res.getHeader("Set-Cookie");
71
+ const cookie = `${name}=${value}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=${TX_LIFETIME_MS / 1e3}`;
72
+ if (Array.isArray(existing)) {
73
+ res.setHeader("Set-Cookie", [...existing, cookie]);
74
+ } else if (typeof existing === "string") {
75
+ res.setHeader("Set-Cookie", [existing, cookie]);
76
+ } else {
77
+ res.setHeader("Set-Cookie", cookie);
78
+ }
79
+ }
80
+ function clearCookie(res, name) {
81
+ setCookie(
82
+ res,
83
+ name,
84
+ `; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`.split(";").slice(0, 1).join("")
85
+ );
86
+ const clear = `${name}=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`;
87
+ const existing = res.getHeader("Set-Cookie");
88
+ if (Array.isArray(existing)) {
89
+ res.setHeader("Set-Cookie", [...existing.filter((c) => !c.includes(`${name}=`)), clear]);
90
+ } else {
91
+ res.setHeader("Set-Cookie", clear);
92
+ }
93
+ }
94
+ async function setTransaction(res, tx, secret) {
95
+ const encoded = await encodeTransaction(tx, secret);
96
+ setCookie(res, COOKIE_NAME, encoded);
97
+ }
98
+ async function getTransaction(req, secret) {
99
+ const raw = getCookie(req, COOKIE_NAME);
100
+ if (!raw) return null;
101
+ const tx = await decodeTransaction(raw, secret);
102
+ if (!tx) return null;
103
+ if (tx.expiresAt < Date.now()) return null;
104
+ return tx;
105
+ }
106
+ function clearTransaction(res) {
107
+ clearCookie(res, COOKIE_NAME);
108
+ }
109
+ function newTransaction(opts) {
110
+ const now = Date.now();
111
+ return {
112
+ state: opts.state,
113
+ pkceVerifier: opts.pkceVerifier,
114
+ returnTo: opts.returnTo,
115
+ createdAt: now,
116
+ expiresAt: now + TX_LIFETIME_MS
117
+ };
118
+ }
119
+ var COOKIE_NAME, TX_LIFETIME_MS;
120
+ var init_oauth_transaction_store = __esm({
121
+ "src/server/auth/oauth-transaction-store.ts"() {
122
+ COOKIE_NAME = "theo_oauth_tx";
123
+ TX_LIFETIME_MS = 10 * 60 * 1e3;
124
+ }
125
+ });
126
+
127
+ // src/server/auth/errors.ts
128
+ var AuthConfigError = class extends Error {
129
+ name = "AuthConfigError";
130
+ code;
131
+ constructor(code, message) {
132
+ super(`[${code}] ${message}`);
133
+ this.code = code;
134
+ }
135
+ };
136
+ var AuthProviderNotFoundError = class extends Error {
137
+ name = "AuthProviderNotFoundError";
138
+ providerName;
139
+ constructor(providerName) {
140
+ super(
141
+ `Auth provider not found: '${providerName}'. Register it in defineAuth({ providers: [...] }).`
142
+ );
143
+ this.providerName = providerName;
144
+ }
145
+ };
146
+ var AuthCallbackError = class extends Error {
147
+ name = "AuthCallbackError";
148
+ code;
149
+ constructor(code, message) {
150
+ super(message ?? `OAuth callback error: ${code}`);
151
+ this.code = code;
152
+ }
153
+ };
154
+ var AuthCancelledError = class extends AuthCallbackError {
155
+ name = "AuthCancelledError";
156
+ errorDescription;
157
+ constructor(errorDescription) {
158
+ super("user_declined_consent", errorDescription ?? "User declined consent at provider");
159
+ this.errorDescription = errorDescription;
160
+ }
161
+ };
162
+ init_oauth_transaction_store();
163
+
164
+ // src/server/auth/validate-return-to.ts
165
+ function validateReturnTo(returnTo, baseUrl) {
166
+ if (!returnTo || typeof returnTo !== "string" || returnTo.trim() === "") {
167
+ return "/";
168
+ }
169
+ const trimmed = returnTo.trim();
170
+ if (trimmed.startsWith("//")) {
171
+ return "/";
172
+ }
173
+ if (URL.canParse(trimmed)) {
174
+ const parsed = new URL(trimmed);
175
+ if (parsed.origin === baseUrl.origin) {
176
+ return parsed.pathname + parsed.search + parsed.hash;
177
+ }
178
+ return "/";
179
+ }
180
+ if (trimmed.startsWith("/")) {
181
+ return trimmed;
182
+ }
183
+ return "/";
184
+ }
185
+
186
+ // src/server/auth/orchestrator.ts
187
+ var PROVIDER_NAME_RE = /^[a-z0-9-]{1,32}$/;
188
+ function randomState() {
189
+ const bytes = webcrypto.getRandomValues(new Uint8Array(24));
190
+ return Buffer.from(bytes).toString("base64url");
191
+ }
192
+ function txCookieSecret(opts) {
193
+ const sess = opts.session;
194
+ if (sess.secret) {
195
+ if (Array.isArray(sess.secret)) {
196
+ const first = sess.secret[0];
197
+ if (first) return first;
198
+ } else {
199
+ return sess.secret;
200
+ }
201
+ }
202
+ return process.env.THEOKIT_OAUTH_TX_SECRET ?? "DEV_ONLY_INSECURE_OAUTH_TX_SECRET_REPLACE_IN_PROD";
203
+ }
204
+ function defineAuth(opts) {
205
+ if (!opts.session) {
206
+ throw new AuthConfigError("missing_session", "defineAuth({ session }) is required");
207
+ }
208
+ const providersMap = /* @__PURE__ */ new Map();
209
+ for (const provider of opts.providers ?? []) {
210
+ if (!provider.name || !PROVIDER_NAME_RE.test(provider.name)) {
211
+ throw new AuthConfigError(
212
+ "invalid_provider_name",
213
+ `Provider name must match ${PROVIDER_NAME_RE} (got: '${provider.name}')`
214
+ );
215
+ }
216
+ if (providersMap.has(provider.name)) {
217
+ throw new AuthConfigError(
218
+ "duplicate_provider_name",
219
+ `Duplicate provider name: '${provider.name}'`
220
+ );
221
+ }
222
+ providersMap.set(provider.name, provider);
223
+ }
224
+ const txSecret = txCookieSecret(opts);
225
+ function requireProvider(name) {
226
+ const p = providersMap.get(name);
227
+ if (!p) throw new AuthProviderNotFoundError(name);
228
+ return p;
229
+ }
230
+ async function startSignIn(providerName, req, startOpts) {
231
+ const provider = requireProvider(providerName);
232
+ const baseUrl = new URL(`http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`);
233
+ const safeReturnTo = validateReturnTo(startOpts?.returnTo, baseUrl);
234
+ const state = randomState();
235
+ const tx = newTransaction({ state, returnTo: safeReturnTo === "/" ? void 0 : safeReturnTo });
236
+ const authUrl = await provider.createAuthorizationURL(tx);
237
+ const headers = new Headers();
238
+ headers.set("Location", authUrl.toString());
239
+ const { encodeTransaction: encodeTransaction2 } = await Promise.resolve().then(() => (init_oauth_transaction_store(), oauth_transaction_store_exports));
240
+ const encodedTx = await encodeTransaction2(tx, txSecret);
241
+ headers.set(
242
+ "Set-Cookie",
243
+ `theo_oauth_tx=${encodedTx}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=600`
244
+ );
245
+ return new Response(null, { status: 302, headers });
246
+ }
247
+ async function finishSignIn(providerName, req, res) {
248
+ const provider = requireProvider(providerName);
249
+ const url = new URL(`http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`);
250
+ const errorParam = url.searchParams.get("error");
251
+ if (errorParam) {
252
+ const errorDescription = url.searchParams.get("error_description") ?? void 0;
253
+ if (errorParam === "access_denied") {
254
+ throw new AuthCancelledError(errorDescription);
255
+ }
256
+ throw new AuthCallbackError(
257
+ "oauth_provider_error",
258
+ `Provider returned error: ${errorParam}${errorDescription ? ` (${errorDescription})` : ""}`
259
+ );
260
+ }
261
+ const tx = await getTransaction(req, txSecret);
262
+ if (!tx) {
263
+ throw new AuthCallbackError(
264
+ "oauth_transaction_expired",
265
+ "OAuth transaction cookie missing or expired (>10min). Please retry sign-in."
266
+ );
267
+ }
268
+ const queryState = url.searchParams.get("state");
269
+ if (!queryState || queryState !== tx.state) {
270
+ throw new AuthCallbackError(
271
+ "oauth_state_mismatch",
272
+ "OAuth state mismatch. Possible CSRF attempt or stale callback."
273
+ );
274
+ }
275
+ const result = await provider.handleCallback(req, tx);
276
+ let sessionData;
277
+ if (opts.onSignIn) {
278
+ sessionData = await opts.onSignIn({ profile: result.profile, provider: result.providerName });
279
+ } else {
280
+ sessionData = result.profile;
281
+ }
282
+ try {
283
+ await opts.session.rotateSession(req, res);
284
+ } catch {
285
+ }
286
+ await opts.session.createSession(res, sessionData);
287
+ clearTransaction(res);
288
+ return { session: sessionData, returnTo: tx.returnTo };
289
+ }
290
+ async function signIn(profile, providerName, req, res) {
291
+ let sessionData;
292
+ if (opts.onSignIn) {
293
+ sessionData = await opts.onSignIn({ profile, provider: providerName });
294
+ } else {
295
+ sessionData = profile;
296
+ }
297
+ try {
298
+ await opts.session.rotateSession(req, res);
299
+ } catch {
300
+ }
301
+ await opts.session.createSession(res, sessionData);
302
+ return sessionData;
303
+ }
304
+ async function signOut(res) {
305
+ let sessionData = null;
306
+ if (opts.onSignOut) {
307
+ sessionData = null;
308
+ }
309
+ opts.session.destroySession(res);
310
+ if (opts.onSignOut) {
311
+ await opts.onSignOut(sessionData);
312
+ }
313
+ }
314
+ async function getSession(req) {
315
+ return opts.session.getSession(req);
316
+ }
317
+ return { startSignIn, finishSignIn, signIn, signOut, getSession };
318
+ }
319
+
320
+ export { AuthCallbackError, AuthCancelledError, AuthConfigError, AuthProviderNotFoundError, defineAuth, validateReturnTo };
321
+ //# sourceMappingURL=index.js.map
322
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/auth/oauth-transaction-store.ts","../../../src/server/auth/errors.ts","../../../src/server/auth/orchestrator.ts","../../../src/server/auth/validate-return-to.ts"],"names":["Buffer","webcrypto","encodeTransaction"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @theokit/sdk/server/auth — encrypted OAuth transaction cookie store\n *\n * Per ADR D5 — cookie-state pattern (no Redis/db dependency in core).\n *\n * Stores OAuthTransaction (state + pkceVerifier + returnTo + expiry) in a\n * single signed+encrypted HttpOnly cookie. Stateless, works in edge/serverless.\n *\n * Cookie name: `theo_oauth_tx`\n * Lifetime: 10 minutes (per D5 invariant)\n * Encryption: AES-256-GCM via Node's webcrypto subtle API\n *\n * Note: this is a minimal in-package implementation. Production deployments\n * may prefer using `theokit/server/auth/crypto`'s encrypt/decrypt helpers\n * via the SessionManager's existing secret rotation chain. For T1.2 we keep\n * it self-contained to avoid cross-package peer-dep complexity; T2+ may\n * refactor to share SessionManager's encrypt path.\n */\n\nimport { Buffer } from \"node:buffer\";\nimport { webcrypto } from \"node:crypto\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { OAuthTransaction } from \"./types.js\";\n\nconst COOKIE_NAME = \"theo_oauth_tx\";\nconst TX_LIFETIME_MS = 10 * 60 * 1000; // 10 minutes per D5\n\nasync function deriveKey(secret: string): Promise<CryptoKey> {\n const keyMaterial = await webcrypto.subtle.importKey(\n \"raw\",\n Buffer.from(secret).slice(0, 32).length === 32\n ? Buffer.from(secret).slice(0, 32)\n : Buffer.concat([Buffer.from(secret), Buffer.alloc(32)]).slice(0, 32),\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n return keyMaterial;\n}\n\nexport async function encodeTransaction(tx: OAuthTransaction, secret: string): Promise<string> {\n const key = await deriveKey(secret);\n const iv = webcrypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(tx));\n const ciphertext = await webcrypto.subtle.encrypt({ name: \"AES-GCM\", iv }, key, plaintext);\n const combined = new Uint8Array(iv.length + ciphertext.byteLength);\n combined.set(iv, 0);\n combined.set(new Uint8Array(ciphertext), iv.length);\n return Buffer.from(combined).toString(\"base64url\");\n}\n\nexport async function decodeTransaction(\n encoded: string,\n secret: string,\n): Promise<OAuthTransaction | null> {\n try {\n const key = await deriveKey(secret);\n const combined = Buffer.from(encoded, \"base64url\");\n const iv = combined.slice(0, 12);\n const ciphertext = combined.slice(12);\n const plaintext = await webcrypto.subtle.decrypt({ name: \"AES-GCM\", iv }, key, ciphertext);\n const tx = JSON.parse(new TextDecoder().decode(plaintext)) as OAuthTransaction;\n return tx;\n } catch {\n return null;\n }\n}\n\nexport function getCookie(req: IncomingMessage, name: string): string | null {\n const header = req.headers.cookie;\n if (!header) return null;\n for (const part of header.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return v.join(\"=\");\n }\n return null;\n}\n\nexport function setCookie(res: ServerResponse, name: string, value: string): void {\n const existing = res.getHeader(\"Set-Cookie\");\n const cookie = `${name}=${value}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=${TX_LIFETIME_MS / 1000}`;\n if (Array.isArray(existing)) {\n res.setHeader(\"Set-Cookie\", [...existing, cookie]);\n } else if (typeof existing === \"string\") {\n res.setHeader(\"Set-Cookie\", [existing, cookie]);\n } else {\n res.setHeader(\"Set-Cookie\", cookie);\n }\n}\n\nexport function clearCookie(res: ServerResponse, name: string): void {\n setCookie(\n res,\n name,\n `; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`.split(\";\").slice(0, 1).join(\"\"),\n );\n // Explicit clear with Max-Age=0\n const clear = `${name}=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`;\n const existing = res.getHeader(\"Set-Cookie\");\n if (Array.isArray(existing)) {\n res.setHeader(\"Set-Cookie\", [...existing.filter((c) => !c.includes(`${name}=`)), clear]);\n } else {\n res.setHeader(\"Set-Cookie\", clear);\n }\n}\n\nexport async function setTransaction(\n res: ServerResponse,\n tx: OAuthTransaction,\n secret: string,\n): Promise<void> {\n const encoded = await encodeTransaction(tx, secret);\n setCookie(res, COOKIE_NAME, encoded);\n}\n\nexport async function getTransaction(\n req: IncomingMessage,\n secret: string,\n): Promise<OAuthTransaction | null> {\n const raw = getCookie(req, COOKIE_NAME);\n if (!raw) return null;\n const tx = await decodeTransaction(raw, secret);\n if (!tx) return null;\n // Check expiry\n if (tx.expiresAt < Date.now()) return null;\n return tx;\n}\n\nexport function clearTransaction(res: ServerResponse): void {\n clearCookie(res, COOKIE_NAME);\n}\n\nexport function newTransaction(opts: {\n state: string;\n pkceVerifier?: string;\n returnTo?: string;\n}): OAuthTransaction {\n const now = Date.now();\n return {\n state: opts.state,\n pkceVerifier: opts.pkceVerifier,\n returnTo: opts.returnTo,\n createdAt: now,\n expiresAt: now + TX_LIFETIME_MS,\n };\n}\n","/**\n * @theokit/sdk/server/auth — typed error classes\n *\n * Plan T1.2 + v1.1 EC-1 (AuthCancelledError for OAuth provider error response RFC 6749 §4.1.2.1).\n */\n\n/**\n * Thrown at `defineAuth()` time when configuration is invalid\n * (e.g., duplicate provider name, invalid email shape per EC-V1-12).\n */\nexport class AuthConfigError extends Error {\n override readonly name = \"AuthConfigError\";\n readonly code: string;\n\n constructor(code: string, message: string) {\n super(`[${code}] ${message}`);\n this.code = code;\n }\n}\n\n/**\n * Thrown at `startSignIn(providerName, ...)` or `finishSignIn(providerName, ...)`\n * when the named provider is not registered in `providers[]`.\n */\nexport class AuthProviderNotFoundError extends Error {\n override readonly name = \"AuthProviderNotFoundError\";\n readonly providerName: string;\n\n constructor(providerName: string) {\n super(\n `Auth provider not found: '${providerName}'. Register it in defineAuth({ providers: [...] }).`,\n );\n this.providerName = providerName;\n }\n}\n\n/**\n * Thrown during OAuth callback handling for state mismatches, expired\n * transactions, missing query params, or provider 4xx/5xx errors.\n *\n * Typed `code` field lets consumers branch on cause:\n * - 'oauth_transaction_expired' — cookie tx > 10min old (per ADR D5)\n * - 'oauth_state_mismatch' — query state ≠ cookie state (CSRF defense per RFC 6749 §10.12)\n * - 'oauth_provider_error' — non-access_denied error in callback URL\n * - 'oauth_token_exchange_failed' — provider rejected code-for-tokens swap\n * - 'oauth_userinfo_failed' — userinfo endpoint returned error\n * - 'oauth_missing_code_or_state' — required query params absent\n */\nexport class AuthCallbackError extends Error {\n override readonly name: string = \"AuthCallbackError\";\n readonly code: string;\n\n constructor(code: string, message?: string) {\n super(message ?? `OAuth callback error: ${code}`);\n this.code = code;\n }\n}\n\n/**\n * Per v1.1 EC-1 MUST FIX — typed subclass of AuthCallbackError for the\n * specific case where user declined consent at provider screen.\n *\n * OAuth 2.0 RFC 6749 §4.1.2.1: provider redirects with `?error=access_denied`.\n * Apps can catch this distinctly from network/server errors to render\n * \"Login cancelled — try again\" UX instead of opaque \"callback failed\".\n */\nexport class AuthCancelledError extends AuthCallbackError {\n override readonly name: string = \"AuthCancelledError\";\n readonly errorDescription?: string;\n\n constructor(errorDescription?: string) {\n super(\"user_declined_consent\", errorDescription ?? \"User declined consent at provider\");\n this.errorDescription = errorDescription;\n }\n}\n","/**\n * @theokit/sdk/server/auth — defineAuth orchestrator runtime (Caminho C Hybrid)\n *\n * Plan T1.2 implementation per blueprint Q5 § Caminho C signatures.\n * Composes existing primitives + the v1.1 EC-1/EC-2/EC-10 fixes.\n */\n\nimport { webcrypto } from \"node:crypto\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport {\n AuthCallbackError,\n AuthCancelledError,\n AuthConfigError,\n AuthProviderNotFoundError,\n} from \"./errors.js\";\nimport {\n clearTransaction,\n getTransaction,\n newTransaction,\n setTransaction,\n} from \"./oauth-transaction-store.js\";\nimport type { AuthOrchestrator, AuthProvider, DefineAuthOptions } from \"./types.js\";\nimport { validateReturnTo } from \"./validate-return-to.js\";\n\nconst PROVIDER_NAME_RE = /^[a-z0-9-]{1,32}$/;\n\nfunction randomState(): string {\n const bytes = webcrypto.getRandomValues(new Uint8Array(24));\n return Buffer.from(bytes).toString(\"base64url\");\n}\n\nfunction txCookieSecret<TSession>(opts: DefineAuthOptions<TSession>): string {\n // For T1.2 minimal impl, derive a per-session-manager secret from the\n // SessionManager identity. Production T2+ may refactor to share the\n // SessionManager's actual secret rotation chain via a `getCookieSecret()` method.\n // Falls back to TX_FALLBACK_SECRET when not available — apps should override.\n const sess = opts.session as unknown as { secret?: string | string[] };\n if (sess.secret) {\n if (Array.isArray(sess.secret)) {\n const first = sess.secret[0];\n if (first) return first;\n } else {\n return sess.secret;\n }\n }\n // Defensive fallback. Apps without SessionManager.secret pattern should\n // explicitly pass an env-backed secret OR rely on this dev-only fallback\n // (will fail in production scrutiny — flagged by future audit).\n return process.env.THEOKIT_OAUTH_TX_SECRET ?? \"DEV_ONLY_INSECURE_OAUTH_TX_SECRET_REPLACE_IN_PROD\";\n}\n\nexport function defineAuth<TSession>(\n opts: DefineAuthOptions<TSession>,\n): AuthOrchestrator<TSession> {\n // Validate config at define-time (per blueprint Q5 invariants)\n if (!opts.session) {\n throw new AuthConfigError(\"missing_session\", \"defineAuth({ session }) is required\");\n }\n\n const providersMap = new Map<string, AuthProvider<unknown, string>>();\n for (const provider of opts.providers ?? []) {\n if (!provider.name || !PROVIDER_NAME_RE.test(provider.name)) {\n throw new AuthConfigError(\n \"invalid_provider_name\",\n `Provider name must match ${PROVIDER_NAME_RE} (got: '${provider.name}')`,\n );\n }\n if (providersMap.has(provider.name)) {\n throw new AuthConfigError(\n \"duplicate_provider_name\",\n `Duplicate provider name: '${provider.name}'`,\n );\n }\n providersMap.set(provider.name, provider);\n }\n\n const txSecret = txCookieSecret(opts);\n\n function requireProvider(name: string): AuthProvider<unknown, string> {\n const p = providersMap.get(name);\n if (!p) throw new AuthProviderNotFoundError(name);\n return p;\n }\n\n async function startSignIn(\n providerName: string,\n req: IncomingMessage,\n startOpts?: { returnTo?: string },\n ): Promise<Response> {\n const provider = requireProvider(providerName);\n\n // EC-2 (v1.1) — validate returnTo same-origin\n const baseUrl = new URL(`http://${req.headers.host ?? \"localhost\"}${req.url ?? \"/\"}`);\n const safeReturnTo = validateReturnTo(startOpts?.returnTo, baseUrl);\n\n // Generate transaction\n const state = randomState();\n // pkceVerifier: providers that need PKCE generate it themselves and store via mutable side-channel;\n // for T1.2 we put a placeholder slot. T2+ refactors providers to receive the tx for verifier-write.\n const tx = newTransaction({ state, returnTo: safeReturnTo === \"/\" ? undefined : safeReturnTo });\n\n // Persist transaction cookie via headers (since we return Response, need Set-Cookie header manually)\n const authUrl = await provider.createAuthorizationURL(tx);\n\n // Build response with Set-Cookie + redirect\n const headers = new Headers();\n headers.set(\"Location\", authUrl.toString());\n const { encodeTransaction } = await import(\"./oauth-transaction-store.js\");\n const encodedTx = await encodeTransaction(tx, txSecret);\n headers.set(\n \"Set-Cookie\",\n `theo_oauth_tx=${encodedTx}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=600`,\n );\n\n return new Response(null, { status: 302, headers });\n }\n\n async function finishSignIn(\n providerName: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<{ session: TSession; returnTo?: string }> {\n const provider = requireProvider(providerName);\n\n // EC-1 (v1.1) — OAuth provider error response (user declined consent) check BEFORE code-exchange\n const url = new URL(`http://${req.headers.host ?? \"localhost\"}${req.url ?? \"/\"}`);\n const errorParam = url.searchParams.get(\"error\");\n if (errorParam) {\n const errorDescription = url.searchParams.get(\"error_description\") ?? undefined;\n if (errorParam === \"access_denied\") {\n throw new AuthCancelledError(errorDescription);\n }\n throw new AuthCallbackError(\n \"oauth_provider_error\",\n `Provider returned error: ${errorParam}${errorDescription ? ` (${errorDescription})` : \"\"}`,\n );\n }\n\n // Read + validate transaction cookie\n const tx = await getTransaction(req, txSecret);\n if (!tx) {\n throw new AuthCallbackError(\n \"oauth_transaction_expired\",\n \"OAuth transaction cookie missing or expired (>10min). Please retry sign-in.\",\n );\n }\n\n // Verify state matches query param (CSRF defense per RFC 6749 §10.12)\n const queryState = url.searchParams.get(\"state\");\n if (!queryState || queryState !== tx.state) {\n throw new AuthCallbackError(\n \"oauth_state_mismatch\",\n \"OAuth state mismatch. Possible CSRF attempt or stale callback.\",\n );\n }\n\n // Provider-specific callback handling (token exchange + userinfo fetch)\n const result = await provider.handleCallback(req, tx);\n\n // Invoke onSignIn callback if defined to derive session shape\n let sessionData: TSession;\n if (opts.onSignIn) {\n sessionData = await opts.onSignIn({ profile: result.profile, provider: result.providerName });\n } else {\n // No onSignIn — pass the raw profile as session (consumers must type their own TSession)\n sessionData = result.profile as unknown as TSession;\n }\n\n // EC-10 (v1.1) — OWASP A07:2021 session fixation mitigation: rotate session ID BEFORE creating new session\n try {\n await opts.session.rotateSession(req, res);\n } catch {\n // rotateSession may no-op if no pre-existing session — non-fatal\n }\n\n // Create session cookie\n await opts.session.createSession(res, sessionData);\n\n // Clear transaction cookie\n clearTransaction(res);\n\n return { session: sessionData, returnTo: tx.returnTo };\n }\n\n async function signIn<TProfile>(\n profile: TProfile,\n providerName: string,\n req: IncomingMessage,\n res: ServerResponse,\n ): Promise<TSession> {\n // Caminho A escape hatch — skip OAuth flow, directly derive + persist session\n let sessionData: TSession;\n if (opts.onSignIn) {\n sessionData = await opts.onSignIn({ profile, provider: providerName });\n } else {\n sessionData = profile as unknown as TSession;\n }\n\n try {\n await opts.session.rotateSession(req, res);\n } catch {\n /* no-op if no pre-existing session */\n }\n\n await opts.session.createSession(res, sessionData);\n return sessionData;\n }\n\n async function signOut(res: ServerResponse): Promise<void> {\n // Read current session before destroying (so onSignOut can see it)\n let sessionData: TSession | null = null;\n if (opts.onSignOut) {\n // We don't have req here per AuthOrchestrator contract; onSignOut receives null\n // when no pre-existing session is available. Apps that need session-aware\n // signOut should use the manual session.destroySession + custom logic.\n sessionData = null;\n }\n\n opts.session.destroySession(res);\n\n if (opts.onSignOut) {\n await opts.onSignOut(sessionData);\n }\n }\n\n async function getSession(req: IncomingMessage): Promise<TSession | null> {\n return opts.session.getSession(req);\n }\n\n return { startSignIn, finishSignIn, signIn, signOut, getSession };\n}\n","/**\n * @theokit/sdk/server/auth — same-origin returnTo validator\n *\n * Per v1.1 EC-2 MUST FIX — OWASP A01:2021 open-redirect mitigation.\n *\n * Without this check, attacker craft `/login?returnTo=https://evil.com` would\n * cause post-login redirect to attacker domain with authenticated session cookie.\n *\n * Rules:\n * - undefined/empty returnTo → default '/'\n * - protocol-relative `//evil.com` → default '/' (URL parser would resolve to baseUrl protocol)\n * - absolute URL with origin ≠ baseUrl.origin → default '/' (cross-origin redirect)\n * - absolute URL with origin === baseUrl.origin → keep (same-origin allowed)\n * - relative path starting with '/' → keep (same-app navigation)\n * - relative path not starting with '/' → default '/' (defensive)\n */\nexport function validateReturnTo(returnTo: string | undefined, baseUrl: URL): string {\n if (!returnTo || typeof returnTo !== \"string\" || returnTo.trim() === \"\") {\n return \"/\";\n }\n\n const trimmed = returnTo.trim();\n\n // Protocol-relative URLs (//evil.com) are dangerous — browser would resolve to current protocol\n if (trimmed.startsWith(\"//\")) {\n return \"/\";\n }\n\n // Try absolute URL parsing first\n if (URL.canParse(trimmed)) {\n const parsed = new URL(trimmed);\n if (parsed.origin === baseUrl.origin) {\n // Same-origin absolute URL — return the pathname+search+hash portion (drop origin)\n return parsed.pathname + parsed.search + parsed.hash;\n }\n // Cross-origin — reject\n return \"/\";\n }\n\n // Not parseable as absolute. Must start with '/' to be a valid relative path\n if (trimmed.startsWith(\"/\")) {\n return trimmed;\n }\n\n // Anything else (e.g., \"javascript:alert\", \"data:...\" that wasn't parseable, or bare strings) → default\n return \"/\";\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theokit/sdk",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "TypeScript SDK for the Theo agent harness — same surface, local or cloud.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/usetheo/theokit-sdk#readme",
@@ -98,6 +98,16 @@
98
98
  "default": "./dist/eval.cjs"
99
99
  }
100
100
  },
101
+ "./server/auth": {
102
+ "import": {
103
+ "types": "./dist/server/auth/index.d.ts",
104
+ "default": "./dist/server/auth/index.js"
105
+ },
106
+ "require": {
107
+ "types": "./dist/server/auth/index.d.cts",
108
+ "default": "./dist/server/auth/index.cjs"
109
+ }
110
+ },
101
111
  "./package.json": "./package.json"
102
112
  },
103
113
  "files": [
@@ -112,15 +122,6 @@
112
122
  "theokit-migrate-memory": "./bin/theokit-migrate-memory.mjs"
113
123
  },
114
124
  "sideEffects": false,
115
- "scripts": {
116
- "build": "tsup",
117
- "test": "vitest run --no-file-parallelism",
118
- "test:watch": "vitest",
119
- "typecheck": "tsc --noEmit",
120
- "clean": "rm -rf dist",
121
- "docs:json": "typedoc --options typedoc.json",
122
- "docs:drift": "tsx scripts/check-docs-drift.ts"
123
- },
124
125
  "peerDependencies": {
125
126
  "@lancedb/lancedb": "^0.30.0",
126
127
  "better-sqlite3": "^12.0.0",
@@ -165,7 +166,7 @@
165
166
  "sdk",
166
167
  "theo",
167
168
  "theokit",
168
- "usetheo"
169
+ "Theo"
169
170
  ],
170
171
  "dependencies": {
171
172
  "croner": "^9.0.0"
@@ -179,5 +180,14 @@
179
180
  "sqlite-vec": "^0.1.9",
180
181
  "typedoc": "^0.28.19",
181
182
  "zod": "^4.0.0"
183
+ },
184
+ "scripts": {
185
+ "build": "tsup",
186
+ "test": "vitest run --no-file-parallelism",
187
+ "test:watch": "vitest",
188
+ "typecheck": "tsc --noEmit",
189
+ "clean": "rm -rf dist",
190
+ "docs:json": "typedoc --options typedoc.json",
191
+ "docs:drift": "tsx scripts/check-docs-drift.ts"
182
192
  }
183
- }
193
+ }
package/dist/eval.d.cts DELETED
@@ -1,35 +0,0 @@
1
- /**
2
- * Public `Eval` namespace (Adoption Roadmap #2, ADRs D202-D213).
3
- *
4
- * Usage:
5
- *
6
- * import { Eval, Scorers } from "@theokit/sdk/eval";
7
- * const run = await Eval.create({
8
- * name: "qa-smoke",
9
- * dataset: [{ input: "Say ok", expected: "ok" }],
10
- * scorers: [Scorers.containsExpected()],
11
- * agent: { apiKey: process.env.OPENROUTER_API_KEY, model: { id: "openai/gpt-4o-mini" } },
12
- * }).run();
13
- * console.log(run.aggregate.meanScore);
14
- *
15
- * @public
16
- */
17
- import type { EvalOptions, EvalRun, EvalRunOptions } from "./types/eval.js";
18
- export declare class Eval {
19
- private readonly options;
20
- private constructor();
21
- /**
22
- * Construct an Eval. Throws on bad options (empty name, concurrency
23
- * out of range, empty scorers, etc).
24
- */
25
- static create(options: EvalOptions): Eval;
26
- /**
27
- * Run the eval. Resolves with a populated {@link EvalRun}. Per D208,
28
- * row errors are isolated; per D213, two runs with the same name
29
- * throw `EvalAlreadyRunningError`.
30
- */
31
- run(runOpts?: EvalRunOptions): Promise<EvalRun>;
32
- }
33
- export { EvalAlreadyRunningError } from "./internal/eval/single-flight.js";
34
- export { Scorers } from "./scorers.js";
35
- export type * from "./types/eval.js";