prostgles-server 4.2.203 → 4.2.205

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 (95) hide show
  1. package/dist/Auth/AuthHandler.d.ts +9 -12
  2. package/dist/Auth/AuthHandler.d.ts.map +1 -1
  3. package/dist/Auth/AuthHandler.js +39 -170
  4. package/dist/Auth/AuthHandler.js.map +1 -1
  5. package/dist/Auth/AuthTypes.d.ts +37 -48
  6. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  7. package/dist/Auth/endpoints/setCatchAllRequestHandler.d.ts +1 -2
  8. package/dist/Auth/endpoints/setCatchAllRequestHandler.d.ts.map +1 -1
  9. package/dist/Auth/endpoints/setCatchAllRequestHandler.js +4 -4
  10. package/dist/Auth/endpoints/setCatchAllRequestHandler.js.map +1 -1
  11. package/dist/Auth/endpoints/setConfirmEmailRequestHandler.d.ts +3 -5
  12. package/dist/Auth/endpoints/setConfirmEmailRequestHandler.d.ts.map +1 -1
  13. package/dist/Auth/endpoints/setConfirmEmailRequestHandler.js +4 -5
  14. package/dist/Auth/endpoints/setConfirmEmailRequestHandler.js.map +1 -1
  15. package/dist/Auth/endpoints/setLoginRequestHandler.d.ts +2 -6
  16. package/dist/Auth/endpoints/setLoginRequestHandler.d.ts.map +1 -1
  17. package/dist/Auth/endpoints/setLoginRequestHandler.js +2 -6
  18. package/dist/Auth/endpoints/setLoginRequestHandler.js.map +1 -1
  19. package/dist/Auth/endpoints/setMagicLinkRequestHandler.d.ts +2 -3
  20. package/dist/Auth/endpoints/setMagicLinkRequestHandler.d.ts.map +1 -1
  21. package/dist/Auth/endpoints/setMagicLinkRequestHandler.js +4 -4
  22. package/dist/Auth/endpoints/setMagicLinkRequestHandler.js.map +1 -1
  23. package/dist/Auth/endpoints/setOAuthRequestHandlers.d.ts +5 -0
  24. package/dist/Auth/endpoints/setOAuthRequestHandlers.d.ts.map +1 -0
  25. package/dist/Auth/{authProviders/setOAuthProviders.js → endpoints/setOAuthRequestHandlers.js} +19 -15
  26. package/dist/Auth/endpoints/setOAuthRequestHandlers.js.map +1 -0
  27. package/dist/Auth/endpoints/setRegisterRequestHandler.d.ts +2 -2
  28. package/dist/Auth/endpoints/setRegisterRequestHandler.d.ts.map +1 -1
  29. package/dist/Auth/endpoints/setRegisterRequestHandler.js +18 -44
  30. package/dist/Auth/endpoints/setRegisterRequestHandler.js.map +1 -1
  31. package/dist/Auth/getClientAuth.d.ts +8 -0
  32. package/dist/Auth/getClientAuth.d.ts.map +1 -0
  33. package/dist/Auth/getClientAuth.js +76 -0
  34. package/dist/Auth/getClientAuth.js.map +1 -0
  35. package/dist/Auth/login.d.ts +5 -0
  36. package/dist/Auth/login.d.ts.map +1 -0
  37. package/dist/Auth/login.js +111 -0
  38. package/dist/Auth/login.js.map +1 -0
  39. package/dist/Auth/sendEmail.d.ts +3 -5
  40. package/dist/Auth/sendEmail.d.ts.map +1 -1
  41. package/dist/Auth/sendEmail.js +27 -2
  42. package/dist/Auth/sendEmail.js.map +1 -1
  43. package/dist/Auth/setupAuthRoutes.d.ts.map +1 -1
  44. package/dist/Auth/setupAuthRoutes.js +15 -9
  45. package/dist/Auth/setupAuthRoutes.js.map +1 -1
  46. package/dist/Auth/utils/getSidAndUserFromRequest.d.ts.map +1 -1
  47. package/dist/Auth/utils/getSidAndUserFromRequest.js +2 -1
  48. package/dist/Auth/utils/getSidAndUserFromRequest.js.map +1 -1
  49. package/dist/Auth/utils/throttledReject.d.ts +5 -0
  50. package/dist/Auth/utils/throttledReject.d.ts.map +1 -0
  51. package/dist/Auth/utils/throttledReject.js +34 -0
  52. package/dist/Auth/utils/throttledReject.js.map +1 -0
  53. package/dist/Auth/utils/upsertNamedExpressMiddleware.d.ts +6 -0
  54. package/dist/Auth/utils/upsertNamedExpressMiddleware.d.ts.map +1 -0
  55. package/dist/Auth/utils/upsertNamedExpressMiddleware.js +12 -0
  56. package/dist/Auth/utils/upsertNamedExpressMiddleware.js.map +1 -0
  57. package/dist/DBSchemaBuilder.js.map +1 -1
  58. package/dist/Prostgles.d.ts +1 -1
  59. package/dist/Prostgles.d.ts.map +1 -1
  60. package/dist/Prostgles.js +2 -2
  61. package/dist/Prostgles.js.map +1 -1
  62. package/dist/ProstglesTypes.d.ts +2 -2
  63. package/dist/ProstglesTypes.d.ts.map +1 -1
  64. package/lib/Auth/AuthHandler.ts +50 -218
  65. package/lib/Auth/AuthTypes.ts +72 -72
  66. package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +8 -4
  67. package/lib/Auth/endpoints/setConfirmEmailRequestHandler.ts +9 -13
  68. package/lib/Auth/endpoints/setLoginRequestHandler.ts +7 -13
  69. package/lib/Auth/endpoints/setMagicLinkRequestHandler.ts +6 -6
  70. package/lib/Auth/{authProviders/setOAuthProviders.ts → endpoints/setOAuthRequestHandlers.ts} +24 -17
  71. package/lib/Auth/endpoints/setRegisterRequestHandler.ts +22 -54
  72. package/lib/Auth/getClientAuth.ts +100 -0
  73. package/lib/Auth/login.ts +139 -0
  74. package/lib/Auth/sendEmail.ts +31 -7
  75. package/lib/Auth/setupAuthRoutes.ts +22 -9
  76. package/lib/Auth/utils/getSidAndUserFromRequest.ts +2 -1
  77. package/lib/Auth/utils/throttledReject.ts +33 -0
  78. package/lib/Auth/utils/upsertNamedExpressMiddleware.ts +14 -0
  79. package/lib/DBSchemaBuilder.ts +2 -2
  80. package/lib/Prostgles.ts +1 -1
  81. package/lib/ProstglesTypes.ts +2 -2
  82. package/package.json +2 -2
  83. package/dist/Auth/authProviders/setEmailProvider.d.ts +0 -4
  84. package/dist/Auth/authProviders/setEmailProvider.d.ts.map +0 -1
  85. package/dist/Auth/authProviders/setEmailProvider.js +0 -26
  86. package/dist/Auth/authProviders/setEmailProvider.js.map +0 -1
  87. package/dist/Auth/authProviders/setOAuthProviders.d.ts +0 -5
  88. package/dist/Auth/authProviders/setOAuthProviders.d.ts.map +0 -1
  89. package/dist/Auth/authProviders/setOAuthProviders.js.map +0 -1
  90. package/dist/Auth/setAuthProviders.d.ts +0 -11
  91. package/dist/Auth/setAuthProviders.d.ts.map +0 -1
  92. package/dist/Auth/setAuthProviders.js +0 -41
  93. package/dist/Auth/setAuthProviders.js.map +0 -1
  94. package/lib/Auth/authProviders/setEmailProvider.ts +0 -26
  95. package/lib/Auth/setAuthProviders.ts +0 -47
@@ -1,20 +1,18 @@
1
1
  import e, { Response } from "express";
2
+ import { AuthRequest, AuthResponse, isDefined, isObject } from "prostgles-types";
2
3
  import { AUTH_ROUTES_AND_PARAMS, AuthHandler, HTTP_FAIL_CODES } from "../AuthHandler";
3
- import { BasicSession, LoginParams } from "../AuthTypes";
4
- import { AuthFailure, AuthRequest, AuthResponse, isDefined, isObject } from "prostgles-types";
4
+ import { LoginParams } from "../AuthTypes";
5
5
 
6
6
  export type LoginResponseHandler = Response<
7
- | {
8
- session: BasicSession;
9
- response?: AuthResponse.PasswordLoginSuccess | AuthResponse.MagicLinkAuthSuccess;
10
- }
11
- | AuthFailure
7
+ | AuthResponse.OAuthRegisterSuccess
8
+ | AuthResponse.OAuthRegisterFailure
9
+ | AuthResponse.PasswordLoginSuccess
12
10
  | AuthResponse.PasswordLoginFailure
13
11
  | AuthResponse.MagicLinkAuthFailure
12
+ | AuthResponse.MagicLinkAuthSuccess
14
13
  >;
15
14
 
16
15
  export function setLoginRequestHandler(this: AuthHandler, app: e.Express) {
17
- const { registrations } = this.opts.expressConfig ?? {};
18
16
  app.post(AUTH_ROUTES_AND_PARAMS.login, async (req, res: LoginResponseHandler) => {
19
17
  const loginData = parseLoginData(req.body);
20
18
  if ("error" in loginData) {
@@ -25,14 +23,10 @@ export function setLoginRequestHandler(this: AuthHandler, app: e.Express) {
25
23
  try {
26
24
  const loginParams: LoginParams = {
27
25
  ...loginData,
28
- magicLinkUrlPath:
29
- registrations?.websiteUrl &&
30
- `${registrations.websiteUrl}/${AUTH_ROUTES_AND_PARAMS.magicLinksRoute}`,
31
- signupType: registrations?.email?.signupType,
32
26
  type: "username",
33
27
  };
34
28
 
35
- await this.loginThrottledAndSetCookie(req, res, loginParams);
29
+ await this.login(req, res, loginParams);
36
30
  } catch (_error) {
37
31
  res.status(HTTP_FAIL_CODES.BAD_REQUEST).json({ success: false, code: "server-error" });
38
32
  }
@@ -6,15 +6,16 @@ import {
6
6
  getClientRequestIPsInfo,
7
7
  HTTP_FAIL_CODES,
8
8
  } from "../AuthHandler";
9
- import { ExpressConfig, ExpressReq, SessionUser } from "../AuthTypes";
9
+ import { LoginSignupConfig, ExpressReq, SessionUser } from "../AuthTypes";
10
10
  import { LoginResponseHandler } from "./setLoginRequestHandler";
11
+ import { throttledReject } from "../utils/throttledReject";
11
12
 
12
13
  export function setMagicLinkRequestHandler(
13
14
  this: AuthHandler,
14
- onMagicLink: Required<ExpressConfig<void, SessionUser>>["onMagicLink"],
15
+ onMagicLink: Required<LoginSignupConfig<void, SessionUser>>["onMagicLink"],
15
16
  app: e.Express
16
17
  ) {
17
- const result = async (req: ExpressReq, res: LoginResponseHandler) => {
18
+ const handler = async (req: ExpressReq, res: LoginResponseHandler) => {
18
19
  const { id } = req.params;
19
20
 
20
21
  if (typeof id !== "string" || !id) {
@@ -23,7 +24,7 @@ export function setMagicLinkRequestHandler(
23
24
  .json({ success: false, code: "something-went-wrong", message: "Invalid magic link" });
24
25
  } else {
25
26
  try {
26
- const response = await this.throttledFunc(async () => {
27
+ const response = await throttledReject(async () => {
27
28
  return onMagicLink(
28
29
  id,
29
30
  this.dbo as DBOFullyTyped,
@@ -43,6 +44,5 @@ export function setMagicLinkRequestHandler(
43
44
  }
44
45
  }
45
46
  };
46
- app.get(AUTH_ROUTES_AND_PARAMS.magicLinksExpressRoute, result);
47
- return result;
47
+ app.get(AUTH_ROUTES_AND_PARAMS.magicLinksExpressRoute, handler);
48
48
  }
@@ -1,3 +1,4 @@
1
+ import e from "express";
1
2
  import * as passport from "passport";
2
3
  import { Strategy as FacebookStrategy } from "passport-facebook";
3
4
  import { Strategy as GitHubStrategy } from "passport-github2";
@@ -5,19 +6,21 @@ import { Strategy as GoogleStrategy } from "passport-google-oauth20";
5
6
  import { Strategy as MicrosoftStrategy } from "passport-microsoft";
6
7
  import { getObjectEntries, isEmpty } from "prostgles-types";
7
8
  import { getErrorAsObject } from "../../DboBuilder/dboBuilderUtils";
8
- import { AUTH_ROUTES_AND_PARAMS, AuthHandler } from "../AuthHandler";
9
+ import { DBOFullyTyped } from "../../DBSchemaBuilder";
10
+ import { AUTH_ROUTES_AND_PARAMS, AuthHandler, HTTP_FAIL_CODES } from "../AuthHandler";
11
+ import { LoginWithOAuthConfig } from "../AuthTypes";
9
12
  import { getClientRequestIPsInfo } from "../utils/getClientRequestIPsInfo";
10
- import { AuthRegistrationConfig } from "../AuthTypes";
11
- import { upsertNamedExpressMiddleware } from "../setAuthProviders";
12
- import e from "express";
13
+ import { upsertNamedExpressMiddleware } from "../utils/upsertNamedExpressMiddleware";
14
+ import { LoginResponseHandler } from "./setLoginRequestHandler";
13
15
 
14
- export function setOAuthProviders(
16
+ export function setOAuthRequestHandlers(
15
17
  this: AuthHandler,
16
18
  app: e.Express,
17
- registrations: AuthRegistrationConfig<void>
19
+ loginWithOAuthConfig: LoginWithOAuthConfig<void>
18
20
  ) {
19
- const { onProviderLoginFail, onProviderLoginStart, websiteUrl, OAuthProviders } = registrations;
20
- if (!OAuthProviders || isEmpty(OAuthProviders)) {
21
+ const { onProviderLoginFail, onProviderLoginStart, websiteUrl, OAuthProviders } =
22
+ loginWithOAuthConfig;
23
+ if (isEmpty(OAuthProviders)) {
21
24
  return;
22
25
  }
23
26
 
@@ -54,15 +57,15 @@ export function setOAuthProviders(
54
57
  passport.authenticate(providerName, authOpts ?? {})
55
58
  );
56
59
 
57
- app.get(callbackPath, async (req, res) => {
60
+ app.get(callbackPath, async (req, res: LoginResponseHandler) => {
58
61
  try {
59
62
  const clientInfo = getClientRequestIPsInfo({ httpReq: req, res });
60
63
  const db = this.db;
61
- const dbo = this.dbo as any;
64
+ const dbo = this.dbo as DBOFullyTyped;
62
65
  const args = { provider: providerName, req, res, clientInfo, db, dbo };
63
66
  const startCheck = await onProviderLoginStart?.(args);
64
- if (startCheck && "error" in startCheck) {
65
- res.status(500).json({ error: startCheck.error });
67
+ if (onProviderLoginStart && !startCheck?.success) {
68
+ res.status(HTTP_FAIL_CODES.BAD_REQUEST).json(startCheck);
66
69
  return;
67
70
  }
68
71
  passport.authenticate(
@@ -75,22 +78,26 @@ export function setOAuthProviders(
75
78
  async (error: any, _profile: any, authInfo: any) => {
76
79
  if (error) {
77
80
  await onProviderLoginFail?.({ ...args, error });
78
- res.status(500).json({
79
- error: "Failed to login with provider",
81
+ res.status(HTTP_FAIL_CODES.BAD_REQUEST).json({
82
+ success: false,
83
+ code: "provider-issue",
84
+ message: "Failed to login with provider",
80
85
  });
81
86
  } else {
82
- this.loginThrottledAndSetCookie(req, res, {
87
+ this.login(req, res, {
83
88
  ...authInfo,
84
89
  type: "provider",
85
90
  provider: providerName,
86
91
  }).catch((e: any) => {
87
- res.status(500).json(getErrorAsObject(e));
92
+ res.status(HTTP_FAIL_CODES.INTERNAL_SERVER_ERROR).json(getErrorAsObject(e));
88
93
  });
89
94
  }
90
95
  }
91
96
  )(req, res);
92
97
  } catch (_e) {
93
- res.status(500).json({ error: "Something went wrong" });
98
+ res
99
+ .status(HTTP_FAIL_CODES.INTERNAL_SERVER_ERROR)
100
+ .json({ code: "server-error", success: false });
94
101
  }
95
102
  });
96
103
  });
@@ -1,8 +1,7 @@
1
1
  import e, { Request, Response } from "express";
2
2
  import { AuthResponse } from "prostgles-types";
3
3
  import { AUTH_ROUTES_AND_PARAMS, HTTP_FAIL_CODES } from "../AuthHandler";
4
- import type { AuthRegistrationConfig } from "../AuthTypes";
5
- import { sendEmail } from "../sendEmail";
4
+ import type { SignupWithEmailAndPassword } from "../AuthTypes";
6
5
  import { getClientRequestIPsInfo } from "../utils/getClientRequestIPsInfo";
7
6
  import { parseLoginData } from "./setLoginRequestHandler";
8
7
 
@@ -12,14 +11,13 @@ type ReturnType =
12
11
  | AuthResponse.PasswordRegisterFailure
13
12
  | AuthResponse.PasswordRegisterSuccess;
14
13
 
15
- export const setRegisterRequestHandler = (
16
- {
17
- email: emailAuthConfig,
18
- websiteUrl,
19
- }: Required<Pick<AuthRegistrationConfig<void>, "email" | "websiteUrl">>,
14
+ type RegisterResponseHandler = Response<ReturnType>;
15
+
16
+ export const setRegisterRequestHandler = async (
17
+ { onRegister, minPasswordLength = 8 }: SignupWithEmailAndPassword,
20
18
  app: e.Express
21
19
  ) => {
22
- const registerRequestHandler = async (req: Request, res: Response<ReturnType>) => {
20
+ const registerRequestHandler = async (req: Request, res: RegisterResponseHandler) => {
23
21
  const dataOrError = parseLoginData(req.body);
24
22
  if ("error" in dataOrError) {
25
23
  return res
@@ -37,55 +35,25 @@ export const setRegisterRequestHandler = (
37
35
  if (!username) {
38
36
  return sendResponse({ success: false, code: "username-missing" });
39
37
  }
40
- if (emailAuthConfig.signupType === "withPassword") {
41
- const { minPasswordLength = 8 } = emailAuthConfig;
42
- if (!password) {
43
- return sendResponse({ success: false, code: "password-missing" });
44
- } else if (password.length < minPasswordLength) {
45
- return sendResponse({
46
- success: false,
47
- code: "weak-password",
48
- message: `Password must be at least ${minPasswordLength} characters long`,
49
- });
50
- }
38
+ if (!password) {
39
+ return sendResponse({ success: false, code: "password-missing" });
40
+ } else if (password.length < minPasswordLength) {
41
+ return sendResponse({
42
+ success: false,
43
+ code: "weak-password",
44
+ message: `Password must be at least ${minPasswordLength} characters long`,
45
+ });
51
46
  }
52
47
  try {
53
- const { httpReq, ...clientInfo } = getClientRequestIPsInfo({ httpReq: req, res });
54
- const { smtp } = emailAuthConfig;
55
- const errCodeOrResult =
56
- emailAuthConfig.signupType === "withPassword" ?
57
- !password ? "weak-password"
58
- : await emailAuthConfig.onRegister({
59
- email: username,
60
- password,
61
- confirmationUrlPath: `${websiteUrl}${AUTH_ROUTES_AND_PARAMS.confirmEmail}`,
62
- clientInfo,
63
- req: httpReq,
64
- })
65
- : await emailAuthConfig.onRegister({
66
- email: username,
67
- magicLinkUrlPath: `${websiteUrl}${AUTH_ROUTES_AND_PARAMS.magicLinksRoute}`,
68
- clientInfo,
69
- req: httpReq,
70
- });
71
-
72
- const registrationResult =
73
- typeof errCodeOrResult === "string" ?
74
- { email: undefined, response: { success: false as const, code: errCodeOrResult } }
75
- : errCodeOrResult;
76
- if (!registrationResult.email) {
77
- return sendResponse(registrationResult.response);
78
- }
79
-
80
- const emailMessage = { ...registrationResult.email, to: username };
81
- await sendEmail(smtp, emailMessage);
82
- return sendResponse({
83
- ...registrationResult.response,
84
- message:
85
- emailAuthConfig.signupType === "withPassword" ?
86
- `We've sent a confirmation email to ${emailMessage.to}. Please check your inbox (and your spam folder) for a message from us.`
87
- : "Email sent",
48
+ const { httpReq, ...clientInfo } = getClientRequestIPsInfo({ httpReq: req });
49
+ const result = await onRegister({
50
+ email: username,
51
+ password,
52
+ confirmationUrlPath: AUTH_ROUTES_AND_PARAMS.confirmEmail,
53
+ clientInfo,
54
+ req: httpReq,
88
55
  });
56
+ return sendResponse(result);
89
57
  } catch {
90
58
  return sendResponse({
91
59
  success: false,
@@ -0,0 +1,100 @@
1
+ import {
2
+ AuthGuardLocation,
3
+ AuthGuardLocationResponse,
4
+ AuthSocketSchema,
5
+ CHANNELS,
6
+ getObjectEntries,
7
+ isEmpty,
8
+ } from "prostgles-types";
9
+ import { AuthClientRequest, LoginWithOAuthConfig, AuthResultWithSID } from "./AuthTypes";
10
+ import { AUTH_ROUTES_AND_PARAMS, AuthHandler } from "./AuthHandler";
11
+
12
+ export async function getClientAuth(
13
+ this: AuthHandler,
14
+ clientReq: AuthClientRequest
15
+ ): Promise<{ auth: AuthSocketSchema; userData: AuthResultWithSID }> {
16
+ let pathGuard = false;
17
+ if (
18
+ this.opts.loginSignupConfig?.publicRoutes &&
19
+ !this.opts.loginSignupConfig.disableSocketAuthGuard
20
+ ) {
21
+ pathGuard = true;
22
+
23
+ /**
24
+ * Due to SPA nature of some clients, we need to check if the connected client ends up on a protected route
25
+ */
26
+ if (clientReq.socket) {
27
+ const { socket } = clientReq;
28
+ socket.removeAllListeners(CHANNELS.AUTHGUARD);
29
+ socket.on(
30
+ CHANNELS.AUTHGUARD,
31
+ async (
32
+ params: AuthGuardLocation,
33
+ cb = (_err: any, _res?: AuthGuardLocationResponse) => {
34
+ /** EMPTY */
35
+ }
36
+ ) => {
37
+ try {
38
+ const { pathname, origin } =
39
+ typeof params === "string" ? (JSON.parse(params) as AuthGuardLocation) : params;
40
+ if (pathname && typeof pathname !== "string") {
41
+ console.warn("Invalid pathname provided for AuthGuardLocation: ", pathname);
42
+ }
43
+
44
+ /** These origins */
45
+ const IGNORED_API_ORIGINS = ["file://"];
46
+ if (
47
+ !IGNORED_API_ORIGINS.includes(origin) &&
48
+ pathname &&
49
+ typeof pathname === "string" &&
50
+ this.isUserRoute(pathname) &&
51
+ !(await this.getUserFromRequest({ socket }))
52
+ ) {
53
+ cb(null, { shouldReload: true });
54
+ } else {
55
+ cb(null, { shouldReload: false });
56
+ }
57
+ } catch (err) {
58
+ console.error("AUTHGUARD err: ", err);
59
+ cb(err);
60
+ }
61
+ }
62
+ );
63
+ }
64
+ }
65
+
66
+ const userData = await this.getSidAndUserFromRequest(clientReq);
67
+ const { loginWithOAuth, signupWithEmailAndPassword, localLoginMode, login } =
68
+ this.opts.loginSignupConfig ?? {};
69
+
70
+ const auth: AuthSocketSchema = {
71
+ providers: getOAuthProviders(loginWithOAuth),
72
+ signupWithEmailAndPassword: signupWithEmailAndPassword && {
73
+ minPasswordLength: signupWithEmailAndPassword.minPasswordLength ?? 8,
74
+ url: AUTH_ROUTES_AND_PARAMS.emailRegistration,
75
+ },
76
+ user: userData.clientUser,
77
+ loginType: localLoginMode ?? (login ? "email+password" : undefined),
78
+ pathGuard,
79
+ };
80
+ return { auth, userData };
81
+ }
82
+
83
+ const getOAuthProviders = (
84
+ loginWithOAuth: LoginWithOAuthConfig<any> | undefined
85
+ ): AuthSocketSchema["providers"] | undefined => {
86
+ if (!loginWithOAuth) return undefined;
87
+ const { OAuthProviders } = loginWithOAuth;
88
+ if (isEmpty(OAuthProviders)) return undefined;
89
+
90
+ const result: AuthSocketSchema["providers"] = {};
91
+ getObjectEntries(OAuthProviders).forEach(([providerName, config]) => {
92
+ if (config?.clientID) {
93
+ result[providerName] = {
94
+ url: `${AUTH_ROUTES_AND_PARAMS.loginWithProvider}/${providerName}`,
95
+ };
96
+ }
97
+ });
98
+
99
+ return result;
100
+ };
@@ -0,0 +1,139 @@
1
+ import { AuthHandler, getClientRequestIPsInfo, HTTP_FAIL_CODES } from "./AuthHandler";
2
+ import { DBOFullyTyped } from "../DBSchemaBuilder";
3
+ import { ExpressReq, LoginParams } from "./AuthTypes";
4
+ import { LoginResponseHandler } from "./endpoints/setLoginRequestHandler";
5
+ import { throttledReject } from "./utils/throttledReject";
6
+
7
+ export async function login(
8
+ this: AuthHandler,
9
+ req: ExpressReq,
10
+ res: LoginResponseHandler,
11
+ loginParams: LoginParams
12
+ ) {
13
+ const start = Date.now();
14
+ const { responseThrottle = 500 } = this.opts;
15
+
16
+ const errCodeOrSession = await throttledReject(async () => {
17
+ const { login } = this.opts.loginSignupConfig ?? {};
18
+ if (!login) {
19
+ console.error("Auth login config missing");
20
+ return "server-error";
21
+ }
22
+ const result = await login(
23
+ loginParams,
24
+ this.dbo as DBOFullyTyped,
25
+ this.db,
26
+ getClientRequestIPsInfo({ httpReq: req })
27
+ );
28
+
29
+ if (typeof result === "string" || !result.session) {
30
+ return result;
31
+ }
32
+
33
+ const { sid, expires } = result.session;
34
+ if (!sid) {
35
+ console.error("Invalid sid");
36
+ return "server-error";
37
+ }
38
+ if (sid && (typeof sid !== "string" || typeof expires !== "number")) {
39
+ console.error(
40
+ "Bad login result type. \nExpecting: undefined | null | { sid: string; expires: number }"
41
+ );
42
+ return "server-error";
43
+ }
44
+ if (expires < Date.now()) {
45
+ console.error(
46
+ "auth.login() is returning an expired session. Can only login with a session.expires greater than Date.now()"
47
+ );
48
+ return "server-error";
49
+ }
50
+
51
+ return result;
52
+ }, responseThrottle);
53
+
54
+ const loginResponse =
55
+ typeof errCodeOrSession === "string" ?
56
+ {
57
+ session: undefined,
58
+ response: { success: false, code: errCodeOrSession } as const,
59
+ }
60
+ : errCodeOrSession;
61
+
62
+ await this.prostgles.opts.onLog?.({
63
+ type: "auth",
64
+ command: "login",
65
+ success: !!loginResponse.session,
66
+ duration: Date.now() - start,
67
+ sid: loginResponse.session?.sid,
68
+ socketId: undefined,
69
+ });
70
+
71
+ if (!loginResponse.session) {
72
+ if (!loginResponse.response.success) {
73
+ return res.status(HTTP_FAIL_CODES.BAD_REQUEST).json(loginResponse.response);
74
+ }
75
+ return res.json(loginResponse.response);
76
+ }
77
+ this.setCookieAndGoToReturnURLIFSet(loginResponse.session, { req, res });
78
+ }
79
+
80
+ // async function loginThrottledAndValidate(
81
+ // this: AuthHandler,
82
+ // params: LoginParams,
83
+ // client: LoginClientInfo
84
+ // ): Promise<Exclude<LoginResponse, { loginWithMagicLink: true }>> {
85
+ // const { responseThrottle = 500 } = this.opts;
86
+ // return throttledFunc(async () => {
87
+ // const { login } = this.opts.expressConfig ?? {};
88
+ // if (!login) {
89
+ // console.error("Auth login config missing");
90
+ // return "server-error";
91
+ // }
92
+ // const result = await login(params, this.dbo as DBOFullyTyped, this.db, client);
93
+
94
+ // if (typeof result === "string") return result;
95
+
96
+ // // if ("loginWithMagicLink" in result) {
97
+ // // if (params.type !== "username") {
98
+ // // console.error("loginWithMagicLink is only supported for username login");
99
+ // // return "something-went-wrong";
100
+ // // }
101
+ // // const emailRegistrations = this.opts.expressConfig?.registrations?.email;
102
+ // // if (!emailRegistrations || emailRegistrations.signupType !== "withMagicLink") {
103
+ // // console.error("loginWithMagicLink is not supported");
104
+ // // return "server-error";
105
+ // // }
106
+
107
+ // // const res = await emailRegistrations.onRegister({
108
+ // // email: params.username,
109
+ // // clientInfo: client,
110
+ // // magicLinkUrlPath: `${emailRegistrations.websiteUrl}${AUTH_ROUTES_AND_PARAMS.magicLinksRoute}`,
111
+ // // req,
112
+ // // });
113
+
114
+ // // if (typeof res === "string") {
115
+ // // return { response: { success: false, code: res } as const };
116
+ // // }
117
+ // // }
118
+
119
+ // const { sid, expires } = result.session;
120
+ // if (!sid) {
121
+ // console.error("Invalid sid");
122
+ // return "server-error";
123
+ // }
124
+ // if (sid && (typeof sid !== "string" || typeof expires !== "number")) {
125
+ // console.error(
126
+ // "Bad login result type. \nExpecting: undefined | null | { sid: string; expires: number }"
127
+ // );
128
+ // return "server-error";
129
+ // }
130
+ // if (expires < Date.now()) {
131
+ // console.error(
132
+ // "auth.login() is returning an expired session. Can only login with a session.expires greater than Date.now()"
133
+ // );
134
+ // return "server-error";
135
+ // }
136
+
137
+ // return result;
138
+ // }, responseThrottle);
139
+ // }
@@ -2,11 +2,9 @@ import { Email, SMTPConfig } from "./AuthTypes";
2
2
  import * as nodemailer from "nodemailer";
3
3
  import * as aws from "@aws-sdk/client-ses";
4
4
  import SESTransport from "nodemailer/lib/ses-transport";
5
+ import { checkDmarc } from "./utils/checkDmarc";
5
6
 
6
- type SESTransporter = nodemailer.Transporter<
7
- SESTransport.SentMessageInfo,
8
- SESTransport.Options
9
- >;
7
+ type SESTransporter = nodemailer.Transporter<SESTransport.SentMessageInfo, SESTransport.Options>;
10
8
  type SMTPTransporter = nodemailer.Transporter<
11
9
  nodemailer.SentMessageInfo,
12
10
  nodemailer.TransportOptions
@@ -19,18 +17,44 @@ const transporterCache: Map<string, Transporter> = new Map();
19
17
  * Allows sending emails using nodemailer default config or AWS SES
20
18
  * https://www.nodemailer.com/transports/ses/
21
19
  */
22
- export const sendEmail = (smptConfig: SMTPConfig, email: Email) => {
20
+ const sendEmail = (smptConfig: SMTPConfig, email: Email) => {
23
21
  const transporter = getOrSetTransporter(smptConfig);
24
22
  return send(transporter, email);
25
23
  };
26
24
 
25
+ /**
26
+ * Verifies DMARC and that the website has a valid DMARC records
27
+ */
28
+ const emailSenderCache: Map<string, boolean> = new Map();
29
+ export const getEmailSender = async (smptConfig: SMTPConfig, websiteUrl: string) => {
30
+ const result = {
31
+ sendEmail: (email: Email) => sendEmail(smptConfig, email),
32
+ };
33
+ const configStr = JSON.stringify({ smptConfig, websiteUrl });
34
+ if (emailSenderCache.has(configStr)) {
35
+ return result;
36
+ }
37
+ if (!websiteUrl) {
38
+ throw new Error("websiteUrl is required for email registrations");
39
+ }
40
+ await checkDmarc(websiteUrl);
41
+
42
+ await verifySMTPConfig(smptConfig);
43
+
44
+ /**
45
+ * Setup nodemailer transporters
46
+ */
47
+ getOrSetTransporter(smptConfig);
48
+ emailSenderCache.set(configStr, true);
49
+ return result;
50
+ };
51
+
27
52
  /**
28
53
  * Returns a transporter from cache or creates a new one
29
54
  */
30
55
  export const getOrSetTransporter = (smptConfig: SMTPConfig) => {
31
56
  const configStr = JSON.stringify(smptConfig);
32
- const transporter =
33
- transporterCache.get(configStr) ?? getTransporter(smptConfig);
57
+ const transporter = transporterCache.get(configStr) ?? getTransporter(smptConfig);
34
58
  if (!transporterCache.has(configStr)) {
35
59
  transporterCache.set(configStr, transporter);
36
60
  }
@@ -2,30 +2,43 @@ import { RequestHandler } from "express";
2
2
  import { DBOFullyTyped } from "../DBSchemaBuilder";
3
3
  import { AuthHandler } from "./AuthHandler";
4
4
  import { setCatchAllRequestHandler } from "./endpoints/setCatchAllRequestHandler";
5
+ import { setConfirmEmailRequestHandler } from "./endpoints/setConfirmEmailRequestHandler";
5
6
  import { setLoginRequestHandler } from "./endpoints/setLoginRequestHandler";
6
7
  import { setMagicLinkRequestHandler } from "./endpoints/setMagicLinkRequestHandler";
7
- import { setAuthProviders, upsertNamedExpressMiddleware } from "./setAuthProviders";
8
+ import { setOAuthRequestHandlers } from "./endpoints/setOAuthRequestHandlers";
9
+ import { setRegisterRequestHandler } from "./endpoints/setRegisterRequestHandler";
10
+ import { upsertNamedExpressMiddleware } from "./utils/upsertNamedExpressMiddleware";
8
11
 
9
12
  export async function setupAuthRoutes(this: AuthHandler) {
10
- const { login, expressConfig } = this.opts;
11
-
12
- if (!login) {
13
- throw "Invalid auth: Provide { sidKeyName: string } ";
14
- }
13
+ const { loginSignupConfig } = this.opts;
15
14
 
16
15
  if (this.sidKeyName === "sid") {
17
16
  throw "sidKeyName cannot be 'sid' due to collision with socket.io";
18
17
  }
19
18
 
20
- if (!expressConfig) {
19
+ if (!loginSignupConfig) {
21
20
  return;
22
21
  }
23
- const { app, publicRoutes = [], onMagicLink, use } = expressConfig;
22
+ const {
23
+ app,
24
+ publicRoutes = [],
25
+ onMagicLink,
26
+ use,
27
+ loginWithOAuth,
28
+ signupWithEmailAndPassword,
29
+ } = loginSignupConfig;
24
30
  if (publicRoutes.find((r) => typeof r !== "string" || !r)) {
25
31
  throw "Invalid or empty string provided within publicRoutes ";
26
32
  }
27
33
 
28
- await setAuthProviders.bind(this)(expressConfig);
34
+ if (signupWithEmailAndPassword) {
35
+ setRegisterRequestHandler(signupWithEmailAndPassword, app);
36
+ setConfirmEmailRequestHandler.bind(this)(signupWithEmailAndPassword, app);
37
+ }
38
+
39
+ if (loginWithOAuth) {
40
+ await setOAuthRequestHandlers.bind(this)(app, loginWithOAuth);
41
+ }
29
42
 
30
43
  if (use) {
31
44
  const prostglesUseMiddleware: RequestHandler = (req, res, next) => {
@@ -1,6 +1,7 @@
1
1
  import { DBOFullyTyped } from "../../DBSchemaBuilder";
2
2
  import { AuthHandler, getClientRequestIPsInfo } from "../AuthHandler";
3
3
  import { AuthClientRequest, AuthResultWithSID } from "../AuthTypes";
4
+ import { throttledReject } from "./throttledReject";
4
5
 
5
6
  /**
6
7
  * For a given sid return the user data if available using the auth handler's getUser method.
@@ -32,7 +33,7 @@ export async function getSidAndUserFromRequest(
32
33
  * Get sid from request and fetch user data
33
34
  */
34
35
  const authStart = Date.now();
35
- const result = await this.throttledFunc(async () => {
36
+ const result = await throttledReject(async () => {
36
37
  const { getUser } = this.opts;
37
38
 
38
39
  const sid = this.getSID(clientReq);
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Given an async function, this function will throttle the response time if errored to prevent timing attacks
3
+ */
4
+ export const throttledReject = <T>(func: () => Promise<T>, throttle = 500): Promise<T> => {
5
+ return new Promise(async (resolve, reject) => {
6
+ let result: T,
7
+ error: any,
8
+ finished = false;
9
+
10
+ /**
11
+ * Throttle reject response times to prevent timing attacks
12
+ */
13
+ const interval = setInterval(() => {
14
+ if (finished) {
15
+ clearInterval(interval);
16
+ if (error) {
17
+ reject(error);
18
+ }
19
+ }
20
+ }, throttle);
21
+
22
+ try {
23
+ result = await func();
24
+ resolve(result);
25
+ clearInterval(interval);
26
+ } catch (err) {
27
+ console.log(err);
28
+ error = err;
29
+ }
30
+
31
+ finished = true;
32
+ });
33
+ };