alp-node-auth 5.3.1 → 6.0.3
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/.eslintrc.json +1 -1
- package/CHANGELOG.md +51 -0
- package/README.md +0 -1
- package/dist/MongoUsersManager.d.ts.map +1 -1
- package/dist/authSocketIO.d.ts.map +1 -1
- package/dist/{index-node12-dev.mjs → index-node14.mjs} +33 -21
- package/dist/index-node14.mjs.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/services/authentification/AuthenticationService.d.ts.map +1 -1
- package/dist/services/user/UserAccountGoogleService.d.ts.map +1 -1
- package/dist/services/user/UserAccountSlackService.d.ts.map +1 -1
- package/dist/services/user/UserAccountsService.d.ts.map +1 -1
- package/dist/utils/cookies.d.ts.map +1 -1
- package/dist/utils/createFindConnectedAndUser.d.ts +1 -1
- package/dist/utils/createFindConnectedAndUser.d.ts.map +1 -1
- package/package.json +26 -50
- package/rollup.config.mjs +3 -0
- package/src/.eslintrc.json +19 -2
- package/src/MongoUsersManager.ts +2 -1
- package/src/authApolloContext.ts +4 -4
- package/src/authSocketIO.ts +5 -4
- package/src/createAuthController.ts +2 -2
- package/src/index.ts +6 -3
- package/src/services/authentification/AuthenticationService.ts +5 -3
- package/src/services/user/UserAccountGoogleService.ts +8 -5
- package/src/services/user/UserAccountSlackService.ts +7 -5
- package/src/services/user/UserAccountsService.ts +10 -9
- package/src/utils/cookies.ts +2 -1
- package/src/utils/createFindConnectedAndUser.ts +11 -12
- package/strategies/dropbox.js +4 -6
- package/strategies/facebook.js +4 -6
- package/strategies/foursquare.js +4 -6
- package/strategies/github.js +4 -6
- package/strategies/google.js +4 -6
- package/strategies/slack.js +4 -6
- package/dist/index-node12-dev.cjs.js +0 -848
- package/dist/index-node12-dev.cjs.js.map +0 -1
- package/dist/index-node12-dev.mjs.map +0 -1
- package/dist/index-node12.cjs.js +0 -848
- package/dist/index-node12.cjs.js.map +0 -1
- package/dist/index-node12.mjs +0 -832
- package/dist/index-node12.mjs.map +0 -1
- package/index.js +0 -6
- package/strategies/dropbox.mjs +0 -18
- package/strategies/facebook.mjs +0 -18
- package/strategies/foursquare.mjs +0 -18
- package/strategies/github.mjs +0 -18
- package/strategies/google.mjs +0 -18
- package/strategies/slack.mjs +0 -18
package/dist/index-node12.mjs
DELETED
|
@@ -1,832 +0,0 @@
|
|
|
1
|
-
import { promisify } from 'util';
|
|
2
|
-
import jsonwebtoken from 'jsonwebtoken';
|
|
3
|
-
import Logger from 'nightingale-logger';
|
|
4
|
-
import 'alp-router';
|
|
5
|
-
import { EventEmitter } from 'events';
|
|
6
|
-
import { randomBytes } from 'crypto';
|
|
7
|
-
import Cookies from 'cookies';
|
|
8
|
-
|
|
9
|
-
function createAuthController({
|
|
10
|
-
usersManager,
|
|
11
|
-
authenticationService,
|
|
12
|
-
homeRouterKey = '/',
|
|
13
|
-
defaultStrategy,
|
|
14
|
-
authHooks = {}
|
|
15
|
-
}) {
|
|
16
|
-
return {
|
|
17
|
-
async login(ctx) {
|
|
18
|
-
const strategy = ctx.namedParam('strategy') || defaultStrategy;
|
|
19
|
-
if (!strategy) throw new Error('Strategy missing');
|
|
20
|
-
const params = authHooks.paramsForLogin && (await authHooks.paramsForLogin(strategy, ctx)) || {};
|
|
21
|
-
await authenticationService.redirectAuthUrl(ctx, strategy, {}, params);
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
async addScope(ctx) {
|
|
25
|
-
if (ctx.state.connected) {
|
|
26
|
-
await ctx.redirectTo(homeRouterKey);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const strategy = ctx.namedParam('strategy') || defaultStrategy;
|
|
31
|
-
if (!strategy) throw new Error('Strategy missing');
|
|
32
|
-
const scopeKey = ctx.namedParam('scopeKey');
|
|
33
|
-
if (!scopeKey) throw new Error('Scope missing');
|
|
34
|
-
await authenticationService.redirectAuthUrl(ctx, strategy, {
|
|
35
|
-
scopeKey
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
async loginResponse(ctx) {
|
|
40
|
-
if (ctx.state.connected) {
|
|
41
|
-
await ctx.redirectTo(homeRouterKey);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const strategy = ctx.namedParam('strategy');
|
|
46
|
-
ctx.assert(strategy);
|
|
47
|
-
const connectedUser = await authenticationService.accessResponse(ctx, strategy, ctx.state.connected, {
|
|
48
|
-
afterLoginSuccess: authHooks.afterLoginSuccess,
|
|
49
|
-
afterScopeUpdate: authHooks.afterScopeUpdate
|
|
50
|
-
});
|
|
51
|
-
const keyPath = usersManager.store.keyPath;
|
|
52
|
-
await ctx.setConnected(connectedUser[keyPath], connectedUser);
|
|
53
|
-
await ctx.redirectTo(homeRouterKey);
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
async logout(ctx) {
|
|
57
|
-
ctx.logout();
|
|
58
|
-
await ctx.redirectTo(homeRouterKey);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const createRoutes = controller => ({
|
|
65
|
-
login: ['/login/:strategy?', segment => {
|
|
66
|
-
segment.add('/response', controller.loginResponse, 'loginResponse');
|
|
67
|
-
segment.defaultRoute(controller.login, 'login');
|
|
68
|
-
}],
|
|
69
|
-
addScope: ['/auth/add-scope/:strategy/:scopeKey', controller.addScope],
|
|
70
|
-
logout: ['/logout', controller.logout]
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const randomBytesPromisified = promisify(randomBytes);
|
|
74
|
-
async function randomHex(size) {
|
|
75
|
-
const buffer = await randomBytesPromisified(size);
|
|
76
|
-
return buffer.toString('hex');
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
80
|
-
const logger$4 = new Logger('alp:auth:authentication');
|
|
81
|
-
class AuthenticationService extends EventEmitter {
|
|
82
|
-
constructor(config, strategies, userAccountsService) {
|
|
83
|
-
super();
|
|
84
|
-
this.config = config;
|
|
85
|
-
this.strategies = strategies;
|
|
86
|
-
this.userAccountsService = userAccountsService;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
generateAuthUrl(strategy, params) {
|
|
90
|
-
logger$4.debug('generateAuthUrl', {
|
|
91
|
-
strategy,
|
|
92
|
-
params
|
|
93
|
-
});
|
|
94
|
-
const strategyInstance = this.strategies[strategy];
|
|
95
|
-
|
|
96
|
-
switch (strategyInstance.type) {
|
|
97
|
-
case 'oauth2':
|
|
98
|
-
return strategyInstance.oauth2.authorizationCode.authorizeURL(params);
|
|
99
|
-
|
|
100
|
-
default:
|
|
101
|
-
throw new Error('Invalid strategy');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async getTokens(strategy, options) {
|
|
106
|
-
logger$4.debug('getTokens', {
|
|
107
|
-
strategy,
|
|
108
|
-
options
|
|
109
|
-
});
|
|
110
|
-
const strategyInstance = this.strategies[strategy];
|
|
111
|
-
|
|
112
|
-
switch (strategyInstance.type) {
|
|
113
|
-
case 'oauth2':
|
|
114
|
-
{
|
|
115
|
-
const result = await strategyInstance.oauth2.authorizationCode.getToken({
|
|
116
|
-
code: options.code,
|
|
117
|
-
redirect_uri: options.redirectUri
|
|
118
|
-
});
|
|
119
|
-
if (!result) return result;
|
|
120
|
-
return {
|
|
121
|
-
accessToken: result.access_token,
|
|
122
|
-
refreshToken: result.refresh_token,
|
|
123
|
-
tokenType: result.token_type,
|
|
124
|
-
expiresIn: result.expires_in,
|
|
125
|
-
expireDate: (() => {
|
|
126
|
-
const d = new Date();
|
|
127
|
-
d.setTime(d.getTime() + result.expires_in * 1000);
|
|
128
|
-
return d;
|
|
129
|
-
})(),
|
|
130
|
-
idToken: result.id_token
|
|
131
|
-
}; // return strategyInstance.accessToken.create(result);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
default:
|
|
135
|
-
throw new Error('Invalid stategy');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async refreshToken(strategy, tokensParam) {
|
|
140
|
-
logger$4.debug('refreshToken', {
|
|
141
|
-
strategy
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
if (!tokensParam.refreshToken) {
|
|
145
|
-
throw new Error('Missing refresh token');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const strategyInstance = this.strategies[strategy];
|
|
149
|
-
|
|
150
|
-
switch (strategyInstance.type) {
|
|
151
|
-
case 'oauth2':
|
|
152
|
-
{
|
|
153
|
-
const token = strategyInstance.oauth2.accessToken.create({
|
|
154
|
-
refresh_token: tokensParam.refreshToken
|
|
155
|
-
});
|
|
156
|
-
const result = await token.refresh();
|
|
157
|
-
const tokens = result.token;
|
|
158
|
-
return {
|
|
159
|
-
accessToken: tokens.access_token,
|
|
160
|
-
tokenType: tokens.token_type,
|
|
161
|
-
expiresIn: tokens.expires_in,
|
|
162
|
-
expireDate: (() => {
|
|
163
|
-
const d = new Date();
|
|
164
|
-
d.setTime(d.getTime() + tokens.expires_in * 1000);
|
|
165
|
-
return d;
|
|
166
|
-
})(),
|
|
167
|
-
idToken: tokens.id_token
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
default:
|
|
172
|
-
throw new Error('Invalid stategy');
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
redirectUri(ctx, strategy) {
|
|
177
|
-
const host = `http${this.config.get('allowHttps') ? 's' : ''}://${ctx.request.host}`;
|
|
178
|
-
return `${host}${ctx.urlGenerator('loginResponse', {
|
|
179
|
-
strategy
|
|
180
|
-
})}`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async redirectAuthUrl(ctx, strategy, {
|
|
184
|
-
refreshToken,
|
|
185
|
-
scopeKey,
|
|
186
|
-
user,
|
|
187
|
-
accountId
|
|
188
|
-
}, params) {
|
|
189
|
-
logger$4.debug('redirectAuthUrl', {
|
|
190
|
-
strategy,
|
|
191
|
-
scopeKey,
|
|
192
|
-
refreshToken
|
|
193
|
-
});
|
|
194
|
-
const state = await randomHex(8);
|
|
195
|
-
const scope = this.userAccountsService.getScope(strategy, scopeKey || 'login', user, accountId);
|
|
196
|
-
|
|
197
|
-
if (!scope) {
|
|
198
|
-
throw new Error('Invalid empty scope');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
ctx.cookies.set(`auth_${strategy}_${state}`, JSON.stringify({
|
|
202
|
-
scopeKey,
|
|
203
|
-
scope,
|
|
204
|
-
isLoginAccess: !scopeKey || scopeKey === 'login'
|
|
205
|
-
}), {
|
|
206
|
-
maxAge: 600000,
|
|
207
|
-
httpOnly: true,
|
|
208
|
-
secure: this.config.get('allowHttps')
|
|
209
|
-
});
|
|
210
|
-
const redirectUri = this.generateAuthUrl(strategy, {
|
|
211
|
-
redirect_uri: this.redirectUri(ctx, strategy),
|
|
212
|
-
scope,
|
|
213
|
-
state,
|
|
214
|
-
access_type: refreshToken ? 'offline' : 'online',
|
|
215
|
-
...params
|
|
216
|
-
});
|
|
217
|
-
return ctx.redirect(redirectUri);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
async accessResponse(ctx, strategy, isConnected, hooks) {
|
|
221
|
-
if (ctx.query.error) {
|
|
222
|
-
const error = new Error(ctx.query.error);
|
|
223
|
-
error.status = 403;
|
|
224
|
-
error.expose = true;
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const code = ctx.query.code;
|
|
229
|
-
const state = ctx.query.state;
|
|
230
|
-
const cookieName = `auth_${strategy}_${state}`;
|
|
231
|
-
let cookie = ctx.cookies.get(cookieName);
|
|
232
|
-
ctx.cookies.set(cookieName, '', {
|
|
233
|
-
expires: new Date(1)
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
if (!cookie) {
|
|
237
|
-
throw new Error('No cookie for this state');
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
cookie = JSON.parse(cookie);
|
|
241
|
-
|
|
242
|
-
if (!cookie || !cookie.scope) {
|
|
243
|
-
throw new Error('Unexpected cookie value');
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (!cookie.isLoginAccess) {
|
|
247
|
-
if (!isConnected) {
|
|
248
|
-
throw new Error('You are not connected');
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const tokens = await this.getTokens(strategy, {
|
|
253
|
-
code,
|
|
254
|
-
redirectUri: this.redirectUri(ctx, strategy)
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
if (cookie.isLoginAccess) {
|
|
258
|
-
const user = await this.userAccountsService.findOrCreateFromStrategy(strategy, tokens, cookie.scope, cookie.scopeKey);
|
|
259
|
-
|
|
260
|
-
if (hooks.afterLoginSuccess) {
|
|
261
|
-
await hooks.afterLoginSuccess(strategy, user);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return user;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const connectedUser = ctx.state.user;
|
|
268
|
-
const {
|
|
269
|
-
account,
|
|
270
|
-
user
|
|
271
|
-
} = await this.userAccountsService.update(connectedUser, strategy, tokens, cookie.scope, cookie.scopeKey);
|
|
272
|
-
|
|
273
|
-
if (hooks.afterScopeUpdate) {
|
|
274
|
-
await hooks.afterScopeUpdate(strategy, cookie.scopeKey, account, user);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return connectedUser;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
refreshAccountTokens(user, account) {
|
|
281
|
-
if (account.tokenExpireDate && account.tokenExpireDate.getTime() > Date.now()) {
|
|
282
|
-
return Promise.resolve(false);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return this.refreshToken(account.provider, {
|
|
286
|
-
// accessToken: account.accessToken,
|
|
287
|
-
refreshToken: account.refreshToken
|
|
288
|
-
}).then(tokens => {
|
|
289
|
-
if (!tokens) {
|
|
290
|
-
// serviceGoogle.updateFields({ accessToken:null, refreshToken:null, status: .OUTDATED });
|
|
291
|
-
return false;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
account.accessToken = tokens.accessToken;
|
|
295
|
-
account.tokenExpireDate = tokens.expireDate;
|
|
296
|
-
return this.userAccountsService.updateAccount(user, account).then(() => true);
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const logger$3 = new Logger('alp:auth:userAccounts');
|
|
303
|
-
const STATUSES = {
|
|
304
|
-
VALIDATED: 'validated',
|
|
305
|
-
DELETED: 'deleted'
|
|
306
|
-
};
|
|
307
|
-
class UserAccountsService extends EventEmitter {
|
|
308
|
-
constructor(usersManager, strategyToService) {
|
|
309
|
-
super();
|
|
310
|
-
this.usersManager = usersManager;
|
|
311
|
-
this.strategyToService = strategyToService;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
getScope(strategy, scopeKey, user, accountId) {
|
|
315
|
-
logger$3.debug('getScope', {
|
|
316
|
-
strategy,
|
|
317
|
-
userId: user === null || user === void 0 ? void 0 : user._id
|
|
318
|
-
});
|
|
319
|
-
const service = this.strategyToService[strategy];
|
|
320
|
-
|
|
321
|
-
if (!service) {
|
|
322
|
-
throw new Error('Strategy not supported');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const newScope = service.scopeKeyToScope[scopeKey];
|
|
326
|
-
|
|
327
|
-
if (!user || !accountId) {
|
|
328
|
-
return newScope;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const account = user.accounts.find(account => account.provider === strategy && account.accountId === accountId);
|
|
332
|
-
|
|
333
|
-
if (!account) {
|
|
334
|
-
throw new Error('Could not found associated account');
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return service.getScope(account.scope, newScope).join(' ');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
async update(user, strategy, tokens, scope, subservice) {
|
|
341
|
-
const service = this.strategyToService[strategy];
|
|
342
|
-
const profile = await service.getProfile(tokens);
|
|
343
|
-
const accountId = service.getId(profile);
|
|
344
|
-
const account = user.accounts.find(account => account.provider === strategy && account.accountId === accountId);
|
|
345
|
-
|
|
346
|
-
if (!account) {
|
|
347
|
-
// TODO check if already exists in other user => merge
|
|
348
|
-
// TODO else add a new account in this user
|
|
349
|
-
throw new Error('Could not found associated account');
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
account.status = 'valid';
|
|
353
|
-
account.accessToken = tokens.accessToken;
|
|
354
|
-
|
|
355
|
-
if (tokens.refreshToken) {
|
|
356
|
-
account.refreshToken = tokens.refreshToken;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (tokens.expireDate) {
|
|
360
|
-
account.tokenExpireDate = tokens.expireDate;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
account.scope = service.getScope(account.scope, scope);
|
|
364
|
-
account.subservices = account.subservices || [];
|
|
365
|
-
|
|
366
|
-
if (subservice && !account.subservices.includes(subservice)) {
|
|
367
|
-
account.subservices.push(subservice);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
await this.usersManager.replaceOne(user);
|
|
371
|
-
return {
|
|
372
|
-
user,
|
|
373
|
-
account
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
async findOrCreateFromStrategy(strategy, tokens, scope, subservice) {
|
|
378
|
-
const service = this.strategyToService[strategy];
|
|
379
|
-
if (!service) throw new Error('Strategy not supported');
|
|
380
|
-
const profile = await service.getProfile(tokens);
|
|
381
|
-
const accountId = service.getId(profile);
|
|
382
|
-
if (!accountId) throw new Error('Invalid profile: no id found');
|
|
383
|
-
const emails = service.getEmails(profile);
|
|
384
|
-
let user = await this.usersManager.findOneByAccountOrEmails({
|
|
385
|
-
provider: service.providerKey,
|
|
386
|
-
accountId,
|
|
387
|
-
emails
|
|
388
|
-
});
|
|
389
|
-
logger$3.info(!user ? 'create user' : 'existing user', {
|
|
390
|
-
emails,
|
|
391
|
-
user
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
if (!user) {
|
|
395
|
-
user = {};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
Object.assign(user, {
|
|
399
|
-
displayName: service.getDisplayName(profile),
|
|
400
|
-
fullName: service.getFullName(profile),
|
|
401
|
-
status: STATUSES.VALIDATED
|
|
402
|
-
});
|
|
403
|
-
if (!user.accounts) user.accounts = [];
|
|
404
|
-
let account = user.accounts.find(account => account.provider === strategy && account.accountId === accountId);
|
|
405
|
-
|
|
406
|
-
if (!account) {
|
|
407
|
-
account = {
|
|
408
|
-
provider: strategy,
|
|
409
|
-
accountId
|
|
410
|
-
}; // @ts-expect-error well...
|
|
411
|
-
|
|
412
|
-
user.accounts.push(account);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
account.name = service.getAccountName(profile);
|
|
416
|
-
account.status = 'valid';
|
|
417
|
-
account.profile = profile;
|
|
418
|
-
account.accessToken = tokens.accessToken;
|
|
419
|
-
|
|
420
|
-
if (tokens.refreshToken) {
|
|
421
|
-
account.refreshToken = tokens.refreshToken;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (tokens.expireDate) {
|
|
425
|
-
account.tokenExpireDate = tokens.expireDate;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
account.scope = service.getScope(account.scope, scope);
|
|
429
|
-
if (!account.subservices) account.subservices = [];
|
|
430
|
-
|
|
431
|
-
if (subservice && !account.subservices.includes(subservice)) {
|
|
432
|
-
account.subservices.push(subservice);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (!user.emails) user.emails = [];
|
|
436
|
-
const userEmails = user.emails;
|
|
437
|
-
emails.forEach(email => {
|
|
438
|
-
if (!userEmails.includes(email)) {
|
|
439
|
-
userEmails.push(email);
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
user.emailDomains = [...user.emails.reduce((domains, email) => domains.add(email.split('@', 2)[1]), new Set())];
|
|
443
|
-
const keyPath = this.usersManager.store.keyPath;
|
|
444
|
-
|
|
445
|
-
if (user[keyPath]) {
|
|
446
|
-
await this.usersManager.replaceOne(user);
|
|
447
|
-
} else {
|
|
448
|
-
await this.usersManager.insertOne(user);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
return user;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async updateAccount(user, account) {
|
|
455
|
-
await this.usersManager.updateAccount(user, account);
|
|
456
|
-
return user;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const COOKIE_NAME = 'connectedUser';
|
|
462
|
-
const getTokenFromRequest = (req, options) => {
|
|
463
|
-
const cookies = new Cookies(req, null, { ...options,
|
|
464
|
-
secure: true
|
|
465
|
-
});
|
|
466
|
-
return cookies.get(COOKIE_NAME);
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
const verifyPromisified = promisify(jsonwebtoken.verify);
|
|
470
|
-
|
|
471
|
-
const createDecodeJWT = secretKey => async (token, jwtAudience) => {
|
|
472
|
-
const result = await verifyPromisified(token, secretKey, {
|
|
473
|
-
algorithms: ['HS512'],
|
|
474
|
-
audience: jwtAudience
|
|
475
|
-
});
|
|
476
|
-
return result === null || result === void 0 ? void 0 : result.connected;
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
const createFindConnectedAndUser = (secretKey, usersManager, logger) => {
|
|
480
|
-
const decodeJwt = createDecodeJWT(secretKey);
|
|
481
|
-
return async (jwtAudience, token) => {
|
|
482
|
-
if (!token || !jwtAudience) return [null, null];
|
|
483
|
-
let connected;
|
|
484
|
-
|
|
485
|
-
try {
|
|
486
|
-
connected = await decodeJwt(token, jwtAudience);
|
|
487
|
-
} catch (err) {
|
|
488
|
-
logger.debug('failed to verify authentification', {
|
|
489
|
-
err
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (connected == null) return [null, null];
|
|
494
|
-
const user = await usersManager.findConnected(connected);
|
|
495
|
-
return [connected, user];
|
|
496
|
-
};
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
class MongoUsersManager {
|
|
500
|
-
constructor(store) {
|
|
501
|
-
this.store = store;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
findConnected(connected) {
|
|
505
|
-
return this.store.findByKey(connected);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
insertOne(user) {
|
|
509
|
-
return this.store.insertOne(user);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
replaceOne(user) {
|
|
513
|
-
return this.store.replaceOne(user);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
sanitize(user) {
|
|
517
|
-
return this.sanitizeBaseUser(user);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
findOneByAccountOrEmails({
|
|
521
|
-
accountId,
|
|
522
|
-
emails,
|
|
523
|
-
provider
|
|
524
|
-
}) {
|
|
525
|
-
let query = {
|
|
526
|
-
'accounts.provider': provider,
|
|
527
|
-
'accounts.accountId': accountId
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
if (emails && emails.length > 0) {
|
|
531
|
-
query = {
|
|
532
|
-
$or: [query, {
|
|
533
|
-
emails: {
|
|
534
|
-
$in: emails
|
|
535
|
-
}
|
|
536
|
-
}]
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return this.store.findOne(query);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
updateAccount(user, account) {
|
|
544
|
-
const accountIndex = user.accounts.indexOf(account);
|
|
545
|
-
|
|
546
|
-
if (accountIndex === -1) {
|
|
547
|
-
throw new Error('Invalid account');
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return this.store.partialUpdateOne(user, {
|
|
551
|
-
$set: {
|
|
552
|
-
[`accounts.${accountIndex}`]: account
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
sanitizeBaseUser(user) {
|
|
558
|
-
return {
|
|
559
|
-
_id: user._id,
|
|
560
|
-
created: user.created,
|
|
561
|
-
updated: user.updated,
|
|
562
|
-
displayName: user.displayName,
|
|
563
|
-
fullName: user.fullName,
|
|
564
|
-
status: user.status,
|
|
565
|
-
emails: user.emails,
|
|
566
|
-
emailDomains: user.emailDomains,
|
|
567
|
-
accounts: user.accounts.map(account => ({
|
|
568
|
-
provider: account.provider,
|
|
569
|
-
accountId: account.accountId,
|
|
570
|
-
name: account.name,
|
|
571
|
-
status: account.status,
|
|
572
|
-
profile: account.profile
|
|
573
|
-
}))
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/* global fetch */
|
|
580
|
-
class UserAccountGoogleService {
|
|
581
|
-
constructor(scopeKeyToScope) {
|
|
582
|
-
this.providerKey = 'google';
|
|
583
|
-
this.scopeKeyToScope = { ...scopeKeyToScope,
|
|
584
|
-
login: 'openid profile email'
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
getProfile(tokens) {
|
|
589
|
-
return fetch(`https://www.googleapis.com/oauth2/v1/userinfo?access_token=${tokens.accessToken}`).then(response => response.json());
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
getId(profile) {
|
|
593
|
-
return profile.id;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
getAccountName(profile) {
|
|
597
|
-
return profile.email;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
getEmails(profile) {
|
|
601
|
-
const emails = [];
|
|
602
|
-
|
|
603
|
-
if (profile.email) {
|
|
604
|
-
emails.push(profile.email);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
return emails;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
getDisplayName(profile) {
|
|
611
|
-
return profile.name;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
getFullName(profile) {
|
|
615
|
-
return {
|
|
616
|
-
givenName: profile.given_name,
|
|
617
|
-
familyName: profile.family_name
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
getDefaultScope(newScope) {
|
|
622
|
-
return this.getScope(undefined, newScope);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
getScope(oldScope, newScope) {
|
|
626
|
-
return !oldScope ? newScope.split(' ') : oldScope.concat(newScope.split(' ')).filter((item, i, ar) => ar.indexOf(item) === i);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
/* global fetch */
|
|
632
|
-
// https://api.slack.com/methods/users.identity
|
|
633
|
-
class UserAccountSlackService {
|
|
634
|
-
constructor(scopeKeyToScope) {
|
|
635
|
-
this.providerKey = 'google';
|
|
636
|
-
this.scopeKeyToScope = { ...scopeKeyToScope,
|
|
637
|
-
login: 'identity.basic identity.email identity.avatar'
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
getProfile(tokens) {
|
|
642
|
-
return fetch(`https://slack.com/api/users.identity?token=${tokens.accessToken}`).then(response => response.json());
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
getId(profile) {
|
|
646
|
-
if (!profile || !profile.team || !profile.team.id || !profile.user || !profile.user.id) {
|
|
647
|
-
return null;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return `team:${profile.team.id};user:${profile.user.id}`;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
getAccountName(profile) {
|
|
654
|
-
return profile.user.email;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
getEmails(profile) {
|
|
658
|
-
return profile.user.email ? [profile.user.email] : [];
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
getDisplayName(profile) {
|
|
662
|
-
return profile.user.name;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
getFullName() {
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
getDefaultScope(newScope) {
|
|
670
|
-
return this.getScope(undefined, newScope);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
getScope(oldScope, newScope) {
|
|
674
|
-
return !oldScope ? newScope.split(' ') : oldScope.concat(newScope.split(' ')).filter((item, i, ar) => ar.indexOf(item) === i);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
const logger$2 = new Logger('alp:auth');
|
|
680
|
-
const authSocketIO = (app, usersManager, io) => {
|
|
681
|
-
const findConnectedAndUser = createFindConnectedAndUser(app.config.get('authentication').get('secretKey'), usersManager, logger$2);
|
|
682
|
-
const users = new Map();
|
|
683
|
-
io.users = users;
|
|
684
|
-
io.use(async (socket, next) => {
|
|
685
|
-
const handshakeData = socket.request;
|
|
686
|
-
const token = getTokenFromRequest(handshakeData);
|
|
687
|
-
if (!token) return next();
|
|
688
|
-
const [connected, user] = await findConnectedAndUser(handshakeData.headers['user-agent'], token);
|
|
689
|
-
if (!connected || !user) return next();
|
|
690
|
-
socket.user = user;
|
|
691
|
-
users.set(socket.client.id, user);
|
|
692
|
-
socket.on('disconnected', () => users.delete(socket.client.id));
|
|
693
|
-
await next();
|
|
694
|
-
});
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
const logger$1 = new Logger('alp:auth');
|
|
698
|
-
|
|
699
|
-
const getTokenFromReq = req => {
|
|
700
|
-
if (req.cookies) return req.cookies[COOKIE_NAME];
|
|
701
|
-
return getTokenFromRequest(req);
|
|
702
|
-
};
|
|
703
|
-
/*
|
|
704
|
-
* Not tested yet.
|
|
705
|
-
* @internal
|
|
706
|
-
*/
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const createAuthApolloContext = (config, usersManager) => {
|
|
710
|
-
const findConnectedAndUser = createFindConnectedAndUser(config.get('authentication').get('secretKey'), usersManager, logger$1);
|
|
711
|
-
return async ({
|
|
712
|
-
req,
|
|
713
|
-
connection
|
|
714
|
-
}) => {
|
|
715
|
-
if (connection !== null && connection !== void 0 && connection.user) {
|
|
716
|
-
return {
|
|
717
|
-
user: connection.user
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
if (!req) return null;
|
|
722
|
-
const token = getTokenFromReq(req);
|
|
723
|
-
if (!token) return {
|
|
724
|
-
user: undefined
|
|
725
|
-
};
|
|
726
|
-
const [, user] = await findConnectedAndUser(req.headers['user-agent'], token);
|
|
727
|
-
return {
|
|
728
|
-
user
|
|
729
|
-
};
|
|
730
|
-
};
|
|
731
|
-
};
|
|
732
|
-
|
|
733
|
-
const logger = new Logger('alp:auth');
|
|
734
|
-
const signPromisified = promisify(jsonwebtoken.sign);
|
|
735
|
-
function init({
|
|
736
|
-
homeRouterKey,
|
|
737
|
-
usersManager,
|
|
738
|
-
strategies,
|
|
739
|
-
defaultStrategy,
|
|
740
|
-
strategyToService,
|
|
741
|
-
authHooks,
|
|
742
|
-
jwtAudience
|
|
743
|
-
}) {
|
|
744
|
-
return app => {
|
|
745
|
-
const userAccountsService = new UserAccountsService(usersManager, strategyToService);
|
|
746
|
-
const authenticationService = new AuthenticationService(app.config, strategies, userAccountsService);
|
|
747
|
-
const controller = createAuthController({
|
|
748
|
-
usersManager,
|
|
749
|
-
authenticationService,
|
|
750
|
-
homeRouterKey,
|
|
751
|
-
defaultStrategy,
|
|
752
|
-
authHooks
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
app.context.setConnected = async function (connected, user) {
|
|
756
|
-
logger.debug('setConnected', {
|
|
757
|
-
connected
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
if (!connected) {
|
|
761
|
-
throw new Error('Illegal value for setConnected');
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
this.state.connected = connected;
|
|
765
|
-
this.state.user = user;
|
|
766
|
-
const token = await signPromisified({
|
|
767
|
-
connected,
|
|
768
|
-
time: Date.now()
|
|
769
|
-
}, this.config.get('authentication').get('secretKey'), {
|
|
770
|
-
algorithm: 'HS512',
|
|
771
|
-
audience: jwtAudience || this.request.headers['user-agent'],
|
|
772
|
-
expiresIn: '30 days'
|
|
773
|
-
});
|
|
774
|
-
this.cookies.set(COOKIE_NAME, token, {
|
|
775
|
-
httpOnly: true,
|
|
776
|
-
secure: this.config.get('allowHttps')
|
|
777
|
-
});
|
|
778
|
-
};
|
|
779
|
-
|
|
780
|
-
app.context.logout = function () {
|
|
781
|
-
delete this.state.connected;
|
|
782
|
-
delete this.state.user;
|
|
783
|
-
this.cookies.set(COOKIE_NAME, '', {
|
|
784
|
-
expires: new Date(1)
|
|
785
|
-
});
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
const getConnectedAndUser = createFindConnectedAndUser(app.config.get('authentication').get('secretKey'), usersManager, logger);
|
|
789
|
-
return {
|
|
790
|
-
routes: createRoutes(controller),
|
|
791
|
-
getConnectedAndUserFromRequest: req => {
|
|
792
|
-
const token = getTokenFromRequest(req);
|
|
793
|
-
return getConnectedAndUser(jwtAudience || req.headers['user-agent'], token);
|
|
794
|
-
},
|
|
795
|
-
getConnectedAndUser,
|
|
796
|
-
middleware: async (ctx, next) => {
|
|
797
|
-
const token = ctx.cookies.get(COOKIE_NAME);
|
|
798
|
-
const userAgent = ctx.request.headers['user-agent'];
|
|
799
|
-
logger.debug('middleware', {
|
|
800
|
-
token
|
|
801
|
-
});
|
|
802
|
-
|
|
803
|
-
const setState = (connected, user) => {
|
|
804
|
-
ctx.state.connected = connected;
|
|
805
|
-
ctx.state.user = user;
|
|
806
|
-
ctx.sanitizedState.connected = connected;
|
|
807
|
-
ctx.sanitizedState.user = user && usersManager.sanitize(user);
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
const [connected, user] = await getConnectedAndUser(jwtAudience || userAgent, token);
|
|
811
|
-
logger.debug('middleware', {
|
|
812
|
-
connected
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
if (connected == null || user == null) {
|
|
816
|
-
if (token) ctx.cookies.set(COOKIE_NAME, '', {
|
|
817
|
-
expires: new Date(1)
|
|
818
|
-
});
|
|
819
|
-
setState(null, null);
|
|
820
|
-
return next();
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
setState(connected, user);
|
|
824
|
-
return next();
|
|
825
|
-
}
|
|
826
|
-
};
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
export default init;
|
|
831
|
-
export { AuthenticationService, MongoUsersManager, STATUSES, UserAccountGoogleService, UserAccountSlackService, authSocketIO, createAuthApolloContext };
|
|
832
|
-
//# sourceMappingURL=index-node12.mjs.map
|