alp-node-auth 10.0.0 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +13 -13
- package/dist/definitions/MongoUsersManager.d.ts +2 -2
- package/dist/definitions/MongoUsersManager.d.ts.map +1 -1
- package/dist/definitions/authApolloContext.d.ts +4 -4
- package/dist/definitions/authApolloContext.d.ts.map +1 -1
- package/dist/definitions/authSocketIO.d.ts +4 -4
- package/dist/definitions/authSocketIO.d.ts.map +1 -1
- package/dist/definitions/createAuthController.d.ts +6 -9
- package/dist/definitions/createAuthController.d.ts.map +1 -1
- package/dist/definitions/createRoutes.d.ts +3 -3
- package/dist/definitions/createRoutes.d.ts.map +1 -1
- package/dist/definitions/index.d.ts +24 -25
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/services/authentification/AuthenticationService.d.ts +6 -8
- package/dist/definitions/services/authentification/AuthenticationService.d.ts.map +1 -1
- package/dist/definitions/services/authentification/types.d.ts +3 -3
- package/dist/definitions/services/user/UserAccountGoogleService.d.ts +4 -4
- package/dist/definitions/services/user/UserAccountGoogleService.d.ts.map +1 -1
- package/dist/definitions/services/user/UserAccountSlackService.d.ts +4 -4
- package/dist/definitions/services/user/UserAccountSlackService.d.ts.map +1 -1
- package/dist/definitions/services/user/UserAccountsService.d.ts +6 -7
- package/dist/definitions/services/user/UserAccountsService.d.ts.map +1 -1
- package/dist/definitions/services/user/types.d.ts +1 -1
- package/dist/definitions/types.d.ts +1 -1
- package/dist/definitions/utils/cookies.d.ts +3 -4
- package/dist/definitions/utils/cookies.d.ts.map +1 -1
- package/dist/definitions/utils/createFindLoggedInUser.d.ts +4 -4
- package/dist/definitions/utils/createFindLoggedInUser.d.ts.map +1 -1
- package/dist/{index-node18.mjs → index-node.mjs} +308 -302
- package/dist/index-node.mjs.map +1 -0
- package/package.json +28 -26
- package/src/MongoUsersManager.ts +5 -6
- package/src/authApolloContext.ts +10 -10
- package/src/authSocketIO.ts +10 -10
- package/src/createAuthController.ts +19 -16
- package/src/createRoutes.ts +8 -8
- package/src/index.ts +58 -63
- package/src/services/authentification/AuthenticationService.ts +37 -37
- package/src/services/authentification/types.ts +6 -6
- package/src/services/user/UserAccountGoogleService.ts +8 -9
- package/src/services/user/UserAccountSlackService.ts +8 -9
- package/src/services/user/UserAccountsService.ts +23 -25
- package/src/services/user/types.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/cookies.ts +8 -8
- package/src/utils/createFindLoggedInUser.ts +9 -9
- package/src/utils/generators.ts +4 -4
- package/strategies/dropbox.js +7 -7
- package/strategies/facebook.js +7 -7
- package/strategies/foursquare.js +7 -7
- package/strategies/github.js +7 -7
- package/strategies/google.js +7 -7
- package/strategies/slack.js +7 -7
- package/strategies/strategies.d.ts +7 -7
- package/dist/index-node18.mjs.map +0 -1
- package/src/.eslintrc.json +0 -38
- package/strategies/.eslintrc.json +0 -3
package/src/index.ts
CHANGED
|
@@ -1,44 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import type MongoUsersManager from './MongoUsersManager';
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import type { Context, ContextState, NodeApplication } from "alp-node";
|
|
4
|
+
import jsonwebtoken from "jsonwebtoken";
|
|
5
|
+
import { Logger } from "nightingale-logger";
|
|
6
|
+
import type MongoUsersManager from "./MongoUsersManager";
|
|
8
7
|
import type {
|
|
9
8
|
AuthController as AuthControllerType,
|
|
10
9
|
AuthHooks,
|
|
11
|
-
} from
|
|
12
|
-
import { createAuthController } from
|
|
13
|
-
import type { AuthRoutes as AuthRoutesType } from
|
|
14
|
-
import { createRoutes } from
|
|
15
|
-
import type { Strategies } from
|
|
16
|
-
import { AuthenticationService } from
|
|
17
|
-
import type { AllowedStrategyKeys } from
|
|
18
|
-
import UserAccountsService from
|
|
19
|
-
import type { AccountService } from
|
|
20
|
-
import type { User, UserSanitized } from
|
|
10
|
+
} from "./createAuthController";
|
|
11
|
+
import { createAuthController } from "./createAuthController";
|
|
12
|
+
import type { AuthRoutes as AuthRoutesType } from "./createRoutes";
|
|
13
|
+
import { createRoutes } from "./createRoutes";
|
|
14
|
+
import type { Strategies } from "./services/authentification/AuthenticationService";
|
|
15
|
+
import { AuthenticationService } from "./services/authentification/AuthenticationService";
|
|
16
|
+
import type { AllowedStrategyKeys } from "./services/authentification/types";
|
|
17
|
+
import UserAccountsService from "./services/user/UserAccountsService";
|
|
18
|
+
import type { AccountService } from "./services/user/types";
|
|
19
|
+
import type { User, UserSanitized } from "./types";
|
|
21
20
|
import {
|
|
22
|
-
getTokenFromRequest,
|
|
23
|
-
COOKIE_NAME_TOKEN,
|
|
24
21
|
COOKIE_NAME_STATE,
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
COOKIE_NAME_TOKEN,
|
|
23
|
+
getTokenFromRequest,
|
|
24
|
+
} from "./utils/cookies";
|
|
25
|
+
import { createFindLoggedInUser } from "./utils/createFindLoggedInUser";
|
|
27
26
|
|
|
28
|
-
export { default as MongoUsersManager } from
|
|
29
|
-
export { default as UserAccountGoogleService } from
|
|
30
|
-
export { default as UserAccountSlackService } from
|
|
31
|
-
export { authSocketIO } from
|
|
32
|
-
export { createAuthApolloContext } from
|
|
33
|
-
export { STATUSES } from
|
|
27
|
+
export { default as MongoUsersManager } from "./MongoUsersManager";
|
|
28
|
+
export { default as UserAccountGoogleService } from "./services/user/UserAccountGoogleService";
|
|
29
|
+
export { default as UserAccountSlackService } from "./services/user/UserAccountSlackService";
|
|
30
|
+
export { authSocketIO } from "./authSocketIO";
|
|
31
|
+
export { createAuthApolloContext } from "./authApolloContext";
|
|
32
|
+
export { STATUSES } from "./services/user/UserAccountsService";
|
|
34
33
|
|
|
35
|
-
export * from
|
|
34
|
+
export type * from "./types";
|
|
36
35
|
|
|
37
|
-
declare module
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
36
|
+
declare module "alp-node" {
|
|
39
37
|
interface ContextState {
|
|
40
38
|
loggedInUserId:
|
|
41
|
-
| NonNullable<ContextState[
|
|
39
|
+
| NonNullable<ContextState["loggedInUser"]>["_id"]
|
|
42
40
|
| null
|
|
43
41
|
| undefined;
|
|
44
42
|
loggedInUser: User | null | undefined;
|
|
@@ -46,7 +44,7 @@ declare module 'alp-node' {
|
|
|
46
44
|
|
|
47
45
|
interface ContextSanitizedState {
|
|
48
46
|
loggedInUserId:
|
|
49
|
-
| NonNullable<ContextSanitizedState[
|
|
47
|
+
| NonNullable<ContextSanitizedState["loggedInUser"]>["_id"]
|
|
50
48
|
| null
|
|
51
49
|
| undefined;
|
|
52
50
|
loggedInUser: UserSanitized | null | undefined;
|
|
@@ -54,23 +52,23 @@ declare module 'alp-node' {
|
|
|
54
52
|
|
|
55
53
|
interface BaseContext {
|
|
56
54
|
setLoggedIn: (
|
|
57
|
-
loggedInUserId: NonNullable<ContextState[
|
|
58
|
-
loggedInUser: NonNullable<ContextState[
|
|
55
|
+
loggedInUserId: NonNullable<ContextState["loggedInUserId"]>,
|
|
56
|
+
loggedInUser: NonNullable<ContextState["loggedInUser"]>,
|
|
59
57
|
) => Promise<void>;
|
|
60
58
|
logout: () => void;
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
const logger = new Logger(
|
|
62
|
+
const logger = new Logger("alp:auth");
|
|
65
63
|
|
|
66
64
|
const signPromisified: any = promisify(jsonwebtoken.sign);
|
|
67
65
|
|
|
68
66
|
export type AuthController = AuthControllerType;
|
|
69
67
|
export type AuthRoutes = AuthRoutesType;
|
|
70
|
-
export { AuthenticationService } from
|
|
68
|
+
export { AuthenticationService } from "./services/authentification/AuthenticationService";
|
|
71
69
|
|
|
72
70
|
export default function init<
|
|
73
|
-
StrategyKeys extends AllowedStrategyKeys =
|
|
71
|
+
StrategyKeys extends AllowedStrategyKeys = "google",
|
|
74
72
|
U extends User = User,
|
|
75
73
|
USanitized extends UserSanitized = UserSanitized,
|
|
76
74
|
>({
|
|
@@ -111,14 +109,14 @@ export default function init<
|
|
|
111
109
|
authHooks,
|
|
112
110
|
});
|
|
113
111
|
|
|
114
|
-
app.context.setLoggedIn = async function (
|
|
112
|
+
app.context.setLoggedIn = async function setLoggedIn(
|
|
115
113
|
this: Context,
|
|
116
|
-
loggedInUserId: NonNullable<ContextState[
|
|
117
|
-
loggedInUser: NonNullable<ContextState[
|
|
114
|
+
loggedInUserId: NonNullable<ContextState["loggedInUser"]>["_id"],
|
|
115
|
+
loggedInUser: NonNullable<ContextState["loggedInUser"]>,
|
|
118
116
|
): Promise<void> {
|
|
119
|
-
logger.debug(
|
|
117
|
+
logger.debug("setLoggedIn", { loggedInUser });
|
|
120
118
|
if (!loggedInUserId) {
|
|
121
|
-
throw new Error(
|
|
119
|
+
throw new Error("Illegal value for setLoggedIn");
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
this.state.loggedInUserId = loggedInUserId;
|
|
@@ -127,12 +125,12 @@ export default function init<
|
|
|
127
125
|
const token = await signPromisified(
|
|
128
126
|
{ loggedInUserId, time: Date.now() },
|
|
129
127
|
this.config
|
|
130
|
-
.get<Map<string, unknown>>(
|
|
131
|
-
.get(
|
|
128
|
+
.get<Map<string, unknown>>("authentication")
|
|
129
|
+
.get("secretKey"),
|
|
132
130
|
{
|
|
133
|
-
algorithm:
|
|
134
|
-
audience: jwtAudience || this.request.headers[
|
|
135
|
-
expiresIn:
|
|
131
|
+
algorithm: "HS512",
|
|
132
|
+
audience: jwtAudience || this.request.headers["user-agent"],
|
|
133
|
+
expiresIn: "30 days",
|
|
136
134
|
},
|
|
137
135
|
);
|
|
138
136
|
|
|
@@ -142,10 +140,9 @@ export default function init<
|
|
|
142
140
|
return date.getTime();
|
|
143
141
|
};
|
|
144
142
|
|
|
145
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
146
143
|
this.cookies.set(COOKIE_NAME_TOKEN, token, {
|
|
147
144
|
httpOnly: true,
|
|
148
|
-
secure: this.config.get(
|
|
145
|
+
secure: this.config.get("allowHttps"),
|
|
149
146
|
});
|
|
150
147
|
|
|
151
148
|
this.cookies.set(
|
|
@@ -153,22 +150,20 @@ export default function init<
|
|
|
153
150
|
JSON.stringify({ loggedInUserId, expiresIn: calcExpiresTime() }),
|
|
154
151
|
{
|
|
155
152
|
httpOnly: false,
|
|
156
|
-
secure: this.config.get(
|
|
153
|
+
secure: this.config.get("allowHttps"),
|
|
157
154
|
},
|
|
158
155
|
);
|
|
159
156
|
};
|
|
160
157
|
|
|
161
|
-
app.context.logout = function (this: Context): void {
|
|
158
|
+
app.context.logout = function logout(this: Context): void {
|
|
162
159
|
delete this.state.loggedInUserId;
|
|
163
160
|
delete this.state.loggedInUser;
|
|
164
|
-
this.cookies.set(COOKIE_NAME_TOKEN,
|
|
165
|
-
this.cookies.set(COOKIE_NAME_STATE,
|
|
161
|
+
this.cookies.set(COOKIE_NAME_TOKEN, "", { expires: new Date(1) });
|
|
162
|
+
this.cookies.set(COOKIE_NAME_STATE, "", { expires: new Date(1) });
|
|
166
163
|
};
|
|
167
164
|
|
|
168
165
|
const findLoggedInUser = createFindLoggedInUser(
|
|
169
|
-
app.config
|
|
170
|
-
.get<Map<string, unknown>>('authentication')
|
|
171
|
-
.get('secretKey') as string,
|
|
166
|
+
app.config.get<{ secretKey: string }>("authentication").secretKey,
|
|
172
167
|
usersManager,
|
|
173
168
|
logger,
|
|
174
169
|
);
|
|
@@ -180,7 +175,7 @@ export default function init<
|
|
|
180
175
|
): ReturnType<typeof findLoggedInUser> => {
|
|
181
176
|
const token = getTokenFromRequest(req);
|
|
182
177
|
return findLoggedInUser(
|
|
183
|
-
jwtAudience || req.headers[
|
|
178
|
+
jwtAudience || req.headers["user-agent"],
|
|
184
179
|
token,
|
|
185
180
|
);
|
|
186
181
|
},
|
|
@@ -190,11 +185,11 @@ export default function init<
|
|
|
190
185
|
next: () => Promise<T> | T,
|
|
191
186
|
): Promise<T> => {
|
|
192
187
|
const token = ctx.cookies.get(COOKIE_NAME_TOKEN);
|
|
193
|
-
const userAgent = ctx.request.headers[
|
|
194
|
-
logger.debug(
|
|
188
|
+
const userAgent = ctx.request.headers["user-agent"];
|
|
189
|
+
logger.debug("middleware", { token });
|
|
195
190
|
|
|
196
191
|
const setState = (
|
|
197
|
-
loggedInUserId: U[
|
|
192
|
+
loggedInUserId: U["_id"] | null | undefined,
|
|
198
193
|
loggedInUser: U | null | undefined,
|
|
199
194
|
): void => {
|
|
200
195
|
ctx.state.loggedInUserId = loggedInUserId;
|
|
@@ -208,12 +203,12 @@ export default function init<
|
|
|
208
203
|
jwtAudience || userAgent,
|
|
209
204
|
token,
|
|
210
205
|
);
|
|
211
|
-
logger.debug(
|
|
206
|
+
logger.debug("middleware", { loggedInUserId });
|
|
212
207
|
|
|
213
208
|
if (loggedInUserId == null || loggedInUser == null) {
|
|
214
209
|
if (token) {
|
|
215
|
-
ctx.cookies.set(COOKIE_NAME_TOKEN,
|
|
216
|
-
ctx.cookies.set(COOKIE_NAME_STATE,
|
|
210
|
+
ctx.cookies.set(COOKIE_NAME_TOKEN, "", { expires: new Date(1) });
|
|
211
|
+
ctx.cookies.set(COOKIE_NAME_STATE, "", { expires: new Date(1) });
|
|
217
212
|
}
|
|
218
213
|
setState(null, null);
|
|
219
214
|
return next();
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
2
2
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
4
|
-
/* eslint-disable camelcase
|
|
5
|
-
import { EventEmitter } from
|
|
6
|
-
import type { Context, NodeConfig } from
|
|
7
|
-
import { Logger } from
|
|
8
|
-
import type { Strategy as Oauth2Strategy } from
|
|
9
|
-
import type { AccountId, User,
|
|
10
|
-
import { randomHex } from
|
|
11
|
-
import type UserAccountsService from
|
|
12
|
-
import type { AllowedStrategyKeys, Tokens } from
|
|
13
|
-
|
|
14
|
-
const logger = new Logger(
|
|
4
|
+
/* eslint-disable camelcase */
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
|
+
import type { Context, NodeConfig } from "alp-node";
|
|
7
|
+
import { Logger } from "nightingale-logger";
|
|
8
|
+
import type { Strategy as Oauth2Strategy } from "../../../strategies/strategies.d";
|
|
9
|
+
import type { Account, AccountId, User, UserSanitized } from "../../types";
|
|
10
|
+
import { randomHex } from "../../utils/generators";
|
|
11
|
+
import type UserAccountsService from "../user/UserAccountsService";
|
|
12
|
+
import type { AllowedStrategyKeys, Tokens } from "./types";
|
|
13
|
+
|
|
14
|
+
const logger = new Logger("alp:auth:authentication");
|
|
15
15
|
|
|
16
16
|
export interface GenerateAuthUrlOptions {
|
|
17
17
|
accessType?: string;
|
|
@@ -72,13 +72,13 @@ export class AuthenticationService<
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
generateAuthUrl<T extends StrategyKeys>(strategy: T, params: any): string {
|
|
75
|
-
logger.debug(
|
|
75
|
+
logger.debug("generateAuthUrl", { strategy, params });
|
|
76
76
|
const strategyInstance = this.strategies[strategy];
|
|
77
77
|
switch (strategyInstance.type) {
|
|
78
|
-
case
|
|
78
|
+
case "oauth2":
|
|
79
79
|
return strategyInstance.oauth2.authorizationCode.authorizeURL(params);
|
|
80
80
|
default:
|
|
81
|
-
throw new Error(
|
|
81
|
+
throw new Error("Invalid strategy");
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -86,10 +86,10 @@ export class AuthenticationService<
|
|
|
86
86
|
strategy: StrategyKeys,
|
|
87
87
|
options: GetTokensOptions,
|
|
88
88
|
): Promise<Tokens> {
|
|
89
|
-
logger.debug(
|
|
89
|
+
logger.debug("getTokens", { strategy, options });
|
|
90
90
|
const strategyInstance = this.strategies[strategy];
|
|
91
91
|
switch (strategyInstance.type) {
|
|
92
|
-
case
|
|
92
|
+
case "oauth2": {
|
|
93
93
|
const result = await strategyInstance.oauth2.authorizationCode.getToken(
|
|
94
94
|
{
|
|
95
95
|
code: options.code,
|
|
@@ -116,7 +116,7 @@ export class AuthenticationService<
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
default:
|
|
119
|
-
throw new Error(
|
|
119
|
+
throw new Error("Invalid stategy");
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -124,13 +124,13 @@ export class AuthenticationService<
|
|
|
124
124
|
strategy: StrategyKeys,
|
|
125
125
|
tokensParam: { refreshToken: string },
|
|
126
126
|
): Promise<Tokens> {
|
|
127
|
-
logger.debug(
|
|
127
|
+
logger.debug("refreshToken", { strategy });
|
|
128
128
|
if (!tokensParam.refreshToken) {
|
|
129
|
-
throw new Error(
|
|
129
|
+
throw new Error("Missing refresh token");
|
|
130
130
|
}
|
|
131
131
|
const strategyInstance = this.strategies[strategy];
|
|
132
132
|
switch (strategyInstance.type) {
|
|
133
|
-
case
|
|
133
|
+
case "oauth2": {
|
|
134
134
|
const token = strategyInstance.oauth2.clientCredentials.createToken({
|
|
135
135
|
refresh_token: tokensParam.refreshToken,
|
|
136
136
|
});
|
|
@@ -151,15 +151,15 @@ export class AuthenticationService<
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
default:
|
|
154
|
-
throw new Error(
|
|
154
|
+
throw new Error("Invalid stategy");
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
redirectUri(ctx: Context, strategy: string): string {
|
|
159
|
-
const host = `http${this.config.get(
|
|
159
|
+
const host = `http${this.config.get("allowHttps") ? "s" : ""}://${
|
|
160
160
|
ctx.request.host
|
|
161
161
|
}`;
|
|
162
|
-
return `${host}${ctx.urlGenerator(
|
|
162
|
+
return `${host}${ctx.urlGenerator("authResponse", {
|
|
163
163
|
strategy,
|
|
164
164
|
})}`;
|
|
165
165
|
}
|
|
@@ -180,18 +180,18 @@ export class AuthenticationService<
|
|
|
180
180
|
},
|
|
181
181
|
params?: any,
|
|
182
182
|
): Promise<void> {
|
|
183
|
-
logger.debug(
|
|
183
|
+
logger.debug("redirectAuthUrl", { strategy, scopeKey, refreshToken });
|
|
184
184
|
const state = await randomHex(8);
|
|
185
|
-
const isLoginAccess = !scopeKey || scopeKey ===
|
|
185
|
+
const isLoginAccess = !scopeKey || scopeKey === "login";
|
|
186
186
|
const scope = this.userAccountsService.getScope(
|
|
187
187
|
strategy,
|
|
188
|
-
scopeKey ||
|
|
188
|
+
scopeKey || "login",
|
|
189
189
|
user,
|
|
190
190
|
accountId,
|
|
191
191
|
);
|
|
192
192
|
|
|
193
193
|
if (!scope) {
|
|
194
|
-
throw new Error(
|
|
194
|
+
throw new Error("Invalid empty scope");
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
ctx.cookies.set(
|
|
@@ -204,14 +204,14 @@ export class AuthenticationService<
|
|
|
204
204
|
{
|
|
205
205
|
maxAge: 10 * 60 * 1000,
|
|
206
206
|
httpOnly: true,
|
|
207
|
-
secure: this.config.get(
|
|
207
|
+
secure: this.config.get("allowHttps"),
|
|
208
208
|
},
|
|
209
209
|
);
|
|
210
210
|
const redirectUri = this.generateAuthUrl(strategy, {
|
|
211
211
|
redirect_uri: this.redirectUri(ctx, strategy),
|
|
212
212
|
scope,
|
|
213
213
|
state,
|
|
214
|
-
access_type: refreshToken ?
|
|
214
|
+
access_type: refreshToken ? "offline" : "online",
|
|
215
215
|
...params,
|
|
216
216
|
});
|
|
217
217
|
|
|
@@ -224,29 +224,29 @@ export class AuthenticationService<
|
|
|
224
224
|
isLoggedIn: boolean,
|
|
225
225
|
hooks: AccessResponseHooks<StrategyKeys, U>,
|
|
226
226
|
): Promise<U> {
|
|
227
|
-
const errorParam = ctx.params.queryParam(
|
|
227
|
+
const errorParam = ctx.params.queryParam("error").notEmpty();
|
|
228
228
|
if (errorParam.isValid()) {
|
|
229
|
-
ctx.throw(errorParam.value
|
|
229
|
+
ctx.throw(403, errorParam.value);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
const code = ctx.validParams.queryParam(
|
|
233
|
-
const state = ctx.validParams.queryParam(
|
|
232
|
+
const code = ctx.validParams.queryParam("code").notEmpty().value;
|
|
233
|
+
const state = ctx.validParams.queryParam("state").notEmpty().value;
|
|
234
234
|
|
|
235
235
|
const cookieName = `auth_${strategy}_${state}`;
|
|
236
236
|
const cookie = ctx.cookies.get(cookieName);
|
|
237
|
-
ctx.cookies.set(cookieName,
|
|
237
|
+
ctx.cookies.set(cookieName, "", { expires: new Date(1) });
|
|
238
238
|
if (!cookie) {
|
|
239
|
-
throw new Error(
|
|
239
|
+
throw new Error("No cookie for this state");
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
const parsedCookie = JSON.parse(cookie);
|
|
243
243
|
if (!parsedCookie?.scope) {
|
|
244
|
-
throw new Error(
|
|
244
|
+
throw new Error("Unexpected cookie value");
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
if (!parsedCookie.isLoginAccess) {
|
|
248
248
|
if (!isLoggedIn) {
|
|
249
|
-
throw new Error(
|
|
249
|
+
throw new Error("You are not connected");
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export type GoogleParams =
|
|
2
|
-
|
|
|
3
|
-
|
|
|
4
|
-
|
|
|
5
|
-
|
|
|
6
|
-
export type SlackParams =
|
|
2
|
+
| "access_type"
|
|
3
|
+
| "include_granted_scopes"
|
|
4
|
+
| "login_hint"
|
|
5
|
+
| "prompt";
|
|
6
|
+
export type SlackParams = "client_id" | "team";
|
|
7
7
|
|
|
8
|
-
export type AllowedStrategyKeys =
|
|
8
|
+
export type AllowedStrategyKeys = "google" | "slack";
|
|
9
9
|
|
|
10
10
|
export interface AllowedMapParamsStrategy {
|
|
11
11
|
google: GoogleParams;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/class-methods-use-this */
|
|
2
1
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
2
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
4
|
-
import type { Tokens } from
|
|
5
|
-
import type { AccountService, FullName } from
|
|
3
|
+
import type { Tokens } from "../authentification/types";
|
|
4
|
+
import type { AccountService, FullName } from "./types";
|
|
6
5
|
|
|
7
|
-
export default class UserAccountGoogleService<ScopeKeys extends
|
|
6
|
+
export default class UserAccountGoogleService<ScopeKeys extends "login">
|
|
8
7
|
implements AccountService<ScopeKeys>
|
|
9
8
|
{
|
|
10
9
|
scopeKeyToScope: Record<ScopeKeys, string>;
|
|
11
10
|
|
|
12
|
-
constructor(scopeKeyToScope: Record<Exclude<
|
|
11
|
+
constructor(scopeKeyToScope: Record<Exclude<"login", ScopeKeys>, string>) {
|
|
13
12
|
this.scopeKeyToScope = {
|
|
14
13
|
...scopeKeyToScope,
|
|
15
|
-
login:
|
|
14
|
+
login: "openid profile email",
|
|
16
15
|
};
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
providerKey =
|
|
18
|
+
providerKey = "google";
|
|
20
19
|
|
|
21
20
|
getProfile(tokens: Tokens): Promise<any> {
|
|
22
21
|
return fetch(
|
|
@@ -59,8 +58,8 @@ export default class UserAccountGoogleService<ScopeKeys extends 'login'>
|
|
|
59
58
|
|
|
60
59
|
getScope(oldScope: string[] | undefined, newScope: string): string[] {
|
|
61
60
|
return !oldScope
|
|
62
|
-
? newScope.split(
|
|
63
|
-
: [...oldScope, ...newScope.split(
|
|
61
|
+
? newScope.split(" ")
|
|
62
|
+
: [...oldScope, ...newScope.split(" ")].filter(
|
|
64
63
|
(item, i, ar) => ar.indexOf(item) === i,
|
|
65
64
|
);
|
|
66
65
|
}
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/class-methods-use-this */
|
|
2
1
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
3
|
-
import type { Tokens } from
|
|
4
|
-
import type { AccountService, FullName } from
|
|
2
|
+
import type { Tokens } from "../authentification/types";
|
|
3
|
+
import type { AccountService, FullName } from "./types";
|
|
5
4
|
|
|
6
5
|
// https://api.slack.com/methods/users.identity
|
|
7
6
|
|
|
8
|
-
export default class UserAccountSlackService<ScopeKeys extends
|
|
7
|
+
export default class UserAccountSlackService<ScopeKeys extends "login">
|
|
9
8
|
implements AccountService<ScopeKeys>
|
|
10
9
|
{
|
|
11
10
|
scopeKeyToScope: Record<ScopeKeys, string>;
|
|
12
11
|
|
|
13
|
-
constructor(scopeKeyToScope: Record<Exclude<
|
|
12
|
+
constructor(scopeKeyToScope: Record<Exclude<"login", ScopeKeys>, string>) {
|
|
14
13
|
this.scopeKeyToScope = {
|
|
15
14
|
...scopeKeyToScope,
|
|
16
|
-
login:
|
|
15
|
+
login: "identity.basic identity.email identity.avatar",
|
|
17
16
|
};
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
providerKey =
|
|
19
|
+
providerKey = "google";
|
|
21
20
|
|
|
22
21
|
getProfile(tokens: Tokens): Promise<any> {
|
|
23
22
|
return fetch(
|
|
@@ -56,8 +55,8 @@ export default class UserAccountSlackService<ScopeKeys extends 'login'>
|
|
|
56
55
|
|
|
57
56
|
getScope(oldScope: string[] | undefined, newScope: string): string[] {
|
|
58
57
|
return !oldScope
|
|
59
|
-
? newScope.split(
|
|
60
|
-
: [...oldScope, ...newScope.split(
|
|
58
|
+
? newScope.split(" ")
|
|
59
|
+
: [...oldScope, ...newScope.split(" ")].filter(
|
|
61
60
|
(item, i, ar) => ar.indexOf(item) === i,
|
|
62
61
|
);
|
|
63
62
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
import type { AccountService, TokensObject } from './types';
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import { Logger } from "nightingale-logger";
|
|
3
|
+
import type MongoUsersManager from "../../MongoUsersManager";
|
|
4
|
+
import type { Account, AccountId, User, UserSanitized } from "../../types";
|
|
5
|
+
import type { AllowedStrategyKeys } from "../authentification/types";
|
|
6
|
+
import type { AccountService, TokensObject } from "./types";
|
|
8
7
|
|
|
9
|
-
const logger = new Logger(
|
|
8
|
+
const logger = new Logger("alp:auth:userAccounts");
|
|
10
9
|
|
|
11
10
|
export const STATUSES = {
|
|
12
|
-
VALIDATED:
|
|
13
|
-
DELETED:
|
|
11
|
+
VALIDATED: "validated",
|
|
12
|
+
DELETED: "deleted",
|
|
14
13
|
};
|
|
15
14
|
|
|
16
15
|
export default class UserAccountsService<
|
|
@@ -38,13 +37,13 @@ export default class UserAccountsService<
|
|
|
38
37
|
user?: U,
|
|
39
38
|
accountId?: AccountId,
|
|
40
39
|
): string {
|
|
41
|
-
logger.debug(
|
|
40
|
+
logger.debug("getScope", { strategy, userId: user?._id });
|
|
42
41
|
const service = this.strategyToService[strategy];
|
|
43
42
|
if (!service) {
|
|
44
|
-
throw new Error(
|
|
43
|
+
throw new Error("Strategy not supported");
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
const newScope = service.scopeKeyToScope[scopeKey]
|
|
46
|
+
const newScope = service.scopeKeyToScope[scopeKey]!;
|
|
48
47
|
if (!user || !accountId) {
|
|
49
48
|
return newScope;
|
|
50
49
|
}
|
|
@@ -54,9 +53,9 @@ export default class UserAccountsService<
|
|
|
54
53
|
);
|
|
55
54
|
|
|
56
55
|
if (!account) {
|
|
57
|
-
throw new Error(
|
|
56
|
+
throw new Error("Could not found associated account");
|
|
58
57
|
}
|
|
59
|
-
return service.getScope(account.scope, newScope).join(
|
|
58
|
+
return service.getScope(account.scope, newScope).join(" ");
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
async update(
|
|
@@ -65,7 +64,7 @@ export default class UserAccountsService<
|
|
|
65
64
|
tokens: TokensObject,
|
|
66
65
|
scope: string,
|
|
67
66
|
subservice: string,
|
|
68
|
-
): Promise<{ user: U; account: U[
|
|
67
|
+
): Promise<{ user: U; account: U["accounts"][number] }> {
|
|
69
68
|
const service = this.strategyToService[strategy];
|
|
70
69
|
const profile = await service.getProfile(tokens);
|
|
71
70
|
const accountId = service.getId(profile);
|
|
@@ -76,9 +75,9 @@ export default class UserAccountsService<
|
|
|
76
75
|
if (!account) {
|
|
77
76
|
// TODO check if already exists in other user => merge
|
|
78
77
|
// TODO else add a new account in this user
|
|
79
|
-
throw new Error(
|
|
78
|
+
throw new Error("Could not found associated account");
|
|
80
79
|
}
|
|
81
|
-
account.status =
|
|
80
|
+
account.status = "valid";
|
|
82
81
|
account.accessToken = tokens.accessToken;
|
|
83
82
|
if (tokens.refreshToken) {
|
|
84
83
|
account.refreshToken = tokens.refreshToken;
|
|
@@ -103,11 +102,11 @@ export default class UserAccountsService<
|
|
|
103
102
|
subservice: string,
|
|
104
103
|
): Promise<U> {
|
|
105
104
|
const service = this.strategyToService[strategy];
|
|
106
|
-
if (!service) throw new Error(
|
|
105
|
+
if (!service) throw new Error("Strategy not supported");
|
|
107
106
|
|
|
108
107
|
const profile = await service.getProfile(tokens);
|
|
109
108
|
const accountId = service.getId(profile);
|
|
110
|
-
if (!accountId) throw new Error(
|
|
109
|
+
if (!accountId) throw new Error("Invalid profile: no id found");
|
|
111
110
|
|
|
112
111
|
const emails = service.getEmails(profile);
|
|
113
112
|
|
|
@@ -118,7 +117,7 @@ export default class UserAccountsService<
|
|
|
118
117
|
emails,
|
|
119
118
|
});
|
|
120
119
|
|
|
121
|
-
logger.info(!user ?
|
|
120
|
+
logger.info(!user ? "create user" : "existing user", {
|
|
122
121
|
userId: user?._id,
|
|
123
122
|
accountId,
|
|
124
123
|
/*emails , user*/
|
|
@@ -143,12 +142,11 @@ export default class UserAccountsService<
|
|
|
143
142
|
|
|
144
143
|
if (!account) {
|
|
145
144
|
account = { provider: strategy, accountId };
|
|
146
|
-
|
|
147
|
-
user.accounts.push(account);
|
|
145
|
+
user.accounts.push(account as Account);
|
|
148
146
|
}
|
|
149
147
|
|
|
150
148
|
account.name = service.getAccountName(profile);
|
|
151
|
-
account.status =
|
|
149
|
+
account.status = "valid";
|
|
152
150
|
account.profile = profile;
|
|
153
151
|
account.accessToken = tokens.accessToken;
|
|
154
152
|
if (tokens.refreshToken) {
|
|
@@ -176,7 +174,7 @@ export default class UserAccountsService<
|
|
|
176
174
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
177
175
|
...user.emails.reduce(
|
|
178
176
|
(domains: Set<string>, email: string) =>
|
|
179
|
-
domains.add(email.split(
|
|
177
|
+
domains.add(email.split("@", 2)[1]!),
|
|
180
178
|
new Set<string>(),
|
|
181
179
|
),
|
|
182
180
|
];
|
|
@@ -12,7 +12,7 @@ export interface TokensObject {
|
|
|
12
12
|
idToken: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export interface AccountService<ScopeKeys extends
|
|
15
|
+
export interface AccountService<ScopeKeys extends "login"> {
|
|
16
16
|
scopeKeyToScope: Record<ScopeKeys, string>;
|
|
17
17
|
providerKey: string;
|
|
18
18
|
|
package/src/types.ts
CHANGED