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