alp-node-auth 7.2.1 → 8.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 +25 -0
- package/README.md +1 -1
- package/dist/definitions/MongoUsersManager.d.ts +2 -0
- package/dist/definitions/MongoUsersManager.d.ts.map +1 -1
- package/dist/definitions/authSocketIO.d.ts +1 -2
- package/dist/definitions/authSocketIO.d.ts.map +1 -1
- package/dist/definitions/index.d.ts +7 -7
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/services/authentification/AuthenticationService.d.ts +2 -2
- package/dist/definitions/services/authentification/AuthenticationService.d.ts.map +1 -1
- package/dist/definitions/utils/cookies.d.ts +2 -1
- package/dist/definitions/utils/cookies.d.ts.map +1 -1
- package/dist/definitions/utils/createFindLoggedInUser.d.ts +6 -0
- package/dist/definitions/utils/createFindLoggedInUser.d.ts.map +1 -0
- package/dist/index-node16.mjs +90 -60
- package/dist/index-node16.mjs.map +1 -1
- package/package.json +7 -7
- package/src/MongoUsersManager.ts +5 -0
- package/src/authApolloContext.ts +8 -8
- package/src/authSocketIO.ts +8 -9
- package/src/createAuthController.ts +4 -4
- package/src/index.ts +68 -42
- package/src/services/authentification/AuthenticationService.ts +7 -7
- package/src/utils/cookies.ts +7 -2
- package/src/utils/{createFindConnectedAndUser.ts → createFindLoggedInUser.ts} +14 -15
- package/dist/definitions/utils/createFindConnectedAndUser.d.ts +0 -6
- package/dist/definitions/utils/createFindConnectedAndUser.d.ts.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
1
2
|
import type { IncomingMessage } from 'http';
|
|
2
3
|
import { promisify } from 'util';
|
|
3
4
|
import type { Context } from 'alp-node';
|
|
@@ -18,8 +19,12 @@ import { AuthenticationService } from './services/authentification/Authenticatio
|
|
|
18
19
|
import type { AllowedStrategyKeys } from './services/authentification/types';
|
|
19
20
|
import UserAccountsService from './services/user/UserAccountsService';
|
|
20
21
|
import type { AccountService } from './services/user/types';
|
|
21
|
-
import {
|
|
22
|
-
|
|
22
|
+
import {
|
|
23
|
+
getTokenFromRequest,
|
|
24
|
+
COOKIE_NAME_TOKEN,
|
|
25
|
+
COOKIE_NAME_STATE,
|
|
26
|
+
} from './utils/cookies';
|
|
27
|
+
import { createFindLoggedInUser } from './utils/createFindLoggedInUser';
|
|
23
28
|
|
|
24
29
|
export { default as MongoUsersManager } from './MongoUsersManager';
|
|
25
30
|
export { default as UserAccountGoogleService } from './services/user/UserAccountGoogleService';
|
|
@@ -31,22 +36,25 @@ export { STATUSES } from './services/user/UserAccountsService';
|
|
|
31
36
|
declare module 'alp-types' {
|
|
32
37
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
33
38
|
interface ContextState {
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
loggedInUserId:
|
|
40
|
+
| NonNullable<ContextState['loggedInUser']>['_id']
|
|
41
|
+
| null
|
|
42
|
+
| undefined;
|
|
43
|
+
loggedInUser: User | null | undefined;
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
interface ContextSanitizedState {
|
|
39
|
-
|
|
40
|
-
| NonNullable<ContextSanitizedState['
|
|
47
|
+
loggedInUserId:
|
|
48
|
+
| NonNullable<ContextSanitizedState['loggedInUser']>['_id']
|
|
41
49
|
| null
|
|
42
50
|
| undefined;
|
|
43
|
-
|
|
51
|
+
loggedInUser: UserSanitized | null | undefined;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
interface BaseContext {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
setLoggedIn: (
|
|
56
|
+
loggedInUserId: NonNullable<ContextState['loggedInUserId']>,
|
|
57
|
+
loggedInUser: NonNullable<ContextState['loggedInUser']>,
|
|
50
58
|
) => Promise<void>;
|
|
51
59
|
logout: () => void;
|
|
52
60
|
}
|
|
@@ -102,21 +110,21 @@ export default function init<
|
|
|
102
110
|
authHooks,
|
|
103
111
|
});
|
|
104
112
|
|
|
105
|
-
app.context.
|
|
113
|
+
app.context.setLoggedIn = async function (
|
|
106
114
|
this: Context,
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
loggedInUserId: NonNullable<ContextState['loggedInUser']>['_id'],
|
|
116
|
+
loggedInUser: NonNullable<ContextState['loggedInUser']>,
|
|
109
117
|
): Promise<void> {
|
|
110
|
-
logger.debug('
|
|
111
|
-
if (!
|
|
112
|
-
throw new Error('Illegal value for
|
|
118
|
+
logger.debug('setLoggedIn', { loggedInUser });
|
|
119
|
+
if (!loggedInUserId) {
|
|
120
|
+
throw new Error('Illegal value for setLoggedIn');
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
this.state.
|
|
116
|
-
this.state.
|
|
123
|
+
this.state.loggedInUserId = loggedInUserId;
|
|
124
|
+
this.state.loggedInUser = loggedInUser;
|
|
117
125
|
|
|
118
126
|
const token = await signPromisified(
|
|
119
|
-
{
|
|
127
|
+
{ loggedInUserId, time: Date.now() },
|
|
120
128
|
this.config
|
|
121
129
|
.get<Map<string, unknown>>('authentication')
|
|
122
130
|
.get('secretKey'),
|
|
@@ -127,20 +135,36 @@ export default function init<
|
|
|
127
135
|
},
|
|
128
136
|
);
|
|
129
137
|
|
|
138
|
+
const calcExpiresTime = (): number => {
|
|
139
|
+
const date = new Date();
|
|
140
|
+
date.setDate(date.getDate() + 30);
|
|
141
|
+
return date.getTime();
|
|
142
|
+
};
|
|
143
|
+
|
|
130
144
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
131
|
-
this.cookies.set(
|
|
145
|
+
this.cookies.set(COOKIE_NAME_TOKEN, token, {
|
|
132
146
|
httpOnly: true,
|
|
133
147
|
secure: this.config.get('allowHttps'),
|
|
134
148
|
});
|
|
149
|
+
|
|
150
|
+
this.cookies.set(
|
|
151
|
+
COOKIE_NAME_STATE,
|
|
152
|
+
JSON.stringify({ loggedInUserId, expiresIn: calcExpiresTime() }),
|
|
153
|
+
{
|
|
154
|
+
httpOnly: false,
|
|
155
|
+
secure: this.config.get('allowHttps'),
|
|
156
|
+
},
|
|
157
|
+
);
|
|
135
158
|
};
|
|
136
159
|
|
|
137
160
|
app.context.logout = function (this: Context): void {
|
|
138
|
-
delete this.state.
|
|
139
|
-
delete this.state.
|
|
140
|
-
this.cookies.set(
|
|
161
|
+
delete this.state.loggedInUserId;
|
|
162
|
+
delete this.state.loggedInUser;
|
|
163
|
+
this.cookies.set(COOKIE_NAME_TOKEN, '', { expires: new Date(1) });
|
|
164
|
+
this.cookies.set(COOKIE_NAME_STATE, '', { expires: new Date(1) });
|
|
141
165
|
};
|
|
142
166
|
|
|
143
|
-
const
|
|
167
|
+
const findLoggedInUser = createFindLoggedInUser(
|
|
144
168
|
app.config
|
|
145
169
|
.get<Map<string, unknown>>('authentication')
|
|
146
170
|
.get('secretKey') as string,
|
|
@@ -150,49 +174,51 @@ export default function init<
|
|
|
150
174
|
|
|
151
175
|
return {
|
|
152
176
|
routes: createRoutes(controller),
|
|
153
|
-
|
|
154
|
-
getConnectedAndUserFromRequest: (
|
|
177
|
+
findLoggedInUserFromRequest: (
|
|
155
178
|
req: IncomingMessage,
|
|
156
|
-
): ReturnType<typeof
|
|
179
|
+
): ReturnType<typeof findLoggedInUser> => {
|
|
157
180
|
const token = getTokenFromRequest(req);
|
|
158
|
-
return
|
|
181
|
+
return findLoggedInUser(
|
|
159
182
|
jwtAudience || req.headers['user-agent'],
|
|
160
183
|
token,
|
|
161
184
|
);
|
|
162
185
|
},
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
findLoggedInUser,
|
|
165
187
|
middleware: async <T>(
|
|
166
188
|
ctx: Context,
|
|
167
189
|
next: () => T | Promise<T>,
|
|
168
190
|
): Promise<T> => {
|
|
169
|
-
const token = ctx.cookies.get(
|
|
191
|
+
const token = ctx.cookies.get(COOKIE_NAME_TOKEN);
|
|
170
192
|
const userAgent = ctx.request.headers['user-agent'];
|
|
171
193
|
logger.debug('middleware', { token });
|
|
172
194
|
|
|
173
195
|
const setState = (
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
loggedInUserId: U['_id'] | null | undefined,
|
|
197
|
+
loggedInUser: U | null | undefined,
|
|
176
198
|
): void => {
|
|
177
|
-
ctx.state.
|
|
178
|
-
ctx.state.user =
|
|
179
|
-
ctx.sanitizedState.
|
|
180
|
-
ctx.sanitizedState.
|
|
199
|
+
ctx.state.loggedInUserId = loggedInUserId;
|
|
200
|
+
ctx.state.user = loggedInUser;
|
|
201
|
+
ctx.sanitizedState.loggedInUserId = loggedInUserId;
|
|
202
|
+
ctx.sanitizedState.loggedInUser =
|
|
203
|
+
loggedInUser && usersManager.sanitize(loggedInUser);
|
|
181
204
|
};
|
|
182
205
|
|
|
183
|
-
const [
|
|
206
|
+
const [loggedInUserId, loggedInUser] = await findLoggedInUser(
|
|
184
207
|
jwtAudience || userAgent,
|
|
185
208
|
token,
|
|
186
209
|
);
|
|
187
|
-
logger.debug('middleware', {
|
|
210
|
+
logger.debug('middleware', { loggedInUserId });
|
|
188
211
|
|
|
189
|
-
if (
|
|
190
|
-
if (token)
|
|
212
|
+
if (loggedInUserId == null || loggedInUser == null) {
|
|
213
|
+
if (token) {
|
|
214
|
+
ctx.cookies.set(COOKIE_NAME_TOKEN, '', { expires: new Date(1) });
|
|
215
|
+
ctx.cookies.set(COOKIE_NAME_STATE, '', { expires: new Date(1) });
|
|
216
|
+
}
|
|
191
217
|
setState(null, null);
|
|
192
218
|
return next();
|
|
193
219
|
}
|
|
194
220
|
|
|
195
|
-
setState(
|
|
221
|
+
setState(loggedInUserId, loggedInUser);
|
|
196
222
|
return next();
|
|
197
223
|
},
|
|
198
224
|
};
|
|
@@ -46,7 +46,7 @@ export type Strategies<StrategyKeys extends AllowedStrategyKeys> = Record<
|
|
|
46
46
|
export interface AccessResponseHooks<StrategyKeys, U extends User = User> {
|
|
47
47
|
afterLoginSuccess?: <StrategyKey extends StrategyKeys>(
|
|
48
48
|
strategy: StrategyKey,
|
|
49
|
-
|
|
49
|
+
loggedInUser: U,
|
|
50
50
|
) => void | Promise<void>;
|
|
51
51
|
|
|
52
52
|
afterScopeUpdate?: <StrategyKey extends StrategyKeys>(
|
|
@@ -223,9 +223,9 @@ export class AuthenticationService<
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async accessResponse<StrategyKey extends StrategyKeys>(
|
|
226
|
-
ctx:
|
|
226
|
+
ctx: Context,
|
|
227
227
|
strategy: StrategyKey,
|
|
228
|
-
|
|
228
|
+
isLoggedIn: boolean,
|
|
229
229
|
hooks: AccessResponseHooks<StrategyKeys, U>,
|
|
230
230
|
): Promise<U> {
|
|
231
231
|
if (ctx.query.error) {
|
|
@@ -250,7 +250,7 @@ export class AuthenticationService<
|
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
if (!cookie.isLoginAccess) {
|
|
253
|
-
if (!
|
|
253
|
+
if (!isLoggedIn) {
|
|
254
254
|
throw new Error('You are not connected');
|
|
255
255
|
}
|
|
256
256
|
}
|
|
@@ -275,9 +275,9 @@ export class AuthenticationService<
|
|
|
275
275
|
return user;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
const
|
|
278
|
+
const loggedInUser = ctx.state.loggedInUser as U;
|
|
279
279
|
const { account, user } = await this.userAccountsService.update(
|
|
280
|
-
|
|
280
|
+
loggedInUser,
|
|
281
281
|
strategy,
|
|
282
282
|
tokens,
|
|
283
283
|
cookie.scope,
|
|
@@ -288,7 +288,7 @@ export class AuthenticationService<
|
|
|
288
288
|
await hooks.afterScopeUpdate(strategy, cookie.scopeKey, account, user);
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
return
|
|
291
|
+
return loggedInUser;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
refreshAccountTokens(user: U, account: Account): Promise<boolean> {
|
package/src/utils/cookies.ts
CHANGED
|
@@ -2,17 +2,22 @@ import type { IncomingMessage } from 'http';
|
|
|
2
2
|
import type { Option } from 'cookies';
|
|
3
3
|
import Cookies from 'cookies';
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const COOKIE_NAME_TOKEN = 'loggedInUserToken';
|
|
6
|
+
export const COOKIE_NAME_STATE = 'loggedInUserState';
|
|
6
7
|
|
|
7
8
|
export const getTokenFromRequest = (
|
|
8
9
|
req: IncomingMessage,
|
|
9
10
|
options?: Pick<Option, Exclude<keyof Option, 'secure'>>,
|
|
10
11
|
): string | undefined => {
|
|
12
|
+
if (req.headers.authorization?.startsWith('Bearer ')) {
|
|
13
|
+
return req.headers.authorization.slice('Bearer '.length);
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
12
17
|
const cookies = new Cookies(req, null as unknown as any, {
|
|
13
18
|
...options,
|
|
14
19
|
secure: true,
|
|
15
20
|
});
|
|
16
21
|
|
|
17
|
-
return cookies.get(
|
|
22
|
+
return cookies.get(COOKIE_NAME_TOKEN);
|
|
18
23
|
};
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
} from 'jsonwebtoken';
|
|
8
8
|
import jsonwebtoken from 'jsonwebtoken';
|
|
9
9
|
import type { Logger } from 'nightingale-logger';
|
|
10
|
-
import type { User, UserSanitized } from '../../types
|
|
10
|
+
import type { User, UserSanitized } from '../../types';
|
|
11
11
|
import type MongoUsersManager from '../MongoUsersManager';
|
|
12
12
|
|
|
13
13
|
type Verify = (
|
|
@@ -31,43 +31,42 @@ const createDecodeJWT =
|
|
|
31
31
|
algorithms: ['HS512'],
|
|
32
32
|
audience: jwtAudience,
|
|
33
33
|
});
|
|
34
|
-
return (result as any)?.
|
|
34
|
+
return (result as any)?.loggedInUserId as string | undefined;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export type
|
|
37
|
+
export type FindLoggedInUser<U extends User> = (
|
|
38
38
|
jwtAudience?: string,
|
|
39
39
|
token?: string,
|
|
40
40
|
) => Promise<[null | undefined | U['_id'], null | undefined | U]>;
|
|
41
41
|
|
|
42
|
-
export const
|
|
42
|
+
export const createFindLoggedInUser = <
|
|
43
43
|
U extends User,
|
|
44
44
|
USanitized extends UserSanitized,
|
|
45
45
|
>(
|
|
46
46
|
secretKey: string,
|
|
47
47
|
usersManager: MongoUsersManager<U, USanitized>,
|
|
48
48
|
logger: Logger,
|
|
49
|
-
):
|
|
49
|
+
): FindLoggedInUser<U> => {
|
|
50
50
|
const decodeJwt = createDecodeJWT(secretKey);
|
|
51
51
|
|
|
52
|
-
const
|
|
53
|
-
jwtAudience,
|
|
54
|
-
token,
|
|
55
|
-
) => {
|
|
52
|
+
const findLoggedInUser: FindLoggedInUser<U> = async (jwtAudience, token) => {
|
|
56
53
|
if (!token || !jwtAudience) return [null, null];
|
|
57
54
|
|
|
58
|
-
let
|
|
55
|
+
let loggedInUserId;
|
|
59
56
|
try {
|
|
60
|
-
|
|
57
|
+
loggedInUserId = await decodeJwt(token, jwtAudience);
|
|
61
58
|
} catch (err: unknown) {
|
|
62
59
|
logger.debug('failed to verify authentification', { err });
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
if (
|
|
62
|
+
if (loggedInUserId == null) return [null, null];
|
|
66
63
|
|
|
67
|
-
const
|
|
64
|
+
const loggedInUser = await usersManager.findById(loggedInUserId);
|
|
68
65
|
|
|
69
|
-
return [
|
|
66
|
+
if (!loggedInUser) return [null, null];
|
|
67
|
+
|
|
68
|
+
return [loggedInUserId, loggedInUser];
|
|
70
69
|
};
|
|
71
70
|
|
|
72
|
-
return
|
|
71
|
+
return findLoggedInUser;
|
|
73
72
|
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Logger } from 'nightingale-logger';
|
|
2
|
-
import type { User, UserSanitized } from '../../types.d';
|
|
3
|
-
import type MongoUsersManager from '../MongoUsersManager';
|
|
4
|
-
export type FindConnectedAndUser<U extends User> = (jwtAudience?: string, token?: string) => Promise<[null | undefined | U['_id'], null | undefined | U]>;
|
|
5
|
-
export declare const createFindConnectedAndUser: <U extends User, USanitized extends UserSanitized>(secretKey: string, usersManager: MongoUsersManager<U, USanitized>, logger: Logger) => FindConnectedAndUser<U>;
|
|
6
|
-
//# sourceMappingURL=createFindConnectedAndUser.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createFindConnectedAndUser.d.ts","sourceRoot":"","sources":["../../../src/utils/createFindConnectedAndUser.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AA0B1D,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,IAAI,IAAI,CACjD,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,KACX,OAAO,CAAC,CAAC,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;AAElE,eAAO,MAAM,0BAA0B,gEAI1B,MAAM,0DAET,MAAM,4BAyBf,CAAC"}
|