@shware/security 3.4.1 → 4.0.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/README.md +40 -6
- package/dist/auth/attributes.cjs +31 -0
- package/dist/auth/attributes.cjs.map +1 -0
- package/dist/auth/attributes.d.cts +17 -0
- package/dist/auth/attributes.d.cts.map +1 -0
- package/dist/auth/attributes.d.mts +17 -0
- package/dist/auth/attributes.d.mts.map +1 -0
- package/dist/auth/attributes.mjs +27 -0
- package/dist/auth/attributes.mjs.map +1 -0
- package/dist/auth/index.cjs +72 -19
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +2 -2
- package/dist/auth/index.d.cts.map +1 -1
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.d.mts.map +1 -1
- package/dist/auth/index.mjs +72 -19
- package/dist/auth/index.mjs.map +1 -1
- package/dist/auth/types.d.cts +32 -2
- package/dist/auth/types.d.cts.map +1 -1
- package/dist/auth/types.d.mts +32 -2
- package/dist/auth/types.d.mts.map +1 -1
- package/dist/email/schema.cjs +3 -1
- package/dist/email/schema.cjs.map +1 -1
- package/dist/email/schema.d.cts +1 -0
- package/dist/email/schema.d.cts.map +1 -1
- package/dist/email/schema.d.mts +1 -0
- package/dist/email/schema.d.mts.map +1 -1
- package/dist/email/schema.mjs +3 -1
- package/dist/email/schema.mjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/oauth2/types.cjs.map +1 -1
- package/dist/oauth2/types.d.cts +6 -0
- package/dist/oauth2/types.d.cts.map +1 -1
- package/dist/oauth2/types.d.mts +6 -0
- package/dist/oauth2/types.d.mts.map +1 -1
- package/dist/oauth2/types.mjs.map +1 -1
- package/package.json +2 -2
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Principal } from "../core/index.mjs";
|
|
2
2
|
import { Session } from "../session/types.mjs";
|
|
3
|
-
import { AuthConfig, AuthService, AuthorizedHandler, LoggedHandler } from "./types.mjs";
|
|
3
|
+
import { AuthConfig, AuthService, AuthorizedHandler, LoggedHandler, OAuth2AuthorizationOptions } from "./types.mjs";
|
|
4
4
|
//#region src/auth/index.d.ts
|
|
5
5
|
export declare const PATH: {
|
|
6
6
|
readonly CSRF: '/csrf';
|
|
@@ -42,7 +42,7 @@ export declare class Auth implements AuthService {
|
|
|
42
42
|
private getDesktopCodeKey;
|
|
43
43
|
oauth2State: (request: Request) => Promise<Response>;
|
|
44
44
|
oauth2Nonce: (request: Request) => Promise<Response>;
|
|
45
|
-
oauth2Authorization: (request: Request) => Promise<Response>;
|
|
45
|
+
oauth2Authorization: (request: Request, options?: OAuth2AuthorizationOptions) => Promise<Response>;
|
|
46
46
|
private readonly redirect;
|
|
47
47
|
loginOAuth2Code: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
|
|
48
48
|
loginOAuth2Native: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/auth/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/auth/index.ts"],"mappings":";;;;qBA+Ea;WACX;WACA;WACA;WAEA;WACA;WACA;WACA;WACA;WACA;WAEA;WACA;WACA;WACA;WASA;WACA;WAEA;;qBAGW,gBAAgB;mBACV;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;EAEjB,cACE,mBACA,cACA,oBACA,QACA,QACA,UACC;UAiBK;EAYR,OAAI,UAAoB,YAAU,QAAQ;EAmB1C,SAAM,SAAmB,YAAU,QAAQ;EAQ3C,SAAM,SAAmB,SAAO,WAAa,kBAAgB,QAAQ;UAsB7D;UAUA;UAIA;UAIA;UAIA;UAIA;EAIR,cAAW,SAAmB,YAAU,QAAQ;EAOhD,cAAW,SAAmB,YAAU,QAAQ;EAOhD,sBAAmB,SACR,SAAO,UACN,+BACT,QAAQ;mBAmCM;EAQjB,kBAAe,SAAmB,SAAO,SAAW,sBAAoB,QAAQ;EAoFhF,oBAAiB,SAAmB,SAAO,SAAW,sBAAoB,QAAQ;EA4ClF,oBAAiB,SACN,SAAO,SACP,mBAAiB,4BAEzB,QAAQ;EA8DX,4BAAyB,SACd,SAAO,SACP;IAAQ;IAAe;QAA+B,kBAC9D,QAAQ;EAqBX,aAAU,SAAmB,SAAO,SAAW,sBAAoB,QAAQ;;;;;;;;;;;;;;;;;;;;EAuD3E,mBAAgB,SAAmB,YAAU,QAAQ;;;;;;;;;;;;;;;;;;;EA0DrD,kBAAe,SAAmB,YAAU,QAAQ;EAgEpD,4BAAyB,UACb,SAAO,UACP;IAAQ;IAAe;QAA+B,kBAC/D,QAAQ;EAIX,aAAU,UAAoB,SAAO,UAAY,sBAAoB,QAAQ;EAI7E,OAAI,WAAqB,cAAY;EAOrC,kBAAe,SAAmB,YAAU;EAU5C,aAAoB,mBAAiB,SAC1B,SAAO,QACR,MACP,QAAQ,iBAAiB,UAAU;EAwBtC,gBAAa,sBAA8B;EAI3C,eAAY,SAAmB,YAAU,QAAQ;EAUjD,eAAY,WAAqB,cAAY,QAAQ;EAKrD,yBAAsB,0BAAkC,QAAQ"}
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MAX_ATTRIBUTES_BYTES, attributesSize, bodyAttributes } from "./attributes.mjs";
|
|
1
2
|
import { loginEmailSchema, sendEmailVerificationCodeSchema } from "../email/schema.mjs";
|
|
2
3
|
import { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from "../oauth2/client.mjs";
|
|
3
4
|
import { google } from "../oauth2/provider/google.mjs";
|
|
@@ -10,6 +11,24 @@ import { createHash, randomInt, randomUUID, timingSafeEqual } from "node:crypto"
|
|
|
10
11
|
import { invariant } from "@shware/utils";
|
|
11
12
|
//#region src/auth/index.ts
|
|
12
13
|
const PKCE_FORMAT = /^[A-Za-z0-9_-]{43,128}$/;
|
|
14
|
+
function authorizationAttributes(attributes) {
|
|
15
|
+
if (!attributes) return void 0;
|
|
16
|
+
const defined = {};
|
|
17
|
+
for (const [key, value] of Object.entries(attributes)) if (value !== void 0) defined[key] = value;
|
|
18
|
+
if (Object.keys(defined).length === 0) return void 0;
|
|
19
|
+
const size = attributesSize(defined);
|
|
20
|
+
invariant(size <= MAX_ATTRIBUTES_BYTES, `oauth2 authorization attributes are ${size} bytes, at most ${MAX_ATTRIBUTES_BYTES} allowed`);
|
|
21
|
+
return defined;
|
|
22
|
+
}
|
|
23
|
+
/** Sign in with Apple answers with `response_mode=form_post`; a charset parameter may follow. */
|
|
24
|
+
const INVALID_ATTRIBUTES = {
|
|
25
|
+
error: "invalid_request",
|
|
26
|
+
error_description: `attributes must map strings to strings, at most ${MAX_ATTRIBUTES_BYTES} bytes`
|
|
27
|
+
};
|
|
28
|
+
function isFormPost(request) {
|
|
29
|
+
const contentType = request.headers.get("content-type")?.toLowerCase();
|
|
30
|
+
return request.method === "POST" && !!contentType?.startsWith("application/x-www-form-urlencoded");
|
|
31
|
+
}
|
|
13
32
|
const PATH = {
|
|
14
33
|
CSRF: "/csrf",
|
|
15
34
|
LOGOUT: "/logout",
|
|
@@ -135,8 +154,9 @@ var Auth = class {
|
|
|
135
154
|
await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 600);
|
|
136
155
|
return Response.json({ nonce });
|
|
137
156
|
};
|
|
138
|
-
oauth2Authorization = async (request) => {
|
|
157
|
+
oauth2Authorization = async (request, options) => {
|
|
139
158
|
invariant(this.oauth2Client, "oauth2Client is not initialized");
|
|
159
|
+
const attributes = authorizationAttributes(options?.attributes);
|
|
140
160
|
const { mark, setTiming } = timing({ enabled: this.timing });
|
|
141
161
|
const { registrationId } = param(request, PATH.OAUTH2_AUTHORIZATION);
|
|
142
162
|
const state = randomUUID();
|
|
@@ -153,7 +173,8 @@ var Auth = class {
|
|
|
153
173
|
state,
|
|
154
174
|
registrationId,
|
|
155
175
|
authorizationRequestUri: uri.href,
|
|
156
|
-
additionalParameters: pkce
|
|
176
|
+
additionalParameters: pkce,
|
|
177
|
+
...attributes ? { attributes } : {}
|
|
157
178
|
};
|
|
158
179
|
const value = JSON.stringify(authorizationRequest);
|
|
159
180
|
session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);
|
|
@@ -190,7 +211,7 @@ var Auth = class {
|
|
|
190
211
|
const cached = JSON.parse(json);
|
|
191
212
|
const { registrationId } = param(request, PATH.LOGIN_OAUTH2_CODE);
|
|
192
213
|
let data;
|
|
193
|
-
if (request
|
|
214
|
+
if (isFormPost(request)) {
|
|
194
215
|
const params = new URLSearchParams(await request.text());
|
|
195
216
|
data = Object.fromEntries(params.entries());
|
|
196
217
|
} else data = query(request);
|
|
@@ -211,7 +232,12 @@ var Auth = class {
|
|
|
211
232
|
token
|
|
212
233
|
});
|
|
213
234
|
mark("exchange_code");
|
|
214
|
-
const principal = await handler(request,
|
|
235
|
+
const principal = await handler(request, {
|
|
236
|
+
registrationId,
|
|
237
|
+
userInfo,
|
|
238
|
+
token,
|
|
239
|
+
attributes: cached.attributes ?? {}
|
|
240
|
+
});
|
|
215
241
|
mark("save_principal");
|
|
216
242
|
session.setLastAccessedTime(Date.now());
|
|
217
243
|
session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);
|
|
@@ -237,7 +263,10 @@ var Auth = class {
|
|
|
237
263
|
invariant(this.oauth2Client, "oauth2Client is not initialized");
|
|
238
264
|
const { mark, setTiming } = timing({ enabled: this.timing });
|
|
239
265
|
const { registrationId } = param(request, PATH.LOGIN_OAUTH2_NATIVE);
|
|
240
|
-
const
|
|
266
|
+
const body = await request.json();
|
|
267
|
+
const attributes = bodyAttributes(body);
|
|
268
|
+
if (!attributes) return Response.json(INVALID_ATTRIBUTES, { status: 400 });
|
|
269
|
+
const { attributes: _, ...credentials } = body;
|
|
241
270
|
const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({
|
|
242
271
|
registrationId,
|
|
243
272
|
credentials
|
|
@@ -252,7 +281,12 @@ var Auth = class {
|
|
|
252
281
|
await this.kv.removeItem(this.getOauth2NonceKey(nonce));
|
|
253
282
|
mark("check_nonce");
|
|
254
283
|
}
|
|
255
|
-
const principal = await handler(request,
|
|
284
|
+
const principal = await handler(request, {
|
|
285
|
+
registrationId,
|
|
286
|
+
userInfo,
|
|
287
|
+
token,
|
|
288
|
+
attributes
|
|
289
|
+
});
|
|
256
290
|
mark("save_principal");
|
|
257
291
|
const session = this.repository.createSession();
|
|
258
292
|
session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);
|
|
@@ -273,28 +307,43 @@ var Auth = class {
|
|
|
273
307
|
invariant(this.oauth2Client, "oauth2Client is not initialized");
|
|
274
308
|
const { mark, setTiming } = timing({ enabled: this.timing });
|
|
275
309
|
let body;
|
|
310
|
+
let attributes = {};
|
|
276
311
|
if (request.headers.get("Content-Type")?.includes("application/x-www-form-urlencoded")) {
|
|
277
312
|
const text = await request.text();
|
|
278
313
|
body = Object.fromEntries(new URLSearchParams(text));
|
|
279
|
-
} else if (request.headers.get("Content-Type")?.includes("application/json"))
|
|
280
|
-
|
|
314
|
+
} else if (request.headers.get("Content-Type")?.includes("application/json")) {
|
|
315
|
+
const json = await request.json();
|
|
316
|
+
attributes = bodyAttributes(json);
|
|
317
|
+
body = json;
|
|
318
|
+
} else return Response.json({
|
|
281
319
|
error: "invalid_request",
|
|
282
320
|
error_description: "invalid content type"
|
|
283
321
|
}, { status: 400 });
|
|
322
|
+
if (!attributes) return Response.json(INVALID_ATTRIBUTES, { status: 400 });
|
|
284
323
|
const parsed = googleOneTapSchema.parse(body);
|
|
285
324
|
let principal;
|
|
286
325
|
if (parsed.credential) {
|
|
287
326
|
const { token, userInfo } = await google.getTokenInfo(parsed.credential);
|
|
288
|
-
principal = await handler(request,
|
|
327
|
+
principal = await handler(request, {
|
|
328
|
+
registrationId,
|
|
329
|
+
userInfo,
|
|
330
|
+
token,
|
|
331
|
+
attributes
|
|
332
|
+
});
|
|
289
333
|
} else if (parsed.code) {
|
|
290
334
|
const token = await this.oauth2Client.exchangeAuthorizationCode({
|
|
291
335
|
registrationId,
|
|
292
336
|
code: parsed.code
|
|
293
337
|
});
|
|
294
|
-
principal = await handler(request,
|
|
338
|
+
principal = await handler(request, {
|
|
295
339
|
registrationId,
|
|
296
|
-
|
|
297
|
-
|
|
340
|
+
userInfo: await this.oauth2Client.getUserInfo({
|
|
341
|
+
registrationId,
|
|
342
|
+
token
|
|
343
|
+
}),
|
|
344
|
+
token,
|
|
345
|
+
attributes
|
|
346
|
+
});
|
|
298
347
|
} else throw new Error("invalid request");
|
|
299
348
|
mark("save_principal");
|
|
300
349
|
const session = this.repository.createSession();
|
|
@@ -335,18 +384,22 @@ var Auth = class {
|
|
|
335
384
|
loginEmail = async (request, handler) => {
|
|
336
385
|
const { data, error } = valid(loginEmailSchema, await request.json());
|
|
337
386
|
if (error) return Response.json(error, { status: error.error.code });
|
|
338
|
-
const { email, verificationCode } = data;
|
|
387
|
+
const { email, verificationCode, attributes } = data;
|
|
339
388
|
const key = this.getEmailVerificationCodeKey(email);
|
|
340
389
|
const stored = await this.kv.getItem(key);
|
|
341
390
|
if (typeof stored !== "string" || stored.length !== verificationCode.length || !timingSafeEqual(Buffer.from(stored), Buffer.from(verificationCode))) return Response.json(invalidArgument("INVALID_VERIFICATION_CODE"), { status: 400 });
|
|
342
391
|
await this.kv.removeItem(key);
|
|
343
|
-
const principal = await handler(request,
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
392
|
+
const principal = await handler(request, {
|
|
393
|
+
registrationId: "EMAIL",
|
|
394
|
+
userInfo: {
|
|
395
|
+
claims: {
|
|
396
|
+
sub: email,
|
|
397
|
+
email,
|
|
398
|
+
email_verified: true
|
|
399
|
+
},
|
|
400
|
+
data: {}
|
|
348
401
|
},
|
|
349
|
-
|
|
402
|
+
attributes: attributes ?? {}
|
|
350
403
|
});
|
|
351
404
|
const session = this.repository.createSession();
|
|
352
405
|
session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);
|
package/dist/auth/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomInt, randomUUID, timingSafeEqual } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport type { Principal } from '../core/index';\nimport { loginEmailSchema, sendEmailVerificationCodeSchema } from '../email/schema';\nimport { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport type { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport type {\n NativeCredential,\n OAuth2AuthorizationRequest,\n PkceParameters,\n StandardClaims,\n} from '../oauth2/types';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport {\n type CookieOptions,\n deleteCookie,\n getCookie,\n param,\n query,\n setCookie,\n} from '../utils/http';\nimport { timing } from '../utils/timing';\nimport { invalidArgument, valid, verifyTurnstileToken } from '../utils/valid';\nimport type { AuthConfig, AuthService, AuthorizedHandler, LoggedHandler } from './types';\n\n// PKCE per RFC 7636: code_verifier / code_challenge are 43-128 chars from\n// the unreserved set. We restrict to base64url (drops \".\" and \"~\") since\n// that's what S256 produces and what callers should be sending; rejecting\n// the rest shrinks the accepted input shape without losing legit clients.\n// A 32-byte random verifier base64url-encodes to exactly 43 chars.\nconst PKCE_FORMAT = /^[A-Za-z0-9_-]{43,128}$/;\n\ninterface DesktopCodeRecord {\n /** principal.name from the upstream cookie session */\n name: string;\n /** code_challenge (S256, base64url) — verifier must hash to this */\n cc: string;\n}\n\nexport const PATH = {\n CSRF: '/csrf',\n LOGOUT: '/logout',\n LOGGED: '/logged',\n\n OAUTH2_STATE: '/oauth2/state/:registrationId',\n OAUTH2_NONCE: '/oauth2/nonce/:registrationId',\n OAUTH2_AUTHORIZATION: '/oauth2/authorization/:registrationId',\n LOGIN_OAUTH2_CODE: '/login/oauth2/code/:registrationId',\n LOGIN_OAUTH2_NATIVE: '/login/oauth2/native/:registrationId',\n LOGIN_OAUTH2_ONETAP: '/login/oauth2/onetap/google',\n\n LOGIN_EMAIL: '/login/email',\n LOGIN_PHONE: '/login/phone',\n SEND_EMAIL_VERIFICATION_CODE: '/auth/sendEmailVerificationCode',\n SEND_PHONE_VERIFICATION_CODE: '/auth/sendPhoneVerificationCode',\n\n // Desktop / native app sign-in via web loopback (RFC 8252 + OAuth 2.1 Auth Code Flow):\n // 1. user already authenticated with cookie session → POST DESKTOP_AUTHORIZE → { code }\n // 2. web redirects to desktop's loopback callback with the code\n // 3. desktop renderer POSTs DESKTOP_EXCHANGE with the code → server returns\n // Set-Cookie carrying a fresh session, single-use code is invalidated\n // The two-step flow keeps the actual session id off the redirect URL — only a\n // 5-minute single-use code transits via the loopback.\n DESKTOP_AUTHORIZE: '/auth/desktop/authorize',\n DESKTOP_EXCHANGE: '/auth/desktop/exchange',\n\n CLEANUP_EXPIRED_SESSIONS: '/sessions/expired/cleanup',\n} as const;\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n private readonly turnstileSecretKey?: string;\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n constructor({\n sessionRepository,\n kvRepository,\n turnstileSecretKey,\n oauth2,\n cookie,\n timing,\n }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n this.turnstileSecretKey = turnstileSecretKey;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n private getSessionId(request: Request): string | undefined {\n let sessionId: string | undefined;\n const header = request.headers.get('Authorization');\n if (header?.startsWith('Bearer ')) {\n sessionId = header.replace('Bearer ', '');\n }\n if (!sessionId) {\n sessionId = getCookie(request, this.cookieName);\n }\n return sessionId;\n }\n\n csrf = async (_request: Request): Promise<Response> => {\n const token = randomUUID();\n const response = Response.json({\n token,\n parameterName: '_csrf',\n headerName: 'X-XSRF-TOKEN',\n });\n\n // Double Submit Cookie Protection\n setCookie(response, 'XSRF-TOKEN', token, {\n path: '/',\n secure: true,\n httpOnly: false,\n sameSite: 'none',\n domain: this.cookieOptions.domain,\n });\n return response;\n };\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = this.getSessionId(request);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from header or cookie\n const sessionId = this.getSessionId(request);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n private getEmailVerificationCodeKey(email: string) {\n return `email:verification:${email}`;\n }\n\n private getPhoneVerificationCodeKey(phone: string) {\n return `phone:verification:${phone}`;\n }\n\n private getDesktopCodeKey(code: string) {\n return `desktop:code:${code}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, PATH.OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, PATH.OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (request: Request): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, PATH.OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = await this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = this.getSessionId(request);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private readonly redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from header or cookie\n const sessionId = this.getSessionId(request);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, PATH.LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (\n request.method === 'POST' &&\n request.headers.get('content-type')?.toLowerCase() === 'application/x-www-form-urlencoded'\n ) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal\n const principal = await handler(request, registrationId, userInfo, token);\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const uri = new URL(this.oauth2Client.successUri);\n uri.searchParams.set('status', 'success');\n uri.searchParams.set('registrationId', registrationId);\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, PATH.LOGIN_OAUTH2_NATIVE);\n const credentials = (await request.json()) as NativeCredential;\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await handler(request, registrationId, userInfo, token);\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n handler: AuthorizedHandler,\n registrationId = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n let body: Record<string, string>;\n if (request.headers.get('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type')?.includes('application/json')) {\n body = (await request.json()) as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await handler(request, registrationId, userInfo, token);\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await handler(request, registrationId, userInfo, token);\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n sendEmailVerificationCode = async (\n request: Request,\n sender: (data: { email: string; verificationCode: string }) => Promise<void>\n ): Promise<Response> => {\n invariant(this.turnstileSecretKey, 'turnstileSecretKey is not configured');\n\n const { data, error } = valid(sendEmailVerificationCodeSchema, await request.json());\n if (error) return Response.json(error, { status: error.error.code });\n const { email, turnstileToken } = data;\n\n const result = await verifyTurnstileToken(this.turnstileSecretKey, turnstileToken);\n if (result.error) return Response.json(result.error, { status: result.error.error.code });\n\n try {\n const verificationCode = randomInt(1_000_000).toString().padStart(6, '0');\n await sender({ email, verificationCode });\n await this.kv.setItem(this.getEmailVerificationCodeKey(email), verificationCode, 10 * 60);\n return Response.json({});\n } catch (e) {\n console.error('Failed to send email verification code', e);\n return Response.json(invalidArgument('EMAIL_DELIVERY_FAILED'), { status: 400 });\n }\n };\n\n loginEmail = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n const { data, error } = valid(loginEmailSchema, await request.json());\n if (error) return Response.json(error, { status: error.error.code });\n const { email, verificationCode } = data;\n\n const key = this.getEmailVerificationCodeKey(email);\n const stored = await this.kv.getItem(key);\n if (\n typeof stored !== 'string' ||\n stored.length !== verificationCode.length ||\n !timingSafeEqual(Buffer.from(stored), Buffer.from(verificationCode))\n ) {\n return Response.json(invalidArgument('INVALID_VERIFICATION_CODE'), { status: 400 });\n }\n await this.kv.removeItem(key);\n const claims: StandardClaims = { sub: email, email, email_verified: true };\n const principal = await handler(request, 'EMAIL', { claims, data: {} });\n\n // save session\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n\n return response;\n };\n\n /**\n * Step 1 of the desktop / native app sign-in handshake. The caller already\n * completed normal sign-in on the web (so a cookie session exists); this\n * issues a 5-minute single-use code that the web page hands off to the\n * desktop client via a loopback redirect.\n *\n * PKCE (RFC 7636, S256 only): the desktop generates a random `code_verifier`\n * before opening the browser, derives `code_challenge = base64url(sha256(verifier))`,\n * and threads the challenge through the URL → web page → here. We pin\n * the challenge to the code in KV; `desktopExchange` enforces that the\n * caller knows the matching verifier. This blocks an attacker who only\n * intercepts the auth code (browser history, malicious extension, packet\n * capture on loopback) from exchanging it.\n *\n * Requires an authenticated cookie session — anonymous callers get 401.\n * Throttling / abuse protection on this endpoint is the API layer's job\n * (rate-limit per principal); the SDK doesn't gate it because the\n * upstream sign-in flow already passed CAPTCHA / OAuth / verification.\n */\n desktopAuthorize = async (request: Request): Promise<Response> => {\n const principal = await this.getPrincipal(request);\n if (!principal) {\n const body = {\n error: {\n code: 401,\n status: 'UNAUTHENTICATED',\n message: 'sign in first',\n details: [],\n },\n };\n return Response.json(body, { status: 401 });\n }\n\n let body: { code_challenge?: unknown; code_challenge_method?: unknown };\n try {\n body = (await request.json()) as {\n code_challenge?: unknown;\n code_challenge_method?: unknown;\n };\n } catch {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const codeChallenge = typeof body.code_challenge === 'string' ? body.code_challenge : null;\n if (\n !codeChallenge ||\n body.code_challenge_method !== 'S256' ||\n !PKCE_FORMAT.test(codeChallenge)\n ) {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const code = randomUUID();\n const record: DesktopCodeRecord = { name: principal.name, cc: codeChallenge };\n await this.kv.setItem(this.getDesktopCodeKey(code), JSON.stringify(record), 5 * 60);\n\n return Response.json({ code });\n };\n\n /**\n * Step 2 of the desktop / native app sign-in handshake. The desktop\n * client hands back the auth code it received via the loopback callback,\n * and we mint a fresh session for it — independent of the web cookie\n * session that originally `authorize`'d the code, so logging out on\n * either side doesn't cascade to the other.\n *\n * Cookie attributes: defaults to `SameSite=None; Secure` so the cookie\n * survives cross-site fetches from the desktop renderer's `file://` /\n * `app://` origin. The configured base `cookieOptions` provide\n * everything else (`domain`, `path`, `httpOnly`, `maxAge`); the override\n * is targeted at the SameSite/Secure pair only. In a non-HTTPS dev\n * backend this combination is rejected by Chromium — see the desktop\n * sign-in PRD for workarounds.\n *\n * No CSRF check needed — the request carries no cookie, the auth code\n * itself is the credential, and KV's single-use removal blocks replay.\n */\n desktopExchange = async (request: Request): Promise<Response> => {\n let body: { code?: unknown; code_verifier?: unknown };\n try {\n body = (await request.json()) as { code?: unknown; code_verifier?: unknown };\n } catch {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const code = typeof body.code === 'string' ? body.code : null;\n const verifier = typeof body.code_verifier === 'string' ? body.code_verifier : null;\n if (!code || !verifier || !PKCE_FORMAT.test(verifier)) {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const key = this.getDesktopCodeKey(code);\n const raw = await this.kv.getItem(key);\n if (typeof raw !== 'string') {\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n let record: DesktopCodeRecord;\n try {\n record = JSON.parse(raw) as DesktopCodeRecord;\n } catch {\n // Malformed KV record (shouldn't happen with our own writes) — drop it\n // and surface as an invalid code; the caller can retry the whole flow.\n await this.kv.removeItem(key);\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n\n // Verify PKCE: base64url(sha256(verifier)) === stored challenge.\n // Don't burn the code on a verifier mismatch — that would let an\n // attacker who races a network retry brick a legit exchange. The code\n // is single-use on success and 5-min TTL bounded; brute-forcing the\n // 256-bit verifier in that window is infeasible.\n const computed = createHash('sha256').update(verifier).digest();\n const stored = Buffer.from(record.cc, 'base64url');\n if (stored.length !== computed.length || !timingSafeEqual(computed, stored)) {\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n\n // One-time use: remove BEFORE creating the session so a concurrent\n // duplicate exchange (e.g. retry after timeout) sees the code as\n // consumed even if our session save races it.\n await this.kv.removeItem(key);\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, record.name);\n session.setAttribute('client_type', 'desktop');\n await this.repository.save(session);\n\n const response = Response.json({ ok: true });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), {\n ...this.cookieOptions,\n // Override sameSite/secure for desktop: the renderer's origin\n // (file:// / app://) is cross-site to the API, so SameSite=Lax/Strict\n // would block the cookie on subsequent fetches.\n sameSite: 'none',\n secure: true,\n maxAge,\n });\n return response;\n };\n\n sendPhoneVerificationCode = async (\n _request: Request,\n _sender: (data: { phone: string; verificationCode: string }) => Promise<void>\n ): Promise<Response> => {\n return Response.json({ error: 'not_implemented' }, { status: 501 });\n };\n\n loginPhone = async (_request: Request, _handler: AuthorizedHandler): Promise<Response> => {\n return Response.json({ error: 'not_implemented' }, { status: 501 });\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomInt, randomUUID, timingSafeEqual } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport type { Principal } from '../core/index';\nimport { loginEmailSchema, sendEmailVerificationCodeSchema } from '../email/schema';\nimport { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport type { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport type {\n NativeCredential,\n OAuth2AuthorizationRequest,\n PkceParameters,\n StandardClaims,\n} from '../oauth2/types';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport {\n type CookieOptions,\n deleteCookie,\n getCookie,\n param,\n query,\n setCookie,\n} from '../utils/http';\nimport { timing } from '../utils/timing';\nimport { invalidArgument, valid, verifyTurnstileToken } from '../utils/valid';\nimport { MAX_ATTRIBUTES_BYTES, attributesSize, bodyAttributes } from './attributes';\nimport type {\n AuthConfig,\n AuthService,\n AuthorizedHandler,\n LoggedHandler,\n OAuth2AuthorizationOptions,\n} from './types';\n\n// PKCE per RFC 7636: code_verifier / code_challenge are 43-128 chars from\n// the unreserved set. We restrict to base64url (drops \".\" and \"~\") since\n// that's what S256 produces and what callers should be sending; rejecting\n// the rest shrinks the accepted input shape without losing legit clients.\n// A 32-byte random verifier base64url-encodes to exactly 43 chars.\nconst PKCE_FORMAT = /^[A-Za-z0-9_-]{43,128}$/;\n\nfunction authorizationAttributes(\n attributes: OAuth2AuthorizationOptions['attributes']\n): Record<string, string> | undefined {\n if (!attributes) return undefined;\n const defined: Record<string, string> = {};\n for (const [key, value] of Object.entries(attributes)) {\n if (value !== undefined) defined[key] = value;\n }\n if (Object.keys(defined).length === 0) return undefined;\n // The host's own values, written with the session: oversized is a bug in the host, not a 400.\n const size = attributesSize(defined);\n invariant(\n size <= MAX_ATTRIBUTES_BYTES,\n `oauth2 authorization attributes are ${size} bytes, at most ${MAX_ATTRIBUTES_BYTES} allowed`\n );\n return defined;\n}\n\n/** Sign in with Apple answers with `response_mode=form_post`; a charset parameter may follow. */\nconst INVALID_ATTRIBUTES = {\n error: 'invalid_request',\n error_description: `attributes must map strings to strings, at most ${MAX_ATTRIBUTES_BYTES} bytes`,\n};\n\nfunction isFormPost(request: Request) {\n const contentType = request.headers.get('content-type')?.toLowerCase();\n return (\n request.method === 'POST' && !!contentType?.startsWith('application/x-www-form-urlencoded')\n );\n}\n\ninterface DesktopCodeRecord {\n /** principal.name from the upstream cookie session */\n name: string;\n /** code_challenge (S256, base64url) — verifier must hash to this */\n cc: string;\n}\n\nexport const PATH = {\n CSRF: '/csrf',\n LOGOUT: '/logout',\n LOGGED: '/logged',\n\n OAUTH2_STATE: '/oauth2/state/:registrationId',\n OAUTH2_NONCE: '/oauth2/nonce/:registrationId',\n OAUTH2_AUTHORIZATION: '/oauth2/authorization/:registrationId',\n LOGIN_OAUTH2_CODE: '/login/oauth2/code/:registrationId',\n LOGIN_OAUTH2_NATIVE: '/login/oauth2/native/:registrationId',\n LOGIN_OAUTH2_ONETAP: '/login/oauth2/onetap/google',\n\n LOGIN_EMAIL: '/login/email',\n LOGIN_PHONE: '/login/phone',\n SEND_EMAIL_VERIFICATION_CODE: '/auth/sendEmailVerificationCode',\n SEND_PHONE_VERIFICATION_CODE: '/auth/sendPhoneVerificationCode',\n\n // Desktop / native app sign-in via web loopback (RFC 8252 + OAuth 2.1 Auth Code Flow):\n // 1. user already authenticated with cookie session → POST DESKTOP_AUTHORIZE → { code }\n // 2. web redirects to desktop's loopback callback with the code\n // 3. desktop renderer POSTs DESKTOP_EXCHANGE with the code → server returns\n // Set-Cookie carrying a fresh session, single-use code is invalidated\n // The two-step flow keeps the actual session id off the redirect URL — only a\n // 5-minute single-use code transits via the loopback.\n DESKTOP_AUTHORIZE: '/auth/desktop/authorize',\n DESKTOP_EXCHANGE: '/auth/desktop/exchange',\n\n CLEANUP_EXPIRED_SESSIONS: '/sessions/expired/cleanup',\n} as const;\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n private readonly turnstileSecretKey?: string;\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n constructor({\n sessionRepository,\n kvRepository,\n turnstileSecretKey,\n oauth2,\n cookie,\n timing,\n }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n this.turnstileSecretKey = turnstileSecretKey;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n private getSessionId(request: Request): string | undefined {\n let sessionId: string | undefined;\n const header = request.headers.get('Authorization');\n if (header?.startsWith('Bearer ')) {\n sessionId = header.replace('Bearer ', '');\n }\n if (!sessionId) {\n sessionId = getCookie(request, this.cookieName);\n }\n return sessionId;\n }\n\n csrf = async (_request: Request): Promise<Response> => {\n const token = randomUUID();\n const response = Response.json({\n token,\n parameterName: '_csrf',\n headerName: 'X-XSRF-TOKEN',\n });\n\n // Double Submit Cookie Protection\n setCookie(response, 'XSRF-TOKEN', token, {\n path: '/',\n secure: true,\n httpOnly: false,\n sameSite: 'none',\n domain: this.cookieOptions.domain,\n });\n return response;\n };\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = this.getSessionId(request);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from header or cookie\n const sessionId = this.getSessionId(request);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n private getEmailVerificationCodeKey(email: string) {\n return `email:verification:${email}`;\n }\n\n private getPhoneVerificationCodeKey(phone: string) {\n return `phone:verification:${phone}`;\n }\n\n private getDesktopCodeKey(code: string) {\n return `desktop:code:${code}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, PATH.OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, PATH.OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (\n request: Request,\n options?: OAuth2AuthorizationOptions\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const attributes = authorizationAttributes(options?.attributes);\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, PATH.OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = await this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = this.getSessionId(request);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n ...(attributes ? { attributes } : {}),\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private readonly redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from header or cookie\n const sessionId = this.getSessionId(request);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, PATH.LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (isFormPost(request)) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal, with what the host attached when the flow started\n const principal = await handler(request, {\n registrationId,\n userInfo,\n token,\n attributes: cached.attributes ?? {},\n });\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const uri = new URL(this.oauth2Client.successUri);\n uri.searchParams.set('status', 'success');\n uri.searchParams.set('registrationId', registrationId);\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, PATH.LOGIN_OAUTH2_NATIVE);\n const body: unknown = await request.json();\n // Whatever the app sends besides its credentials for `onAuthorized` (a referral code, ...).\n const attributes = bodyAttributes(body);\n if (!attributes) return Response.json(INVALID_ATTRIBUTES, { status: 400 });\n // Only the credentials go on to the provider client.\n const { attributes: _, ...credentials } = body as NativeCredential & { attributes?: unknown };\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await handler(request, { registrationId, userInfo, token, attributes });\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n handler: AuthorizedHandler,\n registrationId = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n // Google's own form post carries no attributes; an app posting JSON may add them.\n let body: Record<string, string>;\n let attributes: Record<string, string> | null = {};\n if (request.headers.get('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type')?.includes('application/json')) {\n const json: unknown = await request.json();\n attributes = bodyAttributes(json);\n body = json as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n if (!attributes) return Response.json(INVALID_ATTRIBUTES, { status: 400 });\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await handler(request, { registrationId, userInfo, token, attributes });\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await handler(request, { registrationId, userInfo, token, attributes });\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n sendEmailVerificationCode = async (\n request: Request,\n sender: (data: { email: string; verificationCode: string }) => Promise<void>\n ): Promise<Response> => {\n invariant(this.turnstileSecretKey, 'turnstileSecretKey is not configured');\n\n const { data, error } = valid(sendEmailVerificationCodeSchema, await request.json());\n if (error) return Response.json(error, { status: error.error.code });\n const { email, turnstileToken } = data;\n\n const result = await verifyTurnstileToken(this.turnstileSecretKey, turnstileToken);\n if (result.error) return Response.json(result.error, { status: result.error.error.code });\n\n try {\n const verificationCode = randomInt(1_000_000).toString().padStart(6, '0');\n await sender({ email, verificationCode });\n await this.kv.setItem(this.getEmailVerificationCodeKey(email), verificationCode, 10 * 60);\n return Response.json({});\n } catch (e) {\n console.error('Failed to send email verification code', e);\n return Response.json(invalidArgument('EMAIL_DELIVERY_FAILED'), { status: 400 });\n }\n };\n\n loginEmail = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n const { data, error } = valid(loginEmailSchema, await request.json());\n if (error) return Response.json(error, { status: error.error.code });\n const { email, verificationCode, attributes } = data;\n\n const key = this.getEmailVerificationCodeKey(email);\n const stored = await this.kv.getItem(key);\n if (\n typeof stored !== 'string' ||\n stored.length !== verificationCode.length ||\n !timingSafeEqual(Buffer.from(stored), Buffer.from(verificationCode))\n ) {\n return Response.json(invalidArgument('INVALID_VERIFICATION_CODE'), { status: 400 });\n }\n await this.kv.removeItem(key);\n const claims: StandardClaims = { sub: email, email, email_verified: true };\n const principal = await handler(request, {\n registrationId: 'EMAIL',\n userInfo: { claims, data: {} },\n attributes: attributes ?? {},\n });\n\n // save session\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n\n return response;\n };\n\n /**\n * Step 1 of the desktop / native app sign-in handshake. The caller already\n * completed normal sign-in on the web (so a cookie session exists); this\n * issues a 5-minute single-use code that the web page hands off to the\n * desktop client via a loopback redirect.\n *\n * PKCE (RFC 7636, S256 only): the desktop generates a random `code_verifier`\n * before opening the browser, derives `code_challenge = base64url(sha256(verifier))`,\n * and threads the challenge through the URL → web page → here. We pin\n * the challenge to the code in KV; `desktopExchange` enforces that the\n * caller knows the matching verifier. This blocks an attacker who only\n * intercepts the auth code (browser history, malicious extension, packet\n * capture on loopback) from exchanging it.\n *\n * Requires an authenticated cookie session — anonymous callers get 401.\n * Throttling / abuse protection on this endpoint is the API layer's job\n * (rate-limit per principal); the SDK doesn't gate it because the\n * upstream sign-in flow already passed CAPTCHA / OAuth / verification.\n */\n desktopAuthorize = async (request: Request): Promise<Response> => {\n const principal = await this.getPrincipal(request);\n if (!principal) {\n const body = {\n error: {\n code: 401,\n status: 'UNAUTHENTICATED',\n message: 'sign in first',\n details: [],\n },\n };\n return Response.json(body, { status: 401 });\n }\n\n let body: { code_challenge?: unknown; code_challenge_method?: unknown };\n try {\n body = (await request.json()) as {\n code_challenge?: unknown;\n code_challenge_method?: unknown;\n };\n } catch {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const codeChallenge = typeof body.code_challenge === 'string' ? body.code_challenge : null;\n if (\n !codeChallenge ||\n body.code_challenge_method !== 'S256' ||\n !PKCE_FORMAT.test(codeChallenge)\n ) {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const code = randomUUID();\n const record: DesktopCodeRecord = { name: principal.name, cc: codeChallenge };\n await this.kv.setItem(this.getDesktopCodeKey(code), JSON.stringify(record), 5 * 60);\n\n return Response.json({ code });\n };\n\n /**\n * Step 2 of the desktop / native app sign-in handshake. The desktop\n * client hands back the auth code it received via the loopback callback,\n * and we mint a fresh session for it — independent of the web cookie\n * session that originally `authorize`'d the code, so logging out on\n * either side doesn't cascade to the other.\n *\n * Cookie attributes: defaults to `SameSite=None; Secure` so the cookie\n * survives cross-site fetches from the desktop renderer's `file://` /\n * `app://` origin. The configured base `cookieOptions` provide\n * everything else (`domain`, `path`, `httpOnly`, `maxAge`); the override\n * is targeted at the SameSite/Secure pair only. In a non-HTTPS dev\n * backend this combination is rejected by Chromium — see the desktop\n * sign-in PRD for workarounds.\n *\n * No CSRF check needed — the request carries no cookie, the auth code\n * itself is the credential, and KV's single-use removal blocks replay.\n */\n desktopExchange = async (request: Request): Promise<Response> => {\n let body: { code?: unknown; code_verifier?: unknown };\n try {\n body = (await request.json()) as { code?: unknown; code_verifier?: unknown };\n } catch {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const code = typeof body.code === 'string' ? body.code : null;\n const verifier = typeof body.code_verifier === 'string' ? body.code_verifier : null;\n if (!code || !verifier || !PKCE_FORMAT.test(verifier)) {\n return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n }\n\n const key = this.getDesktopCodeKey(code);\n const raw = await this.kv.getItem(key);\n if (typeof raw !== 'string') {\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n let record: DesktopCodeRecord;\n try {\n record = JSON.parse(raw) as DesktopCodeRecord;\n } catch {\n // Malformed KV record (shouldn't happen with our own writes) — drop it\n // and surface as an invalid code; the caller can retry the whole flow.\n await this.kv.removeItem(key);\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n\n // Verify PKCE: base64url(sha256(verifier)) === stored challenge.\n // Don't burn the code on a verifier mismatch — that would let an\n // attacker who races a network retry brick a legit exchange. The code\n // is single-use on success and 5-min TTL bounded; brute-forcing the\n // 256-bit verifier in that window is infeasible.\n const computed = createHash('sha256').update(verifier).digest();\n const stored = Buffer.from(record.cc, 'base64url');\n if (stored.length !== computed.length || !timingSafeEqual(computed, stored)) {\n return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n }\n\n // One-time use: remove BEFORE creating the session so a concurrent\n // duplicate exchange (e.g. retry after timeout) sees the code as\n // consumed even if our session save races it.\n await this.kv.removeItem(key);\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, record.name);\n session.setAttribute('client_type', 'desktop');\n await this.repository.save(session);\n\n const response = Response.json({ ok: true });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), {\n ...this.cookieOptions,\n // Override sameSite/secure for desktop: the renderer's origin\n // (file:// / app://) is cross-site to the API, so SameSite=Lax/Strict\n // would block the cookie on subsequent fetches.\n sameSite: 'none',\n secure: true,\n maxAge,\n });\n return response;\n };\n\n sendPhoneVerificationCode = async (\n _request: Request,\n _sender: (data: { phone: string; verificationCode: string }) => Promise<void>\n ): Promise<Response> => {\n return Response.json({ error: 'not_implemented' }, { status: 501 });\n };\n\n loginPhone = async (_request: Request, _handler: AuthorizedHandler): Promise<Response> => {\n return Response.json({ error: 'not_implemented' }, { status: 501 });\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = this.getSessionId(request);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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"}
|
package/dist/auth/types.d.cts
CHANGED
|
@@ -4,7 +4,37 @@ import { OAuth2ClientConfig, OAuth2Token, UserInfo } from "../oauth2/types.cjs";
|
|
|
4
4
|
import { CookieOptions } from "../utils/http.cjs";
|
|
5
5
|
//#region src/auth/types.d.ts
|
|
6
6
|
export type LoggedHandler = (principal: Principal) => Promise<void>;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Who signed in, and how: what every login method hands to `onAuthorized`.
|
|
9
|
+
*
|
|
10
|
+
* - `registrationId`: the oauth2 registration (`github`, `appleWeb`, ...), or `EMAIL` / `PHONE`.
|
|
11
|
+
* - `userInfo`: the provider's user info, or the verified email / phone as claims.
|
|
12
|
+
* - `token`: the provider's tokens; absent for email and phone sign-in.
|
|
13
|
+
* - `attributes`: what the host attached when it started a redirect flow with
|
|
14
|
+
* `oauth2Authorization(request, { attributes })`; empty for every other login method (native,
|
|
15
|
+
* One Tap, email), whose request the handler already has.
|
|
16
|
+
*/
|
|
17
|
+
export interface Authorization {
|
|
18
|
+
registrationId: string;
|
|
19
|
+
userInfo: UserInfo;
|
|
20
|
+
token?: OAuth2Token;
|
|
21
|
+
attributes: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/** Creates or updates the user and returns the principal whose `name` the session stores. */
|
|
24
|
+
export type AuthorizedHandler = (request: Request, authorization: Authorization) => Promise<Principal>;
|
|
25
|
+
export interface OAuth2AuthorizationOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Values to carry from the start of a redirect flow to its callback, e.g. a referral code read
|
|
28
|
+
* from a cookie or query parameter of the authorization request. Stored with the authorization
|
|
29
|
+
* request in the session (never sent to the provider), returned to `onAuthorized` as
|
|
30
|
+
* `authorization.attributes` once state and session check out, and removed with the request after a
|
|
31
|
+
* successful login. `undefined` values are dropped. At most 2 KB serialized.
|
|
32
|
+
*
|
|
33
|
+
* They come from the browser that started the flow, so trust them like a cookie: fine for
|
|
34
|
+
* attribution or where to go next, not for authorization decisions.
|
|
35
|
+
*/
|
|
36
|
+
attributes?: Record<string, string | undefined>;
|
|
37
|
+
}
|
|
8
38
|
export interface AuthConfig {
|
|
9
39
|
timing?: boolean;
|
|
10
40
|
kvRepository: KVRepository;
|
|
@@ -23,7 +53,7 @@ export interface AuthService {
|
|
|
23
53
|
logged: (request: Request, onLogged?: LoggedHandler) => Promise<Response>;
|
|
24
54
|
oauth2State: (request: Request) => Promise<Response>;
|
|
25
55
|
oauth2Nonce: (request: Request) => Promise<Response>;
|
|
26
|
-
oauth2Authorization: (request: Request) => Promise<Response>;
|
|
56
|
+
oauth2Authorization: (request: Request, options?: OAuth2AuthorizationOptions) => Promise<Response>;
|
|
27
57
|
loginOAuth2Code: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
28
58
|
loginOAuth2Native: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
29
59
|
loginOAuth2Onetap: (request: Request, onAuthorized: AuthorizedHandler, registrationId?: string) => Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAKY,iBAAiB,WAAW,cAAc;
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAKY,iBAAiB,WAAW,cAAc;;;;;;;;;;;iBAWrC;EACf;EACA,UAAU;EACV,QAAQ;EACR,YAAY;;;YAIF,qBACV,SAAS,SACT,eAAe,kBACZ,QAAQ;iBAEI;;;;;;;;;;;EAWf,aAAa;;iBAGE;EACf;EACA,cAAc;EACd,mBAAmB;EACnB,SAAS;IAAkB;;EAC3B;EACA;IACE,SAAS;;;iBAII;EAEf,OAAO,SAAS,YAAY,QAAQ;EACpC,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,SAAS,WAAW,kBAAkB,QAAQ;EAGhE,cAAc,SAAS,YAAY,QAAQ;EAC3C,cAAc,SAAS,YAAY,QAAQ;EAC3C,sBACE,SAAS,SACT,UAAU,+BACP,QAAQ;EACb,kBAAkB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAChF,oBAAoB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAClF,oBACE,SAAS,SACT,cAAc,mBACd,4BACG,QAAQ;EAGb,4BACE,SAAS,SACT,SAAS;IAAQ;IAAe;QAA+B,kBAC5D,QAAQ;EACb,4BACE,SAAS,SACT,SAAS;IAAQ;IAAe;QAA+B,kBAC5D,QAAQ;EAEb,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAC3E,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAG3E,OAAO,WAAW,cAAc;EAChC,kBAAkB,SAAS,YAAY;EACvC,aAAa,mBACX,SAAS,SACT,QAAQ,MACL,QAAQ,iBAAiB,UAAU;EACxC,gBAAgB,sBAAsB;EACtC,eAAe,SAAS,YAAY,QAAQ;EAC5C,eAAe,WAAW,cAAc,QAAQ;EAChD,yBAAyB,0BAA0B,QAAQ"}
|
package/dist/auth/types.d.mts
CHANGED
|
@@ -4,7 +4,37 @@ import { OAuth2ClientConfig, OAuth2Token, UserInfo } from "../oauth2/types.mjs";
|
|
|
4
4
|
import { CookieOptions } from "../utils/http.mjs";
|
|
5
5
|
//#region src/auth/types.d.ts
|
|
6
6
|
export type LoggedHandler = (principal: Principal) => Promise<void>;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Who signed in, and how: what every login method hands to `onAuthorized`.
|
|
9
|
+
*
|
|
10
|
+
* - `registrationId`: the oauth2 registration (`github`, `appleWeb`, ...), or `EMAIL` / `PHONE`.
|
|
11
|
+
* - `userInfo`: the provider's user info, or the verified email / phone as claims.
|
|
12
|
+
* - `token`: the provider's tokens; absent for email and phone sign-in.
|
|
13
|
+
* - `attributes`: what the host attached when it started a redirect flow with
|
|
14
|
+
* `oauth2Authorization(request, { attributes })`; empty for every other login method (native,
|
|
15
|
+
* One Tap, email), whose request the handler already has.
|
|
16
|
+
*/
|
|
17
|
+
export interface Authorization {
|
|
18
|
+
registrationId: string;
|
|
19
|
+
userInfo: UserInfo;
|
|
20
|
+
token?: OAuth2Token;
|
|
21
|
+
attributes: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/** Creates or updates the user and returns the principal whose `name` the session stores. */
|
|
24
|
+
export type AuthorizedHandler = (request: Request, authorization: Authorization) => Promise<Principal>;
|
|
25
|
+
export interface OAuth2AuthorizationOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Values to carry from the start of a redirect flow to its callback, e.g. a referral code read
|
|
28
|
+
* from a cookie or query parameter of the authorization request. Stored with the authorization
|
|
29
|
+
* request in the session (never sent to the provider), returned to `onAuthorized` as
|
|
30
|
+
* `authorization.attributes` once state and session check out, and removed with the request after a
|
|
31
|
+
* successful login. `undefined` values are dropped. At most 2 KB serialized.
|
|
32
|
+
*
|
|
33
|
+
* They come from the browser that started the flow, so trust them like a cookie: fine for
|
|
34
|
+
* attribution or where to go next, not for authorization decisions.
|
|
35
|
+
*/
|
|
36
|
+
attributes?: Record<string, string | undefined>;
|
|
37
|
+
}
|
|
8
38
|
export interface AuthConfig {
|
|
9
39
|
timing?: boolean;
|
|
10
40
|
kvRepository: KVRepository;
|
|
@@ -23,7 +53,7 @@ export interface AuthService {
|
|
|
23
53
|
logged: (request: Request, onLogged?: LoggedHandler) => Promise<Response>;
|
|
24
54
|
oauth2State: (request: Request) => Promise<Response>;
|
|
25
55
|
oauth2Nonce: (request: Request) => Promise<Response>;
|
|
26
|
-
oauth2Authorization: (request: Request) => Promise<Response>;
|
|
56
|
+
oauth2Authorization: (request: Request, options?: OAuth2AuthorizationOptions) => Promise<Response>;
|
|
27
57
|
loginOAuth2Code: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
28
58
|
loginOAuth2Native: (request: Request, onAuthorized: AuthorizedHandler) => Promise<Response>;
|
|
29
59
|
loginOAuth2Onetap: (request: Request, onAuthorized: AuthorizedHandler, registrationId?: string) => Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAKY,iBAAiB,WAAW,cAAc;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/auth/types.ts"],"mappings":";;;;;YAKY,iBAAiB,WAAW,cAAc;;;;;;;;;;;iBAWrC;EACf;EACA,UAAU;EACV,QAAQ;EACR,YAAY;;;YAIF,qBACV,SAAS,SACT,eAAe,kBACZ,QAAQ;iBAEI;;;;;;;;;;;EAWf,aAAa;;iBAGE;EACf;EACA,cAAc;EACd,mBAAmB;EACnB,SAAS;IAAkB;;EAC3B;EACA;IACE,SAAS;;;iBAII;EAEf,OAAO,SAAS,YAAY,QAAQ;EACpC,SAAS,SAAS,YAAY,QAAQ;EACtC,SAAS,SAAS,SAAS,WAAW,kBAAkB,QAAQ;EAGhE,cAAc,SAAS,YAAY,QAAQ;EAC3C,cAAc,SAAS,YAAY,QAAQ;EAC3C,sBACE,SAAS,SACT,UAAU,+BACP,QAAQ;EACb,kBAAkB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAChF,oBAAoB,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAClF,oBACE,SAAS,SACT,cAAc,mBACd,4BACG,QAAQ;EAGb,4BACE,SAAS,SACT,SAAS;IAAQ;IAAe;QAA+B,kBAC5D,QAAQ;EACb,4BACE,SAAS,SACT,SAAS;IAAQ;IAAe;QAA+B,kBAC5D,QAAQ;EAEb,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAC3E,aAAa,SAAS,SAAS,cAAc,sBAAsB,QAAQ;EAG3E,OAAO,WAAW,cAAc;EAChC,kBAAkB,SAAS,YAAY;EACvC,aAAa,mBACX,SAAS,SACT,QAAQ,MACL,QAAQ,iBAAiB,UAAU;EACxC,gBAAgB,sBAAsB;EACtC,eAAe,SAAS,YAAY,QAAQ;EAC5C,eAAe,WAAW,cAAc,QAAQ;EAChD,yBAAyB,0BAA0B,QAAQ"}
|
package/dist/email/schema.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_auth_attributes = require("../auth/attributes.cjs");
|
|
2
3
|
let zod_mini = require("zod/mini");
|
|
3
4
|
//#region src/email/schema.ts
|
|
4
5
|
const sendEmailVerificationCodeSchema = (0, zod_mini.object)({
|
|
@@ -7,7 +8,8 @@ const sendEmailVerificationCodeSchema = (0, zod_mini.object)({
|
|
|
7
8
|
});
|
|
8
9
|
const loginEmailSchema = (0, zod_mini.object)({
|
|
9
10
|
email: (0, zod_mini.email)().check((0, zod_mini.maxLength)(320)),
|
|
10
|
-
verificationCode: (0, zod_mini.string)().check((0, zod_mini.regex)(/^\d{6}$/))
|
|
11
|
+
verificationCode: (0, zod_mini.string)().check((0, zod_mini.regex)(/^\d{6}$/)),
|
|
12
|
+
attributes: require_auth_attributes.attributesSchema
|
|
11
13
|
});
|
|
12
14
|
//#endregion
|
|
13
15
|
exports.loginEmailSchema = loginEmailSchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.cjs","names":["object","email","maxLength","string","regex"],"sources":["../../src/email/schema.ts"],"sourcesContent":["import { email, maxLength, object, regex, string } from 'zod/mini';\n\nexport const sendEmailVerificationCodeSchema = object({\n email: email().check(maxLength(320)),\n turnstileToken: string().check(maxLength(2048)),\n});\n\nexport const loginEmailSchema = object({\n email: email().check(maxLength(320)),\n verificationCode: string().check(regex(/^\\d{6}$/)),\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.cjs","names":["object","email","maxLength","string","regex","attributesSchema"],"sources":["../../src/email/schema.ts"],"sourcesContent":["import { email, maxLength, object, regex, string } from 'zod/mini';\nimport { attributesSchema } from '../auth/attributes';\n\nexport const sendEmailVerificationCodeSchema = object({\n email: email().check(maxLength(320)),\n turnstileToken: string().check(maxLength(2048)),\n});\n\nexport const loginEmailSchema = object({\n email: email().check(maxLength(320)),\n verificationCode: string().check(regex(/^\\d{6}$/)),\n attributes: attributesSchema,\n});\n"],"mappings":";;;;AAGA,MAAa,mCAAA,GAAkCA,SAAAA,OAAAA,CAAO;CACpD,QAAA,GAAOC,SAAAA,MAAAA,CAAM,CAAC,CAAC,OAAA,GAAMC,SAAAA,UAAAA,CAAU,GAAG,CAAC;CACnC,iBAAA,GAAgBC,SAAAA,OAAAA,CAAO,CAAC,CAAC,OAAA,GAAMD,SAAAA,UAAAA,CAAU,IAAI,CAAC;AAChD,CAAC;AAED,MAAa,oBAAA,GAAmBF,SAAAA,OAAAA,CAAO;CACrC,QAAA,GAAOC,SAAAA,MAAAA,CAAM,CAAC,CAAC,OAAA,GAAMC,SAAAA,UAAAA,CAAU,GAAG,CAAC;CACnC,mBAAA,GAAkBC,SAAAA,OAAAA,CAAO,CAAC,CAAC,OAAA,GAAMC,SAAAA,MAAAA,CAAM,SAAS,CAAC;CACjD,YAAYC,wBAAAA;AACd,CAAC"}
|
package/dist/email/schema.d.cts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const sendEmailVerificationCodeSchema: import("zod/mini").ZodMini
|
|
|
6
6
|
export declare const loginEmailSchema: import("zod/mini").ZodMiniObject<{
|
|
7
7
|
email: import("zod/mini").ZodMiniEmail;
|
|
8
8
|
verificationCode: import("zod/mini").ZodMiniString<string>;
|
|
9
|
+
attributes: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>>;
|
|
9
10
|
}, import("zod/v4/core").$strip>;
|
|
10
11
|
//#endregion
|
|
11
12
|
//# sourceMappingURL=schema.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/email/schema.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"schema.d.cts","names":[],"sources":["../../src/email/schema.ts"],"mappings":";qBAGa,oDAA+B;;;yBAG1C;qBAEW,qCAAgB;;;;yBAI3B"}
|
package/dist/email/schema.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ export declare const sendEmailVerificationCodeSchema: import("zod/mini").ZodMini
|
|
|
6
6
|
export declare const loginEmailSchema: import("zod/mini").ZodMiniObject<{
|
|
7
7
|
email: import("zod/mini").ZodMiniEmail;
|
|
8
8
|
verificationCode: import("zod/mini").ZodMiniString<string>;
|
|
9
|
+
attributes: import("zod/mini").ZodMiniOptional<import("zod/mini").ZodMiniRecord<import("zod/mini").ZodMiniString<string>, import("zod/mini").ZodMiniString<string>>>;
|
|
9
10
|
}, import("zod/v4/core").$strip>;
|
|
10
11
|
//#endregion
|
|
11
12
|
//# sourceMappingURL=schema.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/email/schema.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"schema.d.mts","names":[],"sources":["../../src/email/schema.ts"],"mappings":";qBAGa,oDAA+B;;;yBAG1C;qBAEW,qCAAgB;;;;yBAI3B"}
|
package/dist/email/schema.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { attributesSchema } from "../auth/attributes.mjs";
|
|
1
2
|
import { email, maxLength, object, regex, string } from "zod/mini";
|
|
2
3
|
//#region src/email/schema.ts
|
|
3
4
|
const sendEmailVerificationCodeSchema = object({
|
|
@@ -6,7 +7,8 @@ const sendEmailVerificationCodeSchema = object({
|
|
|
6
7
|
});
|
|
7
8
|
const loginEmailSchema = object({
|
|
8
9
|
email: email().check(maxLength(320)),
|
|
9
|
-
verificationCode: string().check(regex(/^\d{6}$/))
|
|
10
|
+
verificationCode: string().check(regex(/^\d{6}$/)),
|
|
11
|
+
attributes: attributesSchema
|
|
10
12
|
});
|
|
11
13
|
//#endregion
|
|
12
14
|
export { loginEmailSchema, sendEmailVerificationCodeSchema };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.mjs","names":[],"sources":["../../src/email/schema.ts"],"sourcesContent":["import { email, maxLength, object, regex, string } from 'zod/mini';\n\nexport const sendEmailVerificationCodeSchema = object({\n email: email().check(maxLength(320)),\n turnstileToken: string().check(maxLength(2048)),\n});\n\nexport const loginEmailSchema = object({\n email: email().check(maxLength(320)),\n verificationCode: string().check(regex(/^\\d{6}$/)),\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema.mjs","names":[],"sources":["../../src/email/schema.ts"],"sourcesContent":["import { email, maxLength, object, regex, string } from 'zod/mini';\nimport { attributesSchema } from '../auth/attributes';\n\nexport const sendEmailVerificationCodeSchema = object({\n email: email().check(maxLength(320)),\n turnstileToken: string().check(maxLength(2048)),\n});\n\nexport const loginEmailSchema = object({\n email: email().check(maxLength(320)),\n verificationCode: string().check(regex(/^\\d{6}$/)),\n attributes: attributesSchema,\n});\n"],"mappings":";;;AAGA,MAAa,kCAAkC,OAAO;CACpD,OAAO,MAAM,CAAC,CAAC,MAAM,UAAU,GAAG,CAAC;CACnC,gBAAgB,OAAO,CAAC,CAAC,MAAM,UAAU,IAAI,CAAC;AAChD,CAAC;AAED,MAAa,mBAAmB,OAAO;CACrC,OAAO,MAAM,CAAC,CAAC,MAAM,UAAU,GAAG,CAAC;CACnC,kBAAkB,OAAO,CAAC,CAAC,MAAM,MAAM,SAAS,CAAC;CACjD,YAAY;AACd,CAAC"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { CsrfToken, PROVIDERS, Principal, Provider } from "./core/index.cjs";
|
|
2
2
|
import { KVRepository, Namespace, Session, SessionRepository } from "./session/types.cjs";
|
|
3
3
|
import { NativeCredential, OAuth2Token, OIDC_SCOPES, OidcScope, OidcToken, StandardClaims, UserInfo } from "./oauth2/types.cjs";
|
|
4
|
-
import { AuthConfig, AuthService } from "./auth/types.cjs";
|
|
4
|
+
import { AuthConfig, AuthService, Authorization, AuthorizedHandler, OAuth2AuthorizationOptions } from "./auth/types.cjs";
|
|
5
5
|
import { Auth, PATH } from "./auth/index.cjs";
|
|
6
6
|
import { OAuth2Client, oauth2RedirectQuerySchema } from "./oauth2/client.cjs";
|
|
7
7
|
import { OAuth2Error, OAuth2ErrorType } from "./oauth2/error.cjs";
|
|
8
8
|
import { RedisIndexedSessionRepository, RedisKVRepository } from "./session/redis.cjs";
|
|
9
9
|
import { DBAdapter, DBIndexedSessionRepository } from "./session/db.cjs";
|
|
10
10
|
import { PRINCIPAL_NAME_INDEX_NAME } from "./session/common.cjs";
|
|
11
|
-
export { Auth, type AuthConfig, type AuthService, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type KVRepository, type Namespace, type NativeCredential, OAuth2Client, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type Principal, type Provider, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, oauth2RedirectQuerySchema };
|
|
11
|
+
export { Auth, type AuthConfig, type AuthService, type Authorization, type AuthorizedHandler, type CsrfToken, type DBAdapter, DBIndexedSessionRepository, type KVRepository, type Namespace, type NativeCredential, type OAuth2AuthorizationOptions, OAuth2Client, OAuth2Error, type OAuth2ErrorType, type OAuth2Token, OIDC_SCOPES, type OidcScope, type OidcToken, PATH, PRINCIPAL_NAME_INDEX_NAME, PROVIDERS, type Principal, type Provider, RedisIndexedSessionRepository, RedisKVRepository, type Session, type SessionRepository, type StandardClaims, type UserInfo, oauth2RedirectQuerySchema };
|