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.
- package/dist/Auth/AuthHandler.d.ts +9 -12
- package/dist/Auth/AuthHandler.d.ts.map +1 -1
- package/dist/Auth/AuthHandler.js +39 -170
- package/dist/Auth/AuthHandler.js.map +1 -1
- package/dist/Auth/AuthTypes.d.ts +37 -48
- package/dist/Auth/AuthTypes.d.ts.map +1 -1
- package/dist/Auth/endpoints/setCatchAllRequestHandler.d.ts +1 -2
- package/dist/Auth/endpoints/setCatchAllRequestHandler.d.ts.map +1 -1
- package/dist/Auth/endpoints/setCatchAllRequestHandler.js +4 -4
- package/dist/Auth/endpoints/setCatchAllRequestHandler.js.map +1 -1
- package/dist/Auth/endpoints/setConfirmEmailRequestHandler.d.ts +3 -5
- package/dist/Auth/endpoints/setConfirmEmailRequestHandler.d.ts.map +1 -1
- package/dist/Auth/endpoints/setConfirmEmailRequestHandler.js +4 -5
- package/dist/Auth/endpoints/setConfirmEmailRequestHandler.js.map +1 -1
- package/dist/Auth/endpoints/setLoginRequestHandler.d.ts +2 -6
- package/dist/Auth/endpoints/setLoginRequestHandler.d.ts.map +1 -1
- package/dist/Auth/endpoints/setLoginRequestHandler.js +2 -6
- package/dist/Auth/endpoints/setLoginRequestHandler.js.map +1 -1
- package/dist/Auth/endpoints/setMagicLinkRequestHandler.d.ts +2 -3
- package/dist/Auth/endpoints/setMagicLinkRequestHandler.d.ts.map +1 -1
- package/dist/Auth/endpoints/setMagicLinkRequestHandler.js +4 -4
- package/dist/Auth/endpoints/setMagicLinkRequestHandler.js.map +1 -1
- package/dist/Auth/endpoints/setOAuthRequestHandlers.d.ts +5 -0
- package/dist/Auth/endpoints/setOAuthRequestHandlers.d.ts.map +1 -0
- package/dist/Auth/{authProviders/setOAuthProviders.js → endpoints/setOAuthRequestHandlers.js} +19 -15
- package/dist/Auth/endpoints/setOAuthRequestHandlers.js.map +1 -0
- package/dist/Auth/endpoints/setRegisterRequestHandler.d.ts +2 -2
- package/dist/Auth/endpoints/setRegisterRequestHandler.d.ts.map +1 -1
- package/dist/Auth/endpoints/setRegisterRequestHandler.js +18 -44
- package/dist/Auth/endpoints/setRegisterRequestHandler.js.map +1 -1
- package/dist/Auth/getClientAuth.d.ts +8 -0
- package/dist/Auth/getClientAuth.d.ts.map +1 -0
- package/dist/Auth/getClientAuth.js +76 -0
- package/dist/Auth/getClientAuth.js.map +1 -0
- package/dist/Auth/login.d.ts +5 -0
- package/dist/Auth/login.d.ts.map +1 -0
- package/dist/Auth/login.js +111 -0
- package/dist/Auth/login.js.map +1 -0
- package/dist/Auth/sendEmail.d.ts +3 -5
- package/dist/Auth/sendEmail.d.ts.map +1 -1
- package/dist/Auth/sendEmail.js +27 -2
- package/dist/Auth/sendEmail.js.map +1 -1
- package/dist/Auth/setupAuthRoutes.d.ts.map +1 -1
- package/dist/Auth/setupAuthRoutes.js +15 -9
- package/dist/Auth/setupAuthRoutes.js.map +1 -1
- package/dist/Auth/utils/getSidAndUserFromRequest.d.ts.map +1 -1
- package/dist/Auth/utils/getSidAndUserFromRequest.js +2 -1
- package/dist/Auth/utils/getSidAndUserFromRequest.js.map +1 -1
- package/dist/Auth/utils/throttledReject.d.ts +5 -0
- package/dist/Auth/utils/throttledReject.d.ts.map +1 -0
- package/dist/Auth/utils/throttledReject.js +34 -0
- package/dist/Auth/utils/throttledReject.js.map +1 -0
- package/dist/Auth/utils/upsertNamedExpressMiddleware.d.ts +6 -0
- package/dist/Auth/utils/upsertNamedExpressMiddleware.d.ts.map +1 -0
- package/dist/Auth/utils/upsertNamedExpressMiddleware.js +12 -0
- package/dist/Auth/utils/upsertNamedExpressMiddleware.js.map +1 -0
- package/dist/DBSchemaBuilder.js.map +1 -1
- package/dist/Prostgles.d.ts +1 -1
- package/dist/Prostgles.d.ts.map +1 -1
- package/dist/Prostgles.js +2 -2
- package/dist/Prostgles.js.map +1 -1
- package/dist/ProstglesTypes.d.ts +2 -2
- package/dist/ProstglesTypes.d.ts.map +1 -1
- package/lib/Auth/AuthHandler.ts +50 -218
- package/lib/Auth/AuthTypes.ts +72 -72
- package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +8 -4
- package/lib/Auth/endpoints/setConfirmEmailRequestHandler.ts +9 -13
- package/lib/Auth/endpoints/setLoginRequestHandler.ts +7 -13
- package/lib/Auth/endpoints/setMagicLinkRequestHandler.ts +6 -6
- package/lib/Auth/{authProviders/setOAuthProviders.ts → endpoints/setOAuthRequestHandlers.ts} +24 -17
- package/lib/Auth/endpoints/setRegisterRequestHandler.ts +22 -54
- package/lib/Auth/getClientAuth.ts +100 -0
- package/lib/Auth/login.ts +139 -0
- package/lib/Auth/sendEmail.ts +31 -7
- package/lib/Auth/setupAuthRoutes.ts +22 -9
- package/lib/Auth/utils/getSidAndUserFromRequest.ts +2 -1
- package/lib/Auth/utils/throttledReject.ts +33 -0
- package/lib/Auth/utils/upsertNamedExpressMiddleware.ts +14 -0
- package/lib/DBSchemaBuilder.ts +2 -2
- package/lib/Prostgles.ts +1 -1
- package/lib/ProstglesTypes.ts +2 -2
- package/package.json +2 -2
- package/dist/Auth/authProviders/setEmailProvider.d.ts +0 -4
- package/dist/Auth/authProviders/setEmailProvider.d.ts.map +0 -1
- package/dist/Auth/authProviders/setEmailProvider.js +0 -26
- package/dist/Auth/authProviders/setEmailProvider.js.map +0 -1
- package/dist/Auth/authProviders/setOAuthProviders.d.ts +0 -5
- package/dist/Auth/authProviders/setOAuthProviders.d.ts.map +0 -1
- package/dist/Auth/authProviders/setOAuthProviders.js.map +0 -1
- package/dist/Auth/setAuthProviders.d.ts +0 -11
- package/dist/Auth/setAuthProviders.d.ts.map +0 -1
- package/dist/Auth/setAuthProviders.js +0 -41
- package/dist/Auth/setAuthProviders.js.map +0 -1
- package/lib/Auth/authProviders/setEmailProvider.ts +0 -26
- 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 {
|
|
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
|
-
|
|
9
|
-
|
|
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.
|
|
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 {
|
|
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<
|
|
15
|
+
onMagicLink: Required<LoginSignupConfig<void, SessionUser>>["onMagicLink"],
|
|
15
16
|
app: e.Express
|
|
16
17
|
) {
|
|
17
|
-
const
|
|
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
|
|
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,
|
|
47
|
-
return result;
|
|
47
|
+
app.get(AUTH_ROUTES_AND_PARAMS.magicLinksExpressRoute, handler);
|
|
48
48
|
}
|
package/lib/Auth/{authProviders/setOAuthProviders.ts → endpoints/setOAuthRequestHandlers.ts}
RENAMED
|
@@ -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 {
|
|
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 {
|
|
11
|
-
import {
|
|
12
|
-
import e from "express";
|
|
13
|
+
import { upsertNamedExpressMiddleware } from "../utils/upsertNamedExpressMiddleware";
|
|
14
|
+
import { LoginResponseHandler } from "./setLoginRequestHandler";
|
|
13
15
|
|
|
14
|
-
export function
|
|
16
|
+
export function setOAuthRequestHandlers(
|
|
15
17
|
this: AuthHandler,
|
|
16
18
|
app: e.Express,
|
|
17
|
-
|
|
19
|
+
loginWithOAuthConfig: LoginWithOAuthConfig<void>
|
|
18
20
|
) {
|
|
19
|
-
const { onProviderLoginFail, onProviderLoginStart, websiteUrl, OAuthProviders } =
|
|
20
|
-
|
|
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
|
|
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 (
|
|
65
|
-
res.status(
|
|
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(
|
|
79
|
-
|
|
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.
|
|
87
|
+
this.login(req, res, {
|
|
83
88
|
...authInfo,
|
|
84
89
|
type: "provider",
|
|
85
90
|
provider: providerName,
|
|
86
91
|
}).catch((e: any) => {
|
|
87
|
-
res.status(
|
|
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
|
|
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 {
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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:
|
|
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 (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
+
// }
|
package/lib/Auth/sendEmail.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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 {
|
|
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 (!
|
|
19
|
+
if (!loginSignupConfig) {
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
|
-
const {
|
|
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
|
-
|
|
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
|
|
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
|
+
};
|