alp-node-auth 9.3.0 → 11.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 -26
- 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 -2
- package/dist/definitions/services/authentification/types.d.ts.map +1 -1
- 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-node20.mjs} +115 -124
- package/dist/index-node20.mjs.map +1 -0
- package/package.json +24 -26
- package/src/MongoUsersManager.ts +5 -6
- package/src/authApolloContext.ts +10 -10
- package/src/authSocketIO.ts +10 -10
- package/src/createAuthController.ts +22 -20
- package/src/createRoutes.ts +8 -8
- package/src/index.ts +58 -64
- package/src/services/authentification/AuthenticationService.ts +56 -53
- package/src/services/authentification/types.ts +7 -2
- package/src/services/user/UserAccountGoogleService.ts +9 -9
- package/src/services/user/UserAccountSlackService.ts +9 -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 +8 -3
- 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,45 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { Logger } from 'nightingale-logger';
|
|
8
|
-
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";
|
|
9
7
|
import type {
|
|
10
8
|
AuthController as AuthControllerType,
|
|
11
9
|
AuthHooks,
|
|
12
|
-
} from
|
|
13
|
-
import { createAuthController } from
|
|
14
|
-
import type { AuthRoutes as AuthRoutesType } from
|
|
15
|
-
import { createRoutes } from
|
|
16
|
-
import type { Strategies } from
|
|
17
|
-
import { AuthenticationService } from
|
|
18
|
-
import type { AllowedStrategyKeys } from
|
|
19
|
-
import UserAccountsService from
|
|
20
|
-
import type { AccountService } from
|
|
21
|
-
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";
|
|
22
20
|
import {
|
|
23
|
-
getTokenFromRequest,
|
|
24
|
-
COOKIE_NAME_TOKEN,
|
|
25
21
|
COOKIE_NAME_STATE,
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
COOKIE_NAME_TOKEN,
|
|
23
|
+
getTokenFromRequest,
|
|
24
|
+
} from "./utils/cookies";
|
|
25
|
+
import { createFindLoggedInUser } from "./utils/createFindLoggedInUser";
|
|
28
26
|
|
|
29
|
-
export { default as MongoUsersManager } from
|
|
30
|
-
export { default as UserAccountGoogleService } from
|
|
31
|
-
export { default as UserAccountSlackService } from
|
|
32
|
-
export { authSocketIO } from
|
|
33
|
-
export { createAuthApolloContext } from
|
|
34
|
-
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";
|
|
35
33
|
|
|
36
|
-
export * from
|
|
34
|
+
export type * from "./types";
|
|
37
35
|
|
|
38
|
-
declare module
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
36
|
+
declare module "alp-node" {
|
|
40
37
|
interface ContextState {
|
|
41
38
|
loggedInUserId:
|
|
42
|
-
| NonNullable<ContextState[
|
|
39
|
+
| NonNullable<ContextState["loggedInUser"]>["_id"]
|
|
43
40
|
| null
|
|
44
41
|
| undefined;
|
|
45
42
|
loggedInUser: User | null | undefined;
|
|
@@ -47,7 +44,7 @@ declare module 'alp-types' {
|
|
|
47
44
|
|
|
48
45
|
interface ContextSanitizedState {
|
|
49
46
|
loggedInUserId:
|
|
50
|
-
| NonNullable<ContextSanitizedState[
|
|
47
|
+
| NonNullable<ContextSanitizedState["loggedInUser"]>["_id"]
|
|
51
48
|
| null
|
|
52
49
|
| undefined;
|
|
53
50
|
loggedInUser: UserSanitized | null | undefined;
|
|
@@ -55,23 +52,23 @@ declare module 'alp-types' {
|
|
|
55
52
|
|
|
56
53
|
interface BaseContext {
|
|
57
54
|
setLoggedIn: (
|
|
58
|
-
loggedInUserId: NonNullable<ContextState[
|
|
59
|
-
loggedInUser: NonNullable<ContextState[
|
|
55
|
+
loggedInUserId: NonNullable<ContextState["loggedInUserId"]>,
|
|
56
|
+
loggedInUser: NonNullable<ContextState["loggedInUser"]>,
|
|
60
57
|
) => Promise<void>;
|
|
61
58
|
logout: () => void;
|
|
62
59
|
}
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
const logger = new Logger(
|
|
62
|
+
const logger = new Logger("alp:auth");
|
|
66
63
|
|
|
67
64
|
const signPromisified: any = promisify(jsonwebtoken.sign);
|
|
68
65
|
|
|
69
66
|
export type AuthController = AuthControllerType;
|
|
70
67
|
export type AuthRoutes = AuthRoutesType;
|
|
71
|
-
export { AuthenticationService } from
|
|
68
|
+
export { AuthenticationService } from "./services/authentification/AuthenticationService";
|
|
72
69
|
|
|
73
70
|
export default function init<
|
|
74
|
-
StrategyKeys extends AllowedStrategyKeys =
|
|
71
|
+
StrategyKeys extends AllowedStrategyKeys = "google",
|
|
75
72
|
U extends User = User,
|
|
76
73
|
USanitized extends UserSanitized = UserSanitized,
|
|
77
74
|
>({
|
|
@@ -112,14 +109,14 @@ export default function init<
|
|
|
112
109
|
authHooks,
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
app.context.setLoggedIn = async function (
|
|
112
|
+
app.context.setLoggedIn = async function setLoggedIn(
|
|
116
113
|
this: Context,
|
|
117
|
-
loggedInUserId: NonNullable<ContextState[
|
|
118
|
-
loggedInUser: NonNullable<ContextState[
|
|
114
|
+
loggedInUserId: NonNullable<ContextState["loggedInUser"]>["_id"],
|
|
115
|
+
loggedInUser: NonNullable<ContextState["loggedInUser"]>,
|
|
119
116
|
): Promise<void> {
|
|
120
|
-
logger.debug(
|
|
117
|
+
logger.debug("setLoggedIn", { loggedInUser });
|
|
121
118
|
if (!loggedInUserId) {
|
|
122
|
-
throw new Error(
|
|
119
|
+
throw new Error("Illegal value for setLoggedIn");
|
|
123
120
|
}
|
|
124
121
|
|
|
125
122
|
this.state.loggedInUserId = loggedInUserId;
|
|
@@ -128,12 +125,12 @@ export default function init<
|
|
|
128
125
|
const token = await signPromisified(
|
|
129
126
|
{ loggedInUserId, time: Date.now() },
|
|
130
127
|
this.config
|
|
131
|
-
.get<Map<string, unknown>>(
|
|
132
|
-
.get(
|
|
128
|
+
.get<Map<string, unknown>>("authentication")
|
|
129
|
+
.get("secretKey"),
|
|
133
130
|
{
|
|
134
|
-
algorithm:
|
|
135
|
-
audience: jwtAudience || this.request.headers[
|
|
136
|
-
expiresIn:
|
|
131
|
+
algorithm: "HS512",
|
|
132
|
+
audience: jwtAudience || this.request.headers["user-agent"],
|
|
133
|
+
expiresIn: "30 days",
|
|
137
134
|
},
|
|
138
135
|
);
|
|
139
136
|
|
|
@@ -143,10 +140,9 @@ export default function init<
|
|
|
143
140
|
return date.getTime();
|
|
144
141
|
};
|
|
145
142
|
|
|
146
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
147
143
|
this.cookies.set(COOKIE_NAME_TOKEN, token, {
|
|
148
144
|
httpOnly: true,
|
|
149
|
-
secure: this.config.get(
|
|
145
|
+
secure: this.config.get("allowHttps"),
|
|
150
146
|
});
|
|
151
147
|
|
|
152
148
|
this.cookies.set(
|
|
@@ -154,22 +150,20 @@ export default function init<
|
|
|
154
150
|
JSON.stringify({ loggedInUserId, expiresIn: calcExpiresTime() }),
|
|
155
151
|
{
|
|
156
152
|
httpOnly: false,
|
|
157
|
-
secure: this.config.get(
|
|
153
|
+
secure: this.config.get("allowHttps"),
|
|
158
154
|
},
|
|
159
155
|
);
|
|
160
156
|
};
|
|
161
157
|
|
|
162
|
-
app.context.logout = function (this: Context): void {
|
|
158
|
+
app.context.logout = function logout(this: Context): void {
|
|
163
159
|
delete this.state.loggedInUserId;
|
|
164
160
|
delete this.state.loggedInUser;
|
|
165
|
-
this.cookies.set(COOKIE_NAME_TOKEN,
|
|
166
|
-
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) });
|
|
167
163
|
};
|
|
168
164
|
|
|
169
165
|
const findLoggedInUser = createFindLoggedInUser(
|
|
170
|
-
app.config
|
|
171
|
-
.get<Map<string, unknown>>('authentication')
|
|
172
|
-
.get('secretKey') as string,
|
|
166
|
+
app.config.get<{ secretKey: string }>("authentication").secretKey,
|
|
173
167
|
usersManager,
|
|
174
168
|
logger,
|
|
175
169
|
);
|
|
@@ -181,7 +175,7 @@ export default function init<
|
|
|
181
175
|
): ReturnType<typeof findLoggedInUser> => {
|
|
182
176
|
const token = getTokenFromRequest(req);
|
|
183
177
|
return findLoggedInUser(
|
|
184
|
-
jwtAudience || req.headers[
|
|
178
|
+
jwtAudience || req.headers["user-agent"],
|
|
185
179
|
token,
|
|
186
180
|
);
|
|
187
181
|
},
|
|
@@ -191,11 +185,11 @@ export default function init<
|
|
|
191
185
|
next: () => Promise<T> | T,
|
|
192
186
|
): Promise<T> => {
|
|
193
187
|
const token = ctx.cookies.get(COOKIE_NAME_TOKEN);
|
|
194
|
-
const userAgent = ctx.request.headers[
|
|
195
|
-
logger.debug(
|
|
188
|
+
const userAgent = ctx.request.headers["user-agent"];
|
|
189
|
+
logger.debug("middleware", { token });
|
|
196
190
|
|
|
197
191
|
const setState = (
|
|
198
|
-
loggedInUserId: U[
|
|
192
|
+
loggedInUserId: U["_id"] | null | undefined,
|
|
199
193
|
loggedInUser: U | null | undefined,
|
|
200
194
|
): void => {
|
|
201
195
|
ctx.state.loggedInUserId = loggedInUserId;
|
|
@@ -209,12 +203,12 @@ export default function init<
|
|
|
209
203
|
jwtAudience || userAgent,
|
|
210
204
|
token,
|
|
211
205
|
);
|
|
212
|
-
logger.debug(
|
|
206
|
+
logger.debug("middleware", { loggedInUserId });
|
|
213
207
|
|
|
214
208
|
if (loggedInUserId == null || loggedInUser == null) {
|
|
215
209
|
if (token) {
|
|
216
|
-
ctx.cookies.set(COOKIE_NAME_TOKEN,
|
|
217
|
-
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) });
|
|
218
212
|
}
|
|
219
213
|
setState(null, null);
|
|
220
214
|
return next();
|
|
@@ -1,18 +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
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const logger = new Logger('alp:auth:authentication');
|
|
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");
|
|
16
15
|
|
|
17
16
|
export interface GenerateAuthUrlOptions {
|
|
18
17
|
accessType?: string;
|
|
@@ -73,13 +72,13 @@ export class AuthenticationService<
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
generateAuthUrl<T extends StrategyKeys>(strategy: T, params: any): string {
|
|
76
|
-
logger.debug(
|
|
75
|
+
logger.debug("generateAuthUrl", { strategy, params });
|
|
77
76
|
const strategyInstance = this.strategies[strategy];
|
|
78
77
|
switch (strategyInstance.type) {
|
|
79
|
-
case
|
|
78
|
+
case "oauth2":
|
|
80
79
|
return strategyInstance.oauth2.authorizationCode.authorizeURL(params);
|
|
81
80
|
default:
|
|
82
|
-
throw new Error(
|
|
81
|
+
throw new Error("Invalid strategy");
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -87,10 +86,10 @@ export class AuthenticationService<
|
|
|
87
86
|
strategy: StrategyKeys,
|
|
88
87
|
options: GetTokensOptions,
|
|
89
88
|
): Promise<Tokens> {
|
|
90
|
-
logger.debug(
|
|
89
|
+
logger.debug("getTokens", { strategy, options });
|
|
91
90
|
const strategyInstance = this.strategies[strategy];
|
|
92
91
|
switch (strategyInstance.type) {
|
|
93
|
-
case
|
|
92
|
+
case "oauth2": {
|
|
94
93
|
const result = await strategyInstance.oauth2.authorizationCode.getToken(
|
|
95
94
|
{
|
|
96
95
|
code: options.code,
|
|
@@ -117,7 +116,7 @@ export class AuthenticationService<
|
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
default:
|
|
120
|
-
throw new Error(
|
|
119
|
+
throw new Error("Invalid stategy");
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
|
|
@@ -125,13 +124,13 @@ export class AuthenticationService<
|
|
|
125
124
|
strategy: StrategyKeys,
|
|
126
125
|
tokensParam: { refreshToken: string },
|
|
127
126
|
): Promise<Tokens> {
|
|
128
|
-
logger.debug(
|
|
127
|
+
logger.debug("refreshToken", { strategy });
|
|
129
128
|
if (!tokensParam.refreshToken) {
|
|
130
|
-
throw new Error(
|
|
129
|
+
throw new Error("Missing refresh token");
|
|
131
130
|
}
|
|
132
131
|
const strategyInstance = this.strategies[strategy];
|
|
133
132
|
switch (strategyInstance.type) {
|
|
134
|
-
case
|
|
133
|
+
case "oauth2": {
|
|
135
134
|
const token = strategyInstance.oauth2.clientCredentials.createToken({
|
|
136
135
|
refresh_token: tokensParam.refreshToken,
|
|
137
136
|
});
|
|
@@ -152,15 +151,15 @@ export class AuthenticationService<
|
|
|
152
151
|
}
|
|
153
152
|
|
|
154
153
|
default:
|
|
155
|
-
throw new Error(
|
|
154
|
+
throw new Error("Invalid stategy");
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
redirectUri(ctx: Context, strategy: string): string {
|
|
160
|
-
const host = `http${this.config.get(
|
|
159
|
+
const host = `http${this.config.get("allowHttps") ? "s" : ""}://${
|
|
161
160
|
ctx.request.host
|
|
162
161
|
}`;
|
|
163
|
-
return `${host}${ctx.urlGenerator(
|
|
162
|
+
return `${host}${ctx.urlGenerator("authResponse", {
|
|
164
163
|
strategy,
|
|
165
164
|
})}`;
|
|
166
165
|
}
|
|
@@ -181,18 +180,18 @@ export class AuthenticationService<
|
|
|
181
180
|
},
|
|
182
181
|
params?: any,
|
|
183
182
|
): Promise<void> {
|
|
184
|
-
logger.debug(
|
|
183
|
+
logger.debug("redirectAuthUrl", { strategy, scopeKey, refreshToken });
|
|
185
184
|
const state = await randomHex(8);
|
|
186
|
-
const isLoginAccess = !scopeKey || scopeKey ===
|
|
185
|
+
const isLoginAccess = !scopeKey || scopeKey === "login";
|
|
187
186
|
const scope = this.userAccountsService.getScope(
|
|
188
187
|
strategy,
|
|
189
|
-
scopeKey ||
|
|
188
|
+
scopeKey || "login",
|
|
190
189
|
user,
|
|
191
190
|
accountId,
|
|
192
191
|
);
|
|
193
192
|
|
|
194
193
|
if (!scope) {
|
|
195
|
-
throw new Error(
|
|
194
|
+
throw new Error("Invalid empty scope");
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
ctx.cookies.set(
|
|
@@ -205,18 +204,18 @@ export class AuthenticationService<
|
|
|
205
204
|
{
|
|
206
205
|
maxAge: 10 * 60 * 1000,
|
|
207
206
|
httpOnly: true,
|
|
208
|
-
secure: this.config.get(
|
|
207
|
+
secure: this.config.get("allowHttps"),
|
|
209
208
|
},
|
|
210
209
|
);
|
|
211
210
|
const redirectUri = this.generateAuthUrl(strategy, {
|
|
212
211
|
redirect_uri: this.redirectUri(ctx, strategy),
|
|
213
212
|
scope,
|
|
214
213
|
state,
|
|
215
|
-
access_type: refreshToken ?
|
|
214
|
+
access_type: refreshToken ? "offline" : "online",
|
|
216
215
|
...params,
|
|
217
216
|
});
|
|
218
217
|
|
|
219
|
-
|
|
218
|
+
ctx.redirect(redirectUri);
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
async accessResponse<StrategyKey extends StrategyKeys>(
|
|
@@ -225,30 +224,29 @@ export class AuthenticationService<
|
|
|
225
224
|
isLoggedIn: boolean,
|
|
226
225
|
hooks: AccessResponseHooks<StrategyKeys, U>,
|
|
227
226
|
): Promise<U> {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
error.expose = true;
|
|
232
|
-
throw error;
|
|
227
|
+
const errorParam = ctx.params.queryParam("error").notEmpty();
|
|
228
|
+
if (errorParam.isValid()) {
|
|
229
|
+
ctx.throw(403, errorParam.value);
|
|
233
230
|
}
|
|
234
231
|
|
|
235
|
-
const code = ctx.
|
|
236
|
-
const state = ctx.
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
ctx.cookies.
|
|
232
|
+
const code = ctx.validParams.queryParam("code").notEmpty().value;
|
|
233
|
+
const state = ctx.validParams.queryParam("state").notEmpty().value;
|
|
234
|
+
|
|
235
|
+
const cookieName = `auth_${strategy}_${state}`;
|
|
236
|
+
const cookie = ctx.cookies.get(cookieName);
|
|
237
|
+
ctx.cookies.set(cookieName, "", { expires: new Date(1) });
|
|
240
238
|
if (!cookie) {
|
|
241
|
-
throw new Error(
|
|
239
|
+
throw new Error("No cookie for this state");
|
|
242
240
|
}
|
|
243
241
|
|
|
244
|
-
|
|
245
|
-
if (!
|
|
246
|
-
throw new Error(
|
|
242
|
+
const parsedCookie = JSON.parse(cookie);
|
|
243
|
+
if (!parsedCookie?.scope) {
|
|
244
|
+
throw new Error("Unexpected cookie value");
|
|
247
245
|
}
|
|
248
246
|
|
|
249
|
-
if (!
|
|
247
|
+
if (!parsedCookie.isLoginAccess) {
|
|
250
248
|
if (!isLoggedIn) {
|
|
251
|
-
throw new Error(
|
|
249
|
+
throw new Error("You are not connected");
|
|
252
250
|
}
|
|
253
251
|
}
|
|
254
252
|
|
|
@@ -257,12 +255,12 @@ export class AuthenticationService<
|
|
|
257
255
|
redirectUri: this.redirectUri(ctx, strategy),
|
|
258
256
|
});
|
|
259
257
|
|
|
260
|
-
if (
|
|
258
|
+
if (parsedCookie.isLoginAccess) {
|
|
261
259
|
const user = await this.userAccountsService.findOrCreateFromStrategy(
|
|
262
260
|
strategy,
|
|
263
261
|
tokens,
|
|
264
|
-
|
|
265
|
-
|
|
262
|
+
parsedCookie.scope,
|
|
263
|
+
parsedCookie.scopeKey,
|
|
266
264
|
);
|
|
267
265
|
|
|
268
266
|
if (hooks.afterLoginSuccess) {
|
|
@@ -277,12 +275,17 @@ export class AuthenticationService<
|
|
|
277
275
|
loggedInUser,
|
|
278
276
|
strategy,
|
|
279
277
|
tokens,
|
|
280
|
-
|
|
281
|
-
|
|
278
|
+
parsedCookie.scope,
|
|
279
|
+
parsedCookie.scopeKey,
|
|
282
280
|
);
|
|
283
281
|
|
|
284
282
|
if (hooks.afterScopeUpdate) {
|
|
285
|
-
await hooks.afterScopeUpdate(
|
|
283
|
+
await hooks.afterScopeUpdate(
|
|
284
|
+
strategy,
|
|
285
|
+
parsedCookie.scopeKey,
|
|
286
|
+
account,
|
|
287
|
+
user,
|
|
288
|
+
);
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
return loggedInUser;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export type GoogleParams =
|
|
2
|
+
| "access_type"
|
|
3
|
+
| "include_granted_scopes"
|
|
4
|
+
| "login_hint"
|
|
5
|
+
| "prompt";
|
|
6
|
+
export type SlackParams = "client_id" | "team";
|
|
2
7
|
|
|
3
|
-
export type AllowedStrategyKeys =
|
|
8
|
+
export type AllowedStrategyKeys = "google" | "slack";
|
|
4
9
|
|
|
5
10
|
export interface AllowedMapParamsStrategy {
|
|
6
11
|
google: GoogleParams;
|
|
@@ -1,24 +1,24 @@
|
|
|
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> {
|
|
21
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
22
22
|
return fetch(
|
|
23
23
|
`https://www.googleapis.com/oauth2/v1/userinfo?access_token=${tokens.accessToken}`,
|
|
24
24
|
).then((response) => response.json());
|
|
@@ -59,8 +59,8 @@ export default class UserAccountGoogleService<ScopeKeys extends 'login'>
|
|
|
59
59
|
|
|
60
60
|
getScope(oldScope: string[] | undefined, newScope: string): string[] {
|
|
61
61
|
return !oldScope
|
|
62
|
-
? newScope.split(
|
|
63
|
-
: [...oldScope, ...newScope.split(
|
|
62
|
+
? newScope.split(" ")
|
|
63
|
+
: [...oldScope, ...newScope.split(" ")].filter(
|
|
64
64
|
(item, i, ar) => ar.indexOf(item) === i,
|
|
65
65
|
);
|
|
66
66
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
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> {
|
|
22
|
+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
23
23
|
return fetch(
|
|
24
24
|
`https://slack.com/api/users.identity?token=${tokens.accessToken}`,
|
|
25
25
|
).then((response) => response.json());
|
|
@@ -56,8 +56,8 @@ export default class UserAccountSlackService<ScopeKeys extends 'login'>
|
|
|
56
56
|
|
|
57
57
|
getScope(oldScope: string[] | undefined, newScope: string): string[] {
|
|
58
58
|
return !oldScope
|
|
59
|
-
? newScope.split(
|
|
60
|
-
: [...oldScope, ...newScope.split(
|
|
59
|
+
? newScope.split(" ")
|
|
60
|
+
: [...oldScope, ...newScope.split(" ")].filter(
|
|
61
61
|
(item, i, ar) => ar.indexOf(item) === i,
|
|
62
62
|
);
|
|
63
63
|
}
|