@theokit/sdk 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -1
- package/dist/server/auth/index.cjs +329 -0
- package/dist/server/auth/index.cjs.map +1 -0
- package/dist/server/auth/index.d.cts +176 -0
- package/dist/server/auth/index.d.ts +176 -0
- package/dist/server/auth/index.js +322 -0
- package/dist/server/auth/index.js.map +1 -0
- package/package.json +22 -12
- package/dist/eval.d.cts +0 -35
- package/dist/path-safety.d.cts +0 -15
- package/dist/task-store.d.cts +0 -8
- package/dist/tools/_path-scope.d.cts +0 -8
- package/dist/tools/_subprocess.d.cts +0 -28
- package/dist/tools/git-diff.d.cts +0 -22
- package/dist/tools/index.d.cts +0 -29
- package/dist/tools/list-dir.d.cts +0 -26
- package/dist/tools/read-file.d.cts +0 -31
- package/dist/tools/run-vitest.d.cts +0 -46
- package/dist/tools/search-text.d.cts +0 -32
- package/dist/workflow.d.cts +0 -97
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.0 - 2026-06-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`@theokit/sdk/server/auth` sub-path** (per ADR D6 of plan g11-auth-architecture-implementation v1.4) — orchestrator-only auth surface ships `defineAuth<TSession>(opts)` factory + 5 supporting types. Implements **Caminho C (Hybrid)** from discovery blueprint `g11-auth-architecture-decision` (SHIPPABLE 97.9). Providers ship as opt-in `@theokit/auth-*` packages (Tier 1: Google + GitHub + Magic Link — separate packages, semver-independent). Aligned with `AUTH-DELEGATION` lock in `theokit/CLAUDE.md:217-225` (lock's own escape-hatch clause "If we do adopt later: ship providers as separate optional packages under `@theokit/auth-*`, NEVER in the framework core").
|
|
8
|
+
- **6 type exports** at `@theokit/sdk/server/auth`:
|
|
9
|
+
- `defineAuth<TSession>(opts): AuthOrchestrator<TSession>` factory
|
|
10
|
+
- `DefineAuthOptions<TSession>` config shape
|
|
11
|
+
- `AuthOrchestrator<TSession>` 5-method surface (`startSignIn`, `finishSignIn`, `signIn`, `signOut`, `getSession`)
|
|
12
|
+
- `AuthProvider<TProfile, TName>` provider contract
|
|
13
|
+
- `AuthResult<TProfile, TName>` callback return shape
|
|
14
|
+
- `OAuthTransaction` cookie-state transaction shape
|
|
15
|
+
- **4 typed error classes:** `AuthConfigError`, `AuthProviderNotFoundError`, `AuthCallbackError`, `AuthCancelledError` (extends `AuthCallbackError`).
|
|
16
|
+
- **`validateReturnTo(returnTo, baseUrl)` helper** — same-origin validation for OWASP A01:2021 open-redirect mitigation.
|
|
17
|
+
|
|
18
|
+
### Edge cases absorbed inline (from plan v1.1 edge-case-plan)
|
|
19
|
+
|
|
20
|
+
- **EC-1** — `AuthCancelledError` thrown on OAuth `?error=access_denied` callback (RFC 6749 §4.1.2.1) BEFORE attempting code-exchange. Apps catch distinctly to render "Login cancelled" UX vs opaque "callback failed".
|
|
21
|
+
- **EC-2** — `validateReturnTo` rejects protocol-relative URLs (`//evil.com`), cross-origin absolute URLs, and bare strings. Defaults to `/` when unsafe.
|
|
22
|
+
- **EC-10** — `rotateSession()` called BEFORE `createSession()` in `finishSignIn` + `signIn` per OWASP A07:2021 session-fixation mitigation.
|
|
23
|
+
- **EC-6** — Typed `oauth_transaction_expired` code on `AuthCallbackError` for expired cookie-state transactions (≥ 10min old).
|
|
24
|
+
- **D5** — OAuth transaction stored in encrypted cookie (`theo_oauth_tx`, AES-256-GCM, 10-min expiry, HttpOnly + Secure + SameSite=Lax).
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
|
|
28
|
+
- v1.6.0 is **additive** — no breaking changes. Existing consumers of `createSessionManager` (from `theokit/server/auth`) unaffected.
|
|
29
|
+
- Providers (`@theokit/auth-google`, `@theokit/auth-github`, `@theokit/auth-magic-link`) ship in separate npm packages (Phase 2-4 of plan G11). They will publish to `@next` tag first per ADR D3 (4-6 week telemetry observation window before promote to `@latest`).
|
|
30
|
+
- Tests: 16/16 GREEN in `tests/server-auth.test.ts` covering config validation, EC-1, EC-2, EC-10, Caminho A signIn, expired transaction, unknown provider.
|
|
31
|
+
|
|
3
32
|
## 1.5.0
|
|
4
33
|
|
|
5
34
|
### Changed
|
|
@@ -1532,7 +1561,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
1532
1561
|
|
|
1533
1562
|
### Changed
|
|
1534
1563
|
|
|
1535
|
-
- License standardized to **Apache-2.0** (was MIT). Aligns all
|
|
1564
|
+
- License standardized to **Apache-2.0** (was MIT). Aligns all Theo open-core pillars under a single license — see root `CLAUDE.md` strategic review of 2026-05-14.
|
|
1536
1565
|
- `UnsupportedRunOperationError` now extends `TheokitAgentError` with `isRetryable: false` and stable `code: "unsupported_run_operation"`. Previously extended `Error` directly — old `instanceof TheokitAgentError` checks against this error now return `true`.
|
|
1537
1566
|
- `RunOperation` union extended with `"listArtifacts"` and `"downloadArtifact"`. Agent-level operations can now be reported through `UnsupportedRunOperationError.operation`.
|
|
1538
1567
|
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var buffer = require('buffer');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/server/auth/oauth-transaction-store.ts
|
|
17
|
+
var oauth_transaction_store_exports = {};
|
|
18
|
+
__export(oauth_transaction_store_exports, {
|
|
19
|
+
clearCookie: () => clearCookie,
|
|
20
|
+
clearTransaction: () => clearTransaction,
|
|
21
|
+
decodeTransaction: () => decodeTransaction,
|
|
22
|
+
encodeTransaction: () => encodeTransaction,
|
|
23
|
+
getCookie: () => getCookie,
|
|
24
|
+
getTransaction: () => getTransaction,
|
|
25
|
+
newTransaction: () => newTransaction,
|
|
26
|
+
setCookie: () => setCookie,
|
|
27
|
+
setTransaction: () => setTransaction
|
|
28
|
+
});
|
|
29
|
+
async function deriveKey(secret) {
|
|
30
|
+
const keyMaterial = await crypto.webcrypto.subtle.importKey(
|
|
31
|
+
"raw",
|
|
32
|
+
buffer.Buffer.from(secret).slice(0, 32).length === 32 ? buffer.Buffer.from(secret).slice(0, 32) : buffer.Buffer.concat([buffer.Buffer.from(secret), buffer.Buffer.alloc(32)]).slice(0, 32),
|
|
33
|
+
{ name: "AES-GCM" },
|
|
34
|
+
false,
|
|
35
|
+
["encrypt", "decrypt"]
|
|
36
|
+
);
|
|
37
|
+
return keyMaterial;
|
|
38
|
+
}
|
|
39
|
+
async function encodeTransaction(tx, secret) {
|
|
40
|
+
const key = await deriveKey(secret);
|
|
41
|
+
const iv = crypto.webcrypto.getRandomValues(new Uint8Array(12));
|
|
42
|
+
const plaintext = new TextEncoder().encode(JSON.stringify(tx));
|
|
43
|
+
const ciphertext = await crypto.webcrypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext);
|
|
44
|
+
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
|
|
45
|
+
combined.set(iv, 0);
|
|
46
|
+
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
47
|
+
return buffer.Buffer.from(combined).toString("base64url");
|
|
48
|
+
}
|
|
49
|
+
async function decodeTransaction(encoded, secret) {
|
|
50
|
+
try {
|
|
51
|
+
const key = await deriveKey(secret);
|
|
52
|
+
const combined = buffer.Buffer.from(encoded, "base64url");
|
|
53
|
+
const iv = combined.slice(0, 12);
|
|
54
|
+
const ciphertext = combined.slice(12);
|
|
55
|
+
const plaintext = await crypto.webcrypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
56
|
+
const tx = JSON.parse(new TextDecoder().decode(plaintext));
|
|
57
|
+
return tx;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function getCookie(req, name) {
|
|
63
|
+
const header = req.headers.cookie;
|
|
64
|
+
if (!header) return null;
|
|
65
|
+
for (const part of header.split(";")) {
|
|
66
|
+
const [k, ...v] = part.trim().split("=");
|
|
67
|
+
if (k === name) return v.join("=");
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function setCookie(res, name, value) {
|
|
72
|
+
const existing = res.getHeader("Set-Cookie");
|
|
73
|
+
const cookie = `${name}=${value}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=${TX_LIFETIME_MS / 1e3}`;
|
|
74
|
+
if (Array.isArray(existing)) {
|
|
75
|
+
res.setHeader("Set-Cookie", [...existing, cookie]);
|
|
76
|
+
} else if (typeof existing === "string") {
|
|
77
|
+
res.setHeader("Set-Cookie", [existing, cookie]);
|
|
78
|
+
} else {
|
|
79
|
+
res.setHeader("Set-Cookie", cookie);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function clearCookie(res, name) {
|
|
83
|
+
setCookie(
|
|
84
|
+
res,
|
|
85
|
+
name,
|
|
86
|
+
`; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`.split(";").slice(0, 1).join("")
|
|
87
|
+
);
|
|
88
|
+
const clear = `${name}=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0`;
|
|
89
|
+
const existing = res.getHeader("Set-Cookie");
|
|
90
|
+
if (Array.isArray(existing)) {
|
|
91
|
+
res.setHeader("Set-Cookie", [...existing.filter((c) => !c.includes(`${name}=`)), clear]);
|
|
92
|
+
} else {
|
|
93
|
+
res.setHeader("Set-Cookie", clear);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function setTransaction(res, tx, secret) {
|
|
97
|
+
const encoded = await encodeTransaction(tx, secret);
|
|
98
|
+
setCookie(res, COOKIE_NAME, encoded);
|
|
99
|
+
}
|
|
100
|
+
async function getTransaction(req, secret) {
|
|
101
|
+
const raw = getCookie(req, COOKIE_NAME);
|
|
102
|
+
if (!raw) return null;
|
|
103
|
+
const tx = await decodeTransaction(raw, secret);
|
|
104
|
+
if (!tx) return null;
|
|
105
|
+
if (tx.expiresAt < Date.now()) return null;
|
|
106
|
+
return tx;
|
|
107
|
+
}
|
|
108
|
+
function clearTransaction(res) {
|
|
109
|
+
clearCookie(res, COOKIE_NAME);
|
|
110
|
+
}
|
|
111
|
+
function newTransaction(opts) {
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
return {
|
|
114
|
+
state: opts.state,
|
|
115
|
+
pkceVerifier: opts.pkceVerifier,
|
|
116
|
+
returnTo: opts.returnTo,
|
|
117
|
+
createdAt: now,
|
|
118
|
+
expiresAt: now + TX_LIFETIME_MS
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
var COOKIE_NAME, TX_LIFETIME_MS;
|
|
122
|
+
var init_oauth_transaction_store = __esm({
|
|
123
|
+
"src/server/auth/oauth-transaction-store.ts"() {
|
|
124
|
+
COOKIE_NAME = "theo_oauth_tx";
|
|
125
|
+
TX_LIFETIME_MS = 10 * 60 * 1e3;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// src/server/auth/errors.ts
|
|
130
|
+
var AuthConfigError = class extends Error {
|
|
131
|
+
name = "AuthConfigError";
|
|
132
|
+
code;
|
|
133
|
+
constructor(code, message) {
|
|
134
|
+
super(`[${code}] ${message}`);
|
|
135
|
+
this.code = code;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var AuthProviderNotFoundError = class extends Error {
|
|
139
|
+
name = "AuthProviderNotFoundError";
|
|
140
|
+
providerName;
|
|
141
|
+
constructor(providerName) {
|
|
142
|
+
super(
|
|
143
|
+
`Auth provider not found: '${providerName}'. Register it in defineAuth({ providers: [...] }).`
|
|
144
|
+
);
|
|
145
|
+
this.providerName = providerName;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var AuthCallbackError = class extends Error {
|
|
149
|
+
name = "AuthCallbackError";
|
|
150
|
+
code;
|
|
151
|
+
constructor(code, message) {
|
|
152
|
+
super(message ?? `OAuth callback error: ${code}`);
|
|
153
|
+
this.code = code;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var AuthCancelledError = class extends AuthCallbackError {
|
|
157
|
+
name = "AuthCancelledError";
|
|
158
|
+
errorDescription;
|
|
159
|
+
constructor(errorDescription) {
|
|
160
|
+
super("user_declined_consent", errorDescription ?? "User declined consent at provider");
|
|
161
|
+
this.errorDescription = errorDescription;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
init_oauth_transaction_store();
|
|
165
|
+
|
|
166
|
+
// src/server/auth/validate-return-to.ts
|
|
167
|
+
function validateReturnTo(returnTo, baseUrl) {
|
|
168
|
+
if (!returnTo || typeof returnTo !== "string" || returnTo.trim() === "") {
|
|
169
|
+
return "/";
|
|
170
|
+
}
|
|
171
|
+
const trimmed = returnTo.trim();
|
|
172
|
+
if (trimmed.startsWith("//")) {
|
|
173
|
+
return "/";
|
|
174
|
+
}
|
|
175
|
+
if (URL.canParse(trimmed)) {
|
|
176
|
+
const parsed = new URL(trimmed);
|
|
177
|
+
if (parsed.origin === baseUrl.origin) {
|
|
178
|
+
return parsed.pathname + parsed.search + parsed.hash;
|
|
179
|
+
}
|
|
180
|
+
return "/";
|
|
181
|
+
}
|
|
182
|
+
if (trimmed.startsWith("/")) {
|
|
183
|
+
return trimmed;
|
|
184
|
+
}
|
|
185
|
+
return "/";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/server/auth/orchestrator.ts
|
|
189
|
+
var PROVIDER_NAME_RE = /^[a-z0-9-]{1,32}$/;
|
|
190
|
+
function randomState() {
|
|
191
|
+
const bytes = crypto.webcrypto.getRandomValues(new Uint8Array(24));
|
|
192
|
+
return Buffer.from(bytes).toString("base64url");
|
|
193
|
+
}
|
|
194
|
+
function txCookieSecret(opts) {
|
|
195
|
+
const sess = opts.session;
|
|
196
|
+
if (sess.secret) {
|
|
197
|
+
if (Array.isArray(sess.secret)) {
|
|
198
|
+
const first = sess.secret[0];
|
|
199
|
+
if (first) return first;
|
|
200
|
+
} else {
|
|
201
|
+
return sess.secret;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return process.env.THEOKIT_OAUTH_TX_SECRET ?? "DEV_ONLY_INSECURE_OAUTH_TX_SECRET_REPLACE_IN_PROD";
|
|
205
|
+
}
|
|
206
|
+
function defineAuth(opts) {
|
|
207
|
+
if (!opts.session) {
|
|
208
|
+
throw new AuthConfigError("missing_session", "defineAuth({ session }) is required");
|
|
209
|
+
}
|
|
210
|
+
const providersMap = /* @__PURE__ */ new Map();
|
|
211
|
+
for (const provider of opts.providers ?? []) {
|
|
212
|
+
if (!provider.name || !PROVIDER_NAME_RE.test(provider.name)) {
|
|
213
|
+
throw new AuthConfigError(
|
|
214
|
+
"invalid_provider_name",
|
|
215
|
+
`Provider name must match ${PROVIDER_NAME_RE} (got: '${provider.name}')`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (providersMap.has(provider.name)) {
|
|
219
|
+
throw new AuthConfigError(
|
|
220
|
+
"duplicate_provider_name",
|
|
221
|
+
`Duplicate provider name: '${provider.name}'`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
providersMap.set(provider.name, provider);
|
|
225
|
+
}
|
|
226
|
+
const txSecret = txCookieSecret(opts);
|
|
227
|
+
function requireProvider(name) {
|
|
228
|
+
const p = providersMap.get(name);
|
|
229
|
+
if (!p) throw new AuthProviderNotFoundError(name);
|
|
230
|
+
return p;
|
|
231
|
+
}
|
|
232
|
+
async function startSignIn(providerName, req, startOpts) {
|
|
233
|
+
const provider = requireProvider(providerName);
|
|
234
|
+
const baseUrl = new URL(`http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`);
|
|
235
|
+
const safeReturnTo = validateReturnTo(startOpts?.returnTo, baseUrl);
|
|
236
|
+
const state = randomState();
|
|
237
|
+
const tx = newTransaction({ state, returnTo: safeReturnTo === "/" ? void 0 : safeReturnTo });
|
|
238
|
+
const authUrl = await provider.createAuthorizationURL(tx);
|
|
239
|
+
const headers = new Headers();
|
|
240
|
+
headers.set("Location", authUrl.toString());
|
|
241
|
+
const { encodeTransaction: encodeTransaction2 } = await Promise.resolve().then(() => (init_oauth_transaction_store(), oauth_transaction_store_exports));
|
|
242
|
+
const encodedTx = await encodeTransaction2(tx, txSecret);
|
|
243
|
+
headers.set(
|
|
244
|
+
"Set-Cookie",
|
|
245
|
+
`theo_oauth_tx=${encodedTx}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=600`
|
|
246
|
+
);
|
|
247
|
+
return new Response(null, { status: 302, headers });
|
|
248
|
+
}
|
|
249
|
+
async function finishSignIn(providerName, req, res) {
|
|
250
|
+
const provider = requireProvider(providerName);
|
|
251
|
+
const url = new URL(`http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`);
|
|
252
|
+
const errorParam = url.searchParams.get("error");
|
|
253
|
+
if (errorParam) {
|
|
254
|
+
const errorDescription = url.searchParams.get("error_description") ?? void 0;
|
|
255
|
+
if (errorParam === "access_denied") {
|
|
256
|
+
throw new AuthCancelledError(errorDescription);
|
|
257
|
+
}
|
|
258
|
+
throw new AuthCallbackError(
|
|
259
|
+
"oauth_provider_error",
|
|
260
|
+
`Provider returned error: ${errorParam}${errorDescription ? ` (${errorDescription})` : ""}`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const tx = await getTransaction(req, txSecret);
|
|
264
|
+
if (!tx) {
|
|
265
|
+
throw new AuthCallbackError(
|
|
266
|
+
"oauth_transaction_expired",
|
|
267
|
+
"OAuth transaction cookie missing or expired (>10min). Please retry sign-in."
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
const queryState = url.searchParams.get("state");
|
|
271
|
+
if (!queryState || queryState !== tx.state) {
|
|
272
|
+
throw new AuthCallbackError(
|
|
273
|
+
"oauth_state_mismatch",
|
|
274
|
+
"OAuth state mismatch. Possible CSRF attempt or stale callback."
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const result = await provider.handleCallback(req, tx);
|
|
278
|
+
let sessionData;
|
|
279
|
+
if (opts.onSignIn) {
|
|
280
|
+
sessionData = await opts.onSignIn({ profile: result.profile, provider: result.providerName });
|
|
281
|
+
} else {
|
|
282
|
+
sessionData = result.profile;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
await opts.session.rotateSession(req, res);
|
|
286
|
+
} catch {
|
|
287
|
+
}
|
|
288
|
+
await opts.session.createSession(res, sessionData);
|
|
289
|
+
clearTransaction(res);
|
|
290
|
+
return { session: sessionData, returnTo: tx.returnTo };
|
|
291
|
+
}
|
|
292
|
+
async function signIn(profile, providerName, req, res) {
|
|
293
|
+
let sessionData;
|
|
294
|
+
if (opts.onSignIn) {
|
|
295
|
+
sessionData = await opts.onSignIn({ profile, provider: providerName });
|
|
296
|
+
} else {
|
|
297
|
+
sessionData = profile;
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
await opts.session.rotateSession(req, res);
|
|
301
|
+
} catch {
|
|
302
|
+
}
|
|
303
|
+
await opts.session.createSession(res, sessionData);
|
|
304
|
+
return sessionData;
|
|
305
|
+
}
|
|
306
|
+
async function signOut(res) {
|
|
307
|
+
let sessionData = null;
|
|
308
|
+
if (opts.onSignOut) {
|
|
309
|
+
sessionData = null;
|
|
310
|
+
}
|
|
311
|
+
opts.session.destroySession(res);
|
|
312
|
+
if (opts.onSignOut) {
|
|
313
|
+
await opts.onSignOut(sessionData);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async function getSession(req) {
|
|
317
|
+
return opts.session.getSession(req);
|
|
318
|
+
}
|
|
319
|
+
return { startSignIn, finishSignIn, signIn, signOut, getSession };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
exports.AuthCallbackError = AuthCallbackError;
|
|
323
|
+
exports.AuthCancelledError = AuthCancelledError;
|
|
324
|
+
exports.AuthConfigError = AuthConfigError;
|
|
325
|
+
exports.AuthProviderNotFoundError = AuthProviderNotFoundError;
|
|
326
|
+
exports.defineAuth = defineAuth;
|
|
327
|
+
exports.validateReturnTo = validateReturnTo;
|
|
328
|
+
//# sourceMappingURL=index.cjs.map
|
|
329
|
+
//# sourceMappingURL=index.cjs.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":["webcrypto","Buffer","encodeTransaction"],"mappings":"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","file":"index.cjs","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"]}
|
|
@@ -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 };
|