@shware/security 2.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/auth/index.cjs +95 -26
  2. package/dist/auth/index.cjs.map +1 -1
  3. package/dist/auth/index.d.cts +34 -17
  4. package/dist/auth/index.d.ts +34 -17
  5. package/dist/auth/index.mjs +93 -25
  6. package/dist/auth/index.mjs.map +1 -1
  7. package/dist/auth/types.cjs.map +1 -1
  8. package/dist/auth/types.d.cts +16 -5
  9. package/dist/auth/types.d.ts +16 -5
  10. package/dist/email/schema.cjs +41 -0
  11. package/dist/email/schema.cjs.map +1 -0
  12. package/dist/email/schema.d.cts +13 -0
  13. package/dist/email/schema.d.ts +13 -0
  14. package/dist/email/schema.mjs +15 -0
  15. package/dist/email/schema.mjs.map +1 -0
  16. package/dist/index.cjs +2 -0
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +3 -3
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.mjs +2 -1
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/oauth2/client.d.cts +17 -17
  23. package/dist/oauth2/client.d.ts +17 -17
  24. package/dist/oauth2/provider/apple.cjs.map +1 -1
  25. package/dist/oauth2/provider/apple.mjs.map +1 -1
  26. package/dist/oauth2/provider/common.cjs.map +1 -1
  27. package/dist/oauth2/provider/common.mjs.map +1 -1
  28. package/dist/oauth2/provider/facebook.cjs.map +1 -1
  29. package/dist/oauth2/provider/facebook.mjs.map +1 -1
  30. package/dist/oauth2/provider/github.cjs.map +1 -1
  31. package/dist/oauth2/provider/github.mjs.map +1 -1
  32. package/dist/oauth2/provider/google.cjs.map +1 -1
  33. package/dist/oauth2/provider/google.mjs.map +1 -1
  34. package/dist/oauth2/provider/wechat.cjs.map +1 -1
  35. package/dist/oauth2/provider/wechat.mjs.map +1 -1
  36. package/dist/oauth2/provider/x.cjs.map +1 -1
  37. package/dist/oauth2/provider/x.mjs.map +1 -1
  38. package/dist/session/__tests__/redis.test.cjs +16 -11
  39. package/dist/session/__tests__/redis.test.cjs.map +1 -1
  40. package/dist/session/__tests__/redis.test.mjs +16 -11
  41. package/dist/session/__tests__/redis.test.mjs.map +1 -1
  42. package/dist/session/db.cjs.map +1 -1
  43. package/dist/session/db.d.cts +1 -1
  44. package/dist/session/db.d.ts +1 -1
  45. package/dist/session/db.mjs.map +1 -1
  46. package/dist/session/redis.cjs.map +1 -1
  47. package/dist/session/redis.d.cts +2 -2
  48. package/dist/session/redis.d.ts +2 -2
  49. package/dist/session/redis.mjs.map +1 -1
  50. package/dist/utils/valid.cjs +78 -0
  51. package/dist/utils/valid.cjs.map +1 -0
  52. package/dist/utils/valid.d.cts +27 -0
  53. package/dist/utils/valid.d.ts +27 -0
  54. package/dist/utils/valid.mjs +51 -0
  55. package/dist/utils/valid.mjs.map +1 -0
  56. package/package.json +4 -4
@@ -20,38 +20,55 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/auth/index.ts
21
21
  var auth_exports = {};
22
22
  __export(auth_exports, {
23
- Auth: () => Auth
23
+ Auth: () => Auth,
24
+ PATH: () => PATH
24
25
  });
25
26
  module.exports = __toCommonJS(auth_exports);
26
- var import_crypto = require("crypto");
27
27
  var import_utils = require("@shware/utils");
28
+ var import_crypto = require("crypto");
29
+ var import_core = require("../core.cjs");
30
+ var import_schema = require("../email/schema.cjs");
28
31
  var import_client = require("../oauth2/client.cjs");
29
32
  var import_provider = require("../oauth2/provider/index.cjs");
30
33
  var import_common = require("../session/common.cjs");
31
34
  var import_http = require("../utils/http.cjs");
32
35
  var import_timing = require("../utils/timing.cjs");
36
+ var import_valid = require("../utils/valid.cjs");
37
+ var PATH = {
38
+ CSRF: "/csrf",
39
+ LOGOUT: "/logout",
40
+ LOGGED: "/logged",
41
+ OAUTH2_STATE: "/oauth2/state/:registrationId",
42
+ OAUTH2_NONCE: "/oauth2/nonce/:registrationId",
43
+ OAUTH2_AUTHORIZATION: "/oauth2/authorization/:registrationId",
44
+ LOGIN_EMAIL: "/login/email",
45
+ LOGIN_PHONE: "/login/phone",
46
+ LOGIN_OAUTH2_CODE: "/login/oauth2/code/:registrationId",
47
+ LOGIN_OAUTH2_NATIVE: "/login/oauth2/native/:registrationId",
48
+ LOGIN_OAUTH2_ONETAP: "/login/oauth2/onetap/google",
49
+ CLEANUP_EXPIRED_SESSIONS: "/sessions/expired/cleanup"
50
+ };
33
51
  var Auth = class {
34
52
  timing;
35
53
  cookieName;
36
54
  cookieOptions;
37
55
  kv;
38
56
  repository;
57
+ turnstileSecretKey;
39
58
  oauth2Client;
40
59
  ATTR_OAUTH2_AUTHORIZATION_REQUEST = "oauth2AuthorizationRequest";
41
- PATH_CSRF = "/csrf";
42
- PATH_LOGOUT = "/logout";
43
- PATH_LOGGED = "/logged";
44
- PATH_OAUTH2_STATE = "/oauth2/state/:registrationId";
45
- PATH_OAUTH2_NONCE = "/oauth2/nonce/:registrationId";
46
- PATH_OAUTH2_AUTHORIZATION = "/oauth2/authorization/:registrationId";
47
- PATH_LOGIN_OAUTH2_CODE = "/login/oauth2/code/:registrationId";
48
- PATH_LOGIN_OAUTH2_NATIVE = "/login/oauth2/native/:registrationId";
49
- PATH_LOGIN_OAUTH2_ONETAP = "/login/oauth2/onetap/google";
50
- PATH_CLEANUP_EXPIRED_SESSIONS = "/sessions/expired/cleanup";
51
- constructor({ sessionRepository, kvRepository, oauth2, cookie, timing: timing2 }) {
60
+ constructor({
61
+ sessionRepository,
62
+ kvRepository,
63
+ turnstileSecretKey,
64
+ oauth2,
65
+ cookie,
66
+ timing: timing2
67
+ }) {
52
68
  this.timing = timing2 ?? false;
53
69
  this.kv = kvRepository;
54
70
  this.repository = sessionRepository;
71
+ this.turnstileSecretKey = turnstileSecretKey;
55
72
  const { name, ...cookieOptions } = cookie ?? {};
56
73
  this.cookieName = name ?? "SESSION";
57
74
  this.cookieOptions = {
@@ -122,14 +139,20 @@ var Auth = class {
122
139
  getOauth2NonceKey(nonce) {
123
140
  return `oauth2:nonce:${nonce}`;
124
141
  }
142
+ getEmailVerificationCodeKey(email) {
143
+ return `email:verification:${email}`;
144
+ }
145
+ getPhoneVerificationCodeKey(phone) {
146
+ return `phone:verification:${phone}`;
147
+ }
125
148
  oauth2State = async (request) => {
126
- const { registrationId } = (0, import_http.param)(request, this.PATH_OAUTH2_STATE);
149
+ const { registrationId } = (0, import_http.param)(request, PATH.OAUTH2_STATE);
127
150
  const state = (0, import_crypto.randomUUID)();
128
151
  await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);
129
152
  return Response.json({ state });
130
153
  };
131
154
  oauth2Nonce = async (request) => {
132
- const { registrationId } = (0, import_http.param)(request, this.PATH_OAUTH2_NONCE);
155
+ const { registrationId } = (0, import_http.param)(request, PATH.OAUTH2_NONCE);
133
156
  const nonce = (0, import_crypto.randomUUID)();
134
157
  await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);
135
158
  return Response.json({ nonce });
@@ -137,7 +160,7 @@ var Auth = class {
137
160
  oauth2Authorization = async (request) => {
138
161
  (0, import_utils.invariant)(this.oauth2Client, "oauth2Client is not initialized");
139
162
  const { mark, setTiming } = (0, import_timing.timing)({ enabled: this.timing });
140
- const { registrationId } = (0, import_http.param)(request, this.PATH_OAUTH2_AUTHORIZATION);
163
+ const { registrationId } = (0, import_http.param)(request, PATH.OAUTH2_AUTHORIZATION);
141
164
  const state = (0, import_crypto.randomUUID)();
142
165
  const pkce = this.createPkceParameters();
143
166
  const uri = await this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });
@@ -167,7 +190,7 @@ var Auth = class {
167
190
  if (description) uri.searchParams.set("error_description", description);
168
191
  return Response.redirect(uri.href, 302);
169
192
  };
170
- loginOAuth2Code = async (request, onAuthorized) => {
193
+ loginOAuth2Code = async (request, handler) => {
171
194
  (0, import_utils.invariant)(this.oauth2Client, "oauth2Client is not initialized");
172
195
  const { mark, setTiming } = (0, import_timing.timing)({ enabled: this.timing });
173
196
  const sessionId = this.getSessionId(request);
@@ -181,7 +204,7 @@ var Auth = class {
181
204
  return this.redirect("invalid_request", "authorization request not found");
182
205
  }
183
206
  const cached = JSON.parse(json);
184
- const { registrationId } = (0, import_http.param)(request, this.PATH_LOGIN_OAUTH2_CODE);
207
+ const { registrationId } = (0, import_http.param)(request, PATH.LOGIN_OAUTH2_CODE);
185
208
  let data;
186
209
  if (request.method === "POST" && request.headers.get("content-type")?.toLowerCase() === "application/x-www-form-urlencoded") {
187
210
  const params = new URLSearchParams(await request.text());
@@ -211,7 +234,7 @@ var Auth = class {
211
234
  });
212
235
  const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });
213
236
  mark("exchange_code");
214
- const principal = await onAuthorized(request, registrationId, userInfo, token);
237
+ const principal = await handler(request, registrationId, userInfo, token);
215
238
  mark("save_principal");
216
239
  session.setLastAccessedTime(Date.now());
217
240
  session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);
@@ -227,10 +250,10 @@ var Auth = class {
227
250
  setTiming(response);
228
251
  return response;
229
252
  };
230
- loginOAuth2Native = async (request, onAuthorized) => {
253
+ loginOAuth2Native = async (request, handler) => {
231
254
  (0, import_utils.invariant)(this.oauth2Client, "oauth2Client is not initialized");
232
255
  const { mark, setTiming } = (0, import_timing.timing)({ enabled: this.timing });
233
- const { registrationId } = (0, import_http.param)(request, this.PATH_LOGIN_OAUTH2_NATIVE);
256
+ const { registrationId } = (0, import_http.param)(request, PATH.LOGIN_OAUTH2_NATIVE);
234
257
  const credentials = await request.json();
235
258
  const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({
236
259
  registrationId,
@@ -247,7 +270,7 @@ var Auth = class {
247
270
  await this.kv.removeItem(this.getOauth2NonceKey(nonce));
248
271
  mark("check_nonce");
249
272
  }
250
- const principal = await onAuthorized(request, registrationId, userInfo, token);
273
+ const principal = await handler(request, registrationId, userInfo, token);
251
274
  mark("save_principal");
252
275
  const session = this.repository.createSession();
253
276
  session.setAttribute(import_common.PRINCIPAL_NAME_INDEX_NAME, principal.name);
@@ -261,7 +284,7 @@ var Auth = class {
261
284
  setTiming(response);
262
285
  return response;
263
286
  };
264
- loginOAuth2Onetap = async (request, onAuthorized, registrationId = "google") => {
287
+ loginOAuth2Onetap = async (request, handler, registrationId = "google") => {
265
288
  (0, import_utils.invariant)(this.oauth2Client, "oauth2Client is not initialized");
266
289
  const { mark, setTiming } = (0, import_timing.timing)({ enabled: this.timing });
267
290
  let body;
@@ -278,14 +301,14 @@ var Auth = class {
278
301
  let principal;
279
302
  if (parsed.credential) {
280
303
  const { token, userInfo } = await import_provider.google.getTokenInfo(parsed.credential);
281
- principal = await onAuthorized(request, registrationId, userInfo, token);
304
+ principal = await handler(request, registrationId, userInfo, token);
282
305
  } else if (parsed.code) {
283
306
  const token = await this.oauth2Client.exchangeAuthorizationCode({
284
307
  registrationId,
285
308
  code: parsed.code
286
309
  });
287
310
  const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });
288
- principal = await onAuthorized(request, registrationId, userInfo, token);
311
+ principal = await handler(request, registrationId, userInfo, token);
289
312
  } else {
290
313
  throw new Error("invalid request");
291
314
  }
@@ -302,6 +325,51 @@ var Auth = class {
302
325
  setTiming(response);
303
326
  return response;
304
327
  };
328
+ sendEmailVerificationCode = async (request, sender) => {
329
+ (0, import_utils.invariant)(this.turnstileSecretKey, "turnstileSecretKey is not configured");
330
+ const { data, error } = (0, import_valid.valid)(import_schema.sendEmailVerificationCodeSchema, await request.json());
331
+ if (error) return Response.json(error, { status: error.error.code });
332
+ const { email, turnstileToken } = data;
333
+ const result = await (0, import_valid.verifyTurnstileToken)(this.turnstileSecretKey, turnstileToken);
334
+ if (result.error) return Response.json(result.error, { status: result.error.error.code });
335
+ try {
336
+ const verificationCode = (0, import_crypto.randomInt)(1e6).toString().padStart(6, "0");
337
+ await sender({ email, verificationCode });
338
+ await this.kv.setItem(this.getEmailVerificationCodeKey(email), verificationCode, 10 * 60);
339
+ return Response.json({});
340
+ } catch {
341
+ return Response.json((0, import_valid.invalidArgument)("EMAIL_DELIVERY_FAILED"), { status: 400 });
342
+ }
343
+ };
344
+ loginEmail = async (request, handler) => {
345
+ const { data, error } = (0, import_valid.valid)(import_schema.loginEmailSchema, await request.json());
346
+ if (error) return Response.json(error, { status: error.error.code });
347
+ const { email, verificationCode } = data;
348
+ const key = this.getEmailVerificationCodeKey(email);
349
+ const stored = await this.kv.getItem(key);
350
+ if (typeof stored !== "string" || stored.length !== verificationCode.length || !(0, import_crypto.timingSafeEqual)(Buffer.from(stored), Buffer.from(verificationCode))) {
351
+ return Response.json((0, import_valid.invalidArgument)("INVALID_VERIFICATION_CODE"), { status: 400 });
352
+ }
353
+ await this.kv.removeItem(key);
354
+ const claims = { sub: email, email, email_verified: true };
355
+ const principal = await handler(request, import_core.Provider.EMAIL, { claims, data: {} });
356
+ const session = this.repository.createSession();
357
+ session.setAttribute(import_common.PRINCIPAL_NAME_INDEX_NAME, principal.name);
358
+ await this.repository.save(session);
359
+ const response = new Response(JSON.stringify(principal), {
360
+ status: 200,
361
+ headers: { "Content-Type": "application/json" }
362
+ });
363
+ const maxAge = session.getMaxInactiveInterval();
364
+ (0, import_http.setCookie)(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });
365
+ return response;
366
+ };
367
+ sendPhoneVerificationCode = async (_request, _sender) => {
368
+ return Response.json({ error: "not_implemented" }, { status: 501 });
369
+ };
370
+ loginPhone = async (_request, _handler) => {
371
+ return Response.json({ error: "not_implemented" }, { status: 501 });
372
+ };
305
373
  kick = async (principal) => {
306
374
  const sessions = await this.repository.findByPrincipalName(principal.name);
307
375
  for (const sessionId of sessions.keys()) {
@@ -363,6 +431,7 @@ var Auth = class {
363
431
  };
364
432
  // Annotate the CommonJS export names for ESM import in node:
365
433
  0 && (module.exports = {
366
- Auth
434
+ Auth,
435
+ PATH
367
436
  });
368
437
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport type { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport {\n type CookieOptions,\n deleteCookie,\n getCookie,\n param,\n query,\n setCookie,\n} from '../utils/http';\nimport { timing } from '../utils/timing';\nimport type { AuthConfig, AuthService, LoggedHandler, OAuth2AuthorizedHandler } from './types';\nimport type { Principal } from '../core';\nimport type { NativeCredential, OAuth2AuthorizationRequest, PkceParameters } from '../oauth2/types';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\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\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n public readonly PATH_CSRF = '/csrf' as const;\n public readonly PATH_LOGOUT = '/logout' as const;\n public readonly PATH_LOGGED = '/logged' as const;\n\n public readonly PATH_OAUTH2_STATE = '/oauth2/state/:registrationId' as const;\n public readonly PATH_OAUTH2_NONCE = '/oauth2/nonce/:registrationId' as const;\n public readonly PATH_OAUTH2_AUTHORIZATION = '/oauth2/authorization/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_CODE = '/login/oauth2/code/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_NATIVE = '/login/oauth2/native/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_ONETAP = '/login/oauth2/onetap/google' as const;\n\n public readonly PATH_CLEANUP_EXPIRED_SESSIONS = '/sessions/expired/cleanup' as const;\n\n constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\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 oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.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, this.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, this.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 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 (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): 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, this.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 onAuthorized(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 (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.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 onAuthorized(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 onAuthorized: OAuth2AuthorizedHandler,\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 onAuthorized(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 onAuthorized(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 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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","names":["timing","session"]}
1
+ {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { createHash, randomInt, randomUUID, timingSafeEqual } from 'crypto';\nimport type { OAuth2ErrorType } from '../oauth2/error';\nimport type {\n NativeCredential,\n OAuth2AuthorizationRequest,\n PkceParameters,\n StandardClaims,\n} from '../oauth2/types';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport type { AuthConfig, AuthService, AuthorizedHandler, LoggedHandler } from './types';\nimport { type Principal, Provider } from '../core';\nimport { loginEmailSchema, sendEmailVerificationCodeSchema } from '../email/schema';\nimport { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport { google } from '../oauth2/provider/index';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\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';\n\nexport const PATH = {\n CSRF: '/csrf',\n LOGOUT: '/logout',\n LOGGED: '/logged',\n OAUTH2_STATE: '/oauth2/state/:registrationId',\n OAUTH2_NONCE: '/oauth2/nonce/:registrationId',\n OAUTH2_AUTHORIZATION: '/oauth2/authorization/:registrationId',\n LOGIN_EMAIL: '/login/email',\n LOGIN_PHONE: '/login/phone',\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 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 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 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 {\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, Provider.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 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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","names":["timing","session"]}
@@ -1,29 +1,34 @@
1
- import { AuthService, AuthConfig, LoggedHandler, OAuth2AuthorizedHandler } from './types.cjs';
2
- import { Principal } from '../core/index.cjs';
3
1
  import { Session } from '../session/types.cjs';
2
+ import { AuthService, AuthConfig, LoggedHandler, AuthorizedHandler } from './types.cjs';
3
+ import { Principal } from '../core/index.cjs';
4
4
  import '../oauth2/types.cjs';
5
5
  import '../utils/http.cjs';
6
6
  import 'cookie';
7
7
 
8
+ declare const PATH: {
9
+ readonly CSRF: "/csrf";
10
+ readonly LOGOUT: "/logout";
11
+ readonly LOGGED: "/logged";
12
+ readonly OAUTH2_STATE: "/oauth2/state/:registrationId";
13
+ readonly OAUTH2_NONCE: "/oauth2/nonce/:registrationId";
14
+ readonly OAUTH2_AUTHORIZATION: "/oauth2/authorization/:registrationId";
15
+ readonly LOGIN_EMAIL: "/login/email";
16
+ readonly LOGIN_PHONE: "/login/phone";
17
+ readonly LOGIN_OAUTH2_CODE: "/login/oauth2/code/:registrationId";
18
+ readonly LOGIN_OAUTH2_NATIVE: "/login/oauth2/native/:registrationId";
19
+ readonly LOGIN_OAUTH2_ONETAP: "/login/oauth2/onetap/google";
20
+ readonly CLEANUP_EXPIRED_SESSIONS: "/sessions/expired/cleanup";
21
+ };
8
22
  declare class Auth implements AuthService {
9
23
  private readonly timing;
10
24
  private readonly cookieName;
11
25
  private readonly cookieOptions;
12
26
  private readonly kv;
13
27
  private readonly repository;
28
+ private readonly turnstileSecretKey?;
14
29
  private readonly oauth2Client;
15
30
  private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST;
16
- readonly PATH_CSRF: "/csrf";
17
- readonly PATH_LOGOUT: "/logout";
18
- readonly PATH_LOGGED: "/logged";
19
- readonly PATH_OAUTH2_STATE: "/oauth2/state/:registrationId";
20
- readonly PATH_OAUTH2_NONCE: "/oauth2/nonce/:registrationId";
21
- readonly PATH_OAUTH2_AUTHORIZATION: "/oauth2/authorization/:registrationId";
22
- readonly PATH_LOGIN_OAUTH2_CODE: "/login/oauth2/code/:registrationId";
23
- readonly PATH_LOGIN_OAUTH2_NATIVE: "/login/oauth2/native/:registrationId";
24
- readonly PATH_LOGIN_OAUTH2_ONETAP: "/login/oauth2/onetap/google";
25
- readonly PATH_CLEANUP_EXPIRED_SESSIONS: "/sessions/expired/cleanup";
26
- constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig);
31
+ constructor({ sessionRepository, kvRepository, turnstileSecretKey, oauth2, cookie, timing, }: AuthConfig);
27
32
  private getSessionId;
28
33
  csrf: (_request: Request) => Promise<Response>;
29
34
  logout: (request: Request) => Promise<Response>;
@@ -31,13 +36,25 @@ declare class Auth implements AuthService {
31
36
  private createPkceParameters;
32
37
  private getOauth2StateKey;
33
38
  private getOauth2NonceKey;
39
+ private getEmailVerificationCodeKey;
40
+ private getPhoneVerificationCodeKey;
34
41
  oauth2State: (request: Request) => Promise<Response>;
35
42
  oauth2Nonce: (request: Request) => Promise<Response>;
36
43
  oauth2Authorization: (request: Request) => Promise<Response>;
37
44
  private redirect;
38
- loginOAuth2Code: (request: Request, onAuthorized: OAuth2AuthorizedHandler) => Promise<Response>;
39
- loginOAuth2Native: (request: Request, onAuthorized: OAuth2AuthorizedHandler) => Promise<Response>;
40
- loginOAuth2Onetap: (request: Request, onAuthorized: OAuth2AuthorizedHandler, registrationId?: string) => Promise<Response>;
45
+ loginOAuth2Code: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
46
+ loginOAuth2Native: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
47
+ loginOAuth2Onetap: (request: Request, handler: AuthorizedHandler, registrationId?: string) => Promise<Response>;
48
+ sendEmailVerificationCode: (request: Request, sender: (data: {
49
+ email: string;
50
+ verificationCode: string;
51
+ }) => Promise<void>) => Promise<Response>;
52
+ loginEmail: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
53
+ sendPhoneVerificationCode: (_request: Request, _sender: (data: {
54
+ phone: string;
55
+ verificationCode: string;
56
+ }) => Promise<void>) => Promise<Response>;
57
+ loginPhone: (_request: Request, _handler: AuthorizedHandler) => Promise<Response>;
41
58
  kick: (principal: Principal) => Promise<void>;
42
59
  isAuthenticated: (request: Request) => Promise<boolean>;
43
60
  getSession: <T extends boolean>(request: Request, create: T) => Promise<T extends true ? Session : Session | null>;
@@ -47,4 +64,4 @@ declare class Auth implements AuthService {
47
64
  cleanupExpiredSessions: (cleanupCount?: number) => Promise<Response>;
48
65
  }
49
66
 
50
- export { Auth };
67
+ export { Auth, PATH };
@@ -1,29 +1,34 @@
1
- import { AuthService, AuthConfig, LoggedHandler, OAuth2AuthorizedHandler } from './types.js';
2
- import { Principal } from '../core/index.js';
3
1
  import { Session } from '../session/types.js';
2
+ import { AuthService, AuthConfig, LoggedHandler, AuthorizedHandler } from './types.js';
3
+ import { Principal } from '../core/index.js';
4
4
  import '../oauth2/types.js';
5
5
  import '../utils/http.js';
6
6
  import 'cookie';
7
7
 
8
+ declare const PATH: {
9
+ readonly CSRF: "/csrf";
10
+ readonly LOGOUT: "/logout";
11
+ readonly LOGGED: "/logged";
12
+ readonly OAUTH2_STATE: "/oauth2/state/:registrationId";
13
+ readonly OAUTH2_NONCE: "/oauth2/nonce/:registrationId";
14
+ readonly OAUTH2_AUTHORIZATION: "/oauth2/authorization/:registrationId";
15
+ readonly LOGIN_EMAIL: "/login/email";
16
+ readonly LOGIN_PHONE: "/login/phone";
17
+ readonly LOGIN_OAUTH2_CODE: "/login/oauth2/code/:registrationId";
18
+ readonly LOGIN_OAUTH2_NATIVE: "/login/oauth2/native/:registrationId";
19
+ readonly LOGIN_OAUTH2_ONETAP: "/login/oauth2/onetap/google";
20
+ readonly CLEANUP_EXPIRED_SESSIONS: "/sessions/expired/cleanup";
21
+ };
8
22
  declare class Auth implements AuthService {
9
23
  private readonly timing;
10
24
  private readonly cookieName;
11
25
  private readonly cookieOptions;
12
26
  private readonly kv;
13
27
  private readonly repository;
28
+ private readonly turnstileSecretKey?;
14
29
  private readonly oauth2Client;
15
30
  private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST;
16
- readonly PATH_CSRF: "/csrf";
17
- readonly PATH_LOGOUT: "/logout";
18
- readonly PATH_LOGGED: "/logged";
19
- readonly PATH_OAUTH2_STATE: "/oauth2/state/:registrationId";
20
- readonly PATH_OAUTH2_NONCE: "/oauth2/nonce/:registrationId";
21
- readonly PATH_OAUTH2_AUTHORIZATION: "/oauth2/authorization/:registrationId";
22
- readonly PATH_LOGIN_OAUTH2_CODE: "/login/oauth2/code/:registrationId";
23
- readonly PATH_LOGIN_OAUTH2_NATIVE: "/login/oauth2/native/:registrationId";
24
- readonly PATH_LOGIN_OAUTH2_ONETAP: "/login/oauth2/onetap/google";
25
- readonly PATH_CLEANUP_EXPIRED_SESSIONS: "/sessions/expired/cleanup";
26
- constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig);
31
+ constructor({ sessionRepository, kvRepository, turnstileSecretKey, oauth2, cookie, timing, }: AuthConfig);
27
32
  private getSessionId;
28
33
  csrf: (_request: Request) => Promise<Response>;
29
34
  logout: (request: Request) => Promise<Response>;
@@ -31,13 +36,25 @@ declare class Auth implements AuthService {
31
36
  private createPkceParameters;
32
37
  private getOauth2StateKey;
33
38
  private getOauth2NonceKey;
39
+ private getEmailVerificationCodeKey;
40
+ private getPhoneVerificationCodeKey;
34
41
  oauth2State: (request: Request) => Promise<Response>;
35
42
  oauth2Nonce: (request: Request) => Promise<Response>;
36
43
  oauth2Authorization: (request: Request) => Promise<Response>;
37
44
  private redirect;
38
- loginOAuth2Code: (request: Request, onAuthorized: OAuth2AuthorizedHandler) => Promise<Response>;
39
- loginOAuth2Native: (request: Request, onAuthorized: OAuth2AuthorizedHandler) => Promise<Response>;
40
- loginOAuth2Onetap: (request: Request, onAuthorized: OAuth2AuthorizedHandler, registrationId?: string) => Promise<Response>;
45
+ loginOAuth2Code: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
46
+ loginOAuth2Native: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
47
+ loginOAuth2Onetap: (request: Request, handler: AuthorizedHandler, registrationId?: string) => Promise<Response>;
48
+ sendEmailVerificationCode: (request: Request, sender: (data: {
49
+ email: string;
50
+ verificationCode: string;
51
+ }) => Promise<void>) => Promise<Response>;
52
+ loginEmail: (request: Request, handler: AuthorizedHandler) => Promise<Response>;
53
+ sendPhoneVerificationCode: (_request: Request, _sender: (data: {
54
+ phone: string;
55
+ verificationCode: string;
56
+ }) => Promise<void>) => Promise<Response>;
57
+ loginPhone: (_request: Request, _handler: AuthorizedHandler) => Promise<Response>;
41
58
  kick: (principal: Principal) => Promise<void>;
42
59
  isAuthenticated: (request: Request) => Promise<boolean>;
43
60
  getSession: <T extends boolean>(request: Request, create: T) => Promise<T extends true ? Session : Session | null>;
@@ -47,4 +64,4 @@ declare class Auth implements AuthService {
47
64
  cleanupExpiredSessions: (cleanupCount?: number) => Promise<Response>;
48
65
  }
49
66
 
50
- export { Auth };
67
+ export { Auth, PATH };