@awesome-lang-auth/node 1.10.1 → 1.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/interfaces/session-store.interface.d.ts +5 -0
- package/dist/interfaces/session-store.interface.d.ts.map +1 -1
- package/dist/models/auth-config.model.d.ts +15 -0
- package/dist/models/auth-config.model.d.ts.map +1 -1
- package/dist/models/session.model.d.ts +2 -0
- package/dist/models/session.model.d.ts.map +1 -1
- package/dist/router/admin.router.d.ts.map +1 -1
- package/dist/router/admin.router.js +47 -23
- package/dist/router/admin.router.js.map +1 -1
- package/dist/router/auth.router.d.ts.map +1 -1
- package/dist/router/auth.router.js +185 -18
- package/dist/router/auth.router.js.map +1 -1
- package/dist/services/token.service.d.ts.map +1 -1
- package/dist/services/token.service.js +9 -2
- package/dist/services/token.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.resolveApiPrefix = resolveApiPrefix;
|
|
4
37
|
exports.buildUiLink = buildUiLink;
|
|
5
38
|
exports.createAuthRouter = createAuthRouter;
|
|
6
39
|
const express_1 = require("express");
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
7
41
|
const token_service_1 = require("../services/token.service");
|
|
8
42
|
const password_service_1 = require("../services/password.service");
|
|
9
43
|
const mailer_service_1 = require("../services/mailer.service");
|
|
@@ -146,6 +180,21 @@ function decodeOAuthStateOrigin(state) {
|
|
|
146
180
|
catch { /* plain nonce or invalid base64 — ignore */ }
|
|
147
181
|
return null;
|
|
148
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Extracts the random nonce from a structured or plain OAuth state parameter.
|
|
185
|
+
* Returns `null` when state is undefined or empty.
|
|
186
|
+
*/
|
|
187
|
+
function decodeOAuthStateNonce(state) {
|
|
188
|
+
if (!state)
|
|
189
|
+
return null;
|
|
190
|
+
try {
|
|
191
|
+
const parsed = JSON.parse(Buffer.from(state, 'base64url').toString());
|
|
192
|
+
if (isOAuthState(parsed))
|
|
193
|
+
return parsed.n;
|
|
194
|
+
}
|
|
195
|
+
catch { /* plain nonce or invalid base64 */ }
|
|
196
|
+
return typeof state === 'string' && state.trim() ? state.trim() : null;
|
|
197
|
+
}
|
|
149
198
|
/**
|
|
150
199
|
* Resolves the post-OAuth redirect target from the encoded state.
|
|
151
200
|
* Falls back to `getDefaultSiteUrl` when the state carries no origin or the
|
|
@@ -242,9 +291,15 @@ function sendTokens(req, res, tokens, config) {
|
|
|
242
291
|
async function issueTokens(req, res, user, config, options, userStore, redirectTo, oldSid) {
|
|
243
292
|
const payload = buildPayload(user, config);
|
|
244
293
|
const refreshExpiryMs = parseExpiryMs(config.refreshTokenExpiresIn);
|
|
294
|
+
let createdSession;
|
|
245
295
|
if (options.sessionStore) {
|
|
296
|
+
const isSingleSession = config.session?.singleSessionPerUser ?? config.session?.singleSession ?? false;
|
|
297
|
+
if (isSingleSession && !oldSid) {
|
|
298
|
+
await options.sessionStore.revokeAllSessionsForUser(user.id, user.tenantId).catch(() => { });
|
|
299
|
+
}
|
|
246
300
|
const session = await options.sessionStore.createSession({
|
|
247
301
|
userId: user.id,
|
|
302
|
+
tenantId: user.tenantId,
|
|
248
303
|
userAgent: req.headers['user-agent'],
|
|
249
304
|
ipAddress: req.ip || req.socket.remoteAddress,
|
|
250
305
|
expiresAt: new Date(Date.now() + refreshExpiryMs),
|
|
@@ -255,10 +310,24 @@ async function issueTokens(req, res, user, config, options, userStore, redirectT
|
|
|
255
310
|
if (oldSid) {
|
|
256
311
|
await options.sessionStore.revokeSession(oldSid).catch(() => { });
|
|
257
312
|
}
|
|
313
|
+
// Save session in local variable for updating hash below
|
|
314
|
+
createdSession = session;
|
|
258
315
|
}
|
|
259
316
|
const tokens = tokenService.generateTokenPair(payload, config);
|
|
260
317
|
const refreshExpiry = new Date(Date.now() + refreshExpiryMs);
|
|
261
318
|
await userStore.updateRefreshToken(user.id, tokens.refreshToken, refreshExpiry);
|
|
319
|
+
if (options.sessionStore && payload.sid) {
|
|
320
|
+
const refreshTokenHash = crypto.createHash('sha256').update(tokens.refreshToken).digest('hex');
|
|
321
|
+
if (createdSession) {
|
|
322
|
+
createdSession.refreshTokenHash = refreshTokenHash;
|
|
323
|
+
if (!createdSession.data)
|
|
324
|
+
createdSession.data = {};
|
|
325
|
+
createdSession.data.refreshTokenHash = refreshTokenHash;
|
|
326
|
+
}
|
|
327
|
+
if (options.sessionStore.updateSessionRefreshTokenHash) {
|
|
328
|
+
await options.sessionStore.updateSessionRefreshTokenHash(payload.sid, refreshTokenHash).catch(() => { });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
262
331
|
if (redirectTo) {
|
|
263
332
|
tokenService.setTokenCookies(res, tokens, config);
|
|
264
333
|
res.redirect(redirectTo);
|
|
@@ -472,13 +541,23 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
472
541
|
if (typeof bodyRefreshToken === 'string' && bodyRefreshToken) {
|
|
473
542
|
try {
|
|
474
543
|
const payload = tokenService.verifyRefreshToken(bodyRefreshToken, config);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
if (
|
|
478
|
-
|
|
544
|
+
if (options.sessionStore && payload.sid) {
|
|
545
|
+
const session = await options.sessionStore.getSession(payload.sid);
|
|
546
|
+
if (session) {
|
|
547
|
+
const expectedHash = session.refreshTokenHash ?? session.data?.refreshTokenHash;
|
|
548
|
+
if (!expectedHash || crypto.createHash('sha256').update(bodyRefreshToken).digest('hex') === expectedHash) {
|
|
549
|
+
await options.sessionStore.revokeSession(payload.sid).catch(() => { });
|
|
550
|
+
if (!req.user)
|
|
551
|
+
req.user = payload;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
const user = await userStore.findById(payload.sub);
|
|
557
|
+
if (user && user.refreshToken === bodyRefreshToken) {
|
|
558
|
+
if (!req.user)
|
|
559
|
+
req.user = payload;
|
|
479
560
|
}
|
|
480
|
-
if (!req.user)
|
|
481
|
-
req.user = payload;
|
|
482
561
|
}
|
|
483
562
|
}
|
|
484
563
|
catch {
|
|
@@ -491,7 +570,14 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
491
570
|
const userId = req.user?.sub;
|
|
492
571
|
const sessionId = req.user?.sid;
|
|
493
572
|
if (req.user?.sub) {
|
|
494
|
-
|
|
573
|
+
let hasOtherSessions = false;
|
|
574
|
+
if (options.sessionStore?.getSessionsForUser) {
|
|
575
|
+
const remaining = await options.sessionStore.getSessionsForUser(req.user.sub).catch(() => []);
|
|
576
|
+
hasOtherSessions = remaining.length > 0;
|
|
577
|
+
}
|
|
578
|
+
if (!hasOtherSessions) {
|
|
579
|
+
await userStore.updateRefreshToken(req.user.sub, null, null);
|
|
580
|
+
}
|
|
495
581
|
}
|
|
496
582
|
tokenService.clearTokenCookies(res, config);
|
|
497
583
|
(0, router_events_1.publishRequestEvent)(eventBus, auth_event_names_1.AuthEventNames.AUTH_LOGOUT, req, {
|
|
@@ -520,18 +606,40 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
520
606
|
const payload = tokenService.verifyRefreshToken(refreshToken, config);
|
|
521
607
|
// Real-time Session Validation
|
|
522
608
|
const checkOn = config.session?.checkOn ?? 'refresh';
|
|
523
|
-
|
|
524
|
-
|
|
609
|
+
let session = null;
|
|
610
|
+
if (options.sessionStore && payload.sid) {
|
|
611
|
+
if (checkOn !== 'none') {
|
|
612
|
+
session = await options.sessionStore.getSession(payload.sid);
|
|
613
|
+
if (!session) {
|
|
614
|
+
res.status(401).json({ error: 'Session has been revoked', code: 'SESSION_REVOKED' });
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
525
618
|
if (!session) {
|
|
526
|
-
|
|
527
|
-
|
|
619
|
+
session = await options.sessionStore.getSession(payload.sid);
|
|
620
|
+
}
|
|
621
|
+
// Per-session refresh token validation (multi-device support)
|
|
622
|
+
const expectedHash = session?.refreshTokenHash ?? session?.data?.refreshTokenHash;
|
|
623
|
+
if (expectedHash) {
|
|
624
|
+
const presentedHash = crypto.createHash('sha256').update(refreshToken).digest('hex');
|
|
625
|
+
if (presentedHash !== expectedHash) {
|
|
626
|
+
res.status(401).json({ error: 'Invalid refresh token' });
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
528
629
|
}
|
|
529
630
|
}
|
|
530
631
|
const user = await userStore.findById(payload.sub);
|
|
531
|
-
if (!user
|
|
632
|
+
if (!user) {
|
|
532
633
|
res.status(401).json({ error: 'Invalid refresh token' });
|
|
533
634
|
return;
|
|
534
635
|
}
|
|
636
|
+
// If no session store is used or payload has no sid, validate against user.refreshToken
|
|
637
|
+
if (!options.sessionStore || !payload.sid) {
|
|
638
|
+
if (user.refreshToken !== refreshToken) {
|
|
639
|
+
res.status(401).json({ error: 'Invalid refresh token' });
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
535
643
|
const { sessionId } = await issueTokens(req, res, user, config, options, userStore, /* redirectTo */ undefined, payload.sid);
|
|
536
644
|
(0, router_events_1.publishRequestEvent)(eventBus, auth_event_names_1.AuthEventNames.SESSION_ROTATED, req, {
|
|
537
645
|
userId: user.id,
|
|
@@ -1243,12 +1351,29 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1243
1351
|
const resolved = resolveSiteUrl(req, config, allowedOrigins);
|
|
1244
1352
|
const returnPath = typeof req.query['return_path'] === 'string' ? req.query['return_path'] : undefined;
|
|
1245
1353
|
const state = resolved ? encodeOAuthState(nonce, resolved, returnPath) : nonce;
|
|
1354
|
+
res.cookie('oauth_nonce_google', nonce, {
|
|
1355
|
+
httpOnly: true,
|
|
1356
|
+
sameSite: 'lax',
|
|
1357
|
+
secure: config.cookieOptions?.secure ?? (req.secure || req.headers['x-forwarded-proto'] === 'https'),
|
|
1358
|
+
maxAge: 10 * 60 * 1000,
|
|
1359
|
+
path: '/',
|
|
1360
|
+
});
|
|
1246
1361
|
const url = googleStrategy.getAuthorizationUrl(state);
|
|
1247
1362
|
res.redirect(url);
|
|
1248
1363
|
});
|
|
1249
1364
|
router.get('/oauth/google/callback', ...rl, async (req, res) => {
|
|
1250
1365
|
try {
|
|
1251
1366
|
const { code, state } = req.query;
|
|
1367
|
+
const cookieNonce = req.cookies?.['oauth_nonce_google']
|
|
1368
|
+
?? tokenService.extractTokenFromCookie(req, 'oauth_nonce_google');
|
|
1369
|
+
res.clearCookie('oauth_nonce_google', { path: '/' });
|
|
1370
|
+
const stateNonce = decodeOAuthStateNonce(state);
|
|
1371
|
+
if (cookieNonce || stateNonce) {
|
|
1372
|
+
if (!cookieNonce || !stateNonce || cookieNonce !== stateNonce) {
|
|
1373
|
+
res.status(400).json({ error: 'Invalid or missing OAuth state nonce', code: 'INVALID_OAUTH_STATE' });
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1252
1377
|
const redirectTo = resolveOAuthRedirect(state, config, allowedOrigins);
|
|
1253
1378
|
const user = await googleStrategy.handleCallback(code, state);
|
|
1254
1379
|
// Link the account if linkedAccountsStore is configured
|
|
@@ -1268,14 +1393,17 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1268
1393
|
(0, router_events_1.publishRequestEvent)(eventBus, auth_event_names_1.AuthEventNames.AUTH_OAUTH_CONFLICT, req, {
|
|
1269
1394
|
data: (0, router_events_1.oauthConflictEventData)('google', err.data),
|
|
1270
1395
|
});
|
|
1271
|
-
const
|
|
1396
|
+
const rawOrigin = decodeOAuthStateOrigin(req.query.state);
|
|
1397
|
+
const conflictOrigin = (rawOrigin && (allowedOrigins.length === 0 || allowedOrigins.includes(rawOrigin)))
|
|
1398
|
+
? rawOrigin
|
|
1399
|
+
: getDefaultSiteUrl(config);
|
|
1272
1400
|
const { email, providerAccountId } = (err.data ?? {});
|
|
1273
1401
|
if (options.pendingLinkStore && email && providerAccountId) {
|
|
1274
1402
|
await options.pendingLinkStore.stash(email, 'google', providerAccountId).catch((e) => { console.error('[node-auth] pendingLinkStore.stash error (google):', e); });
|
|
1275
1403
|
}
|
|
1276
1404
|
const emailParam = email ? `&email=${encodeURIComponent(email)}` : '';
|
|
1277
1405
|
const paramStr = `?provider=google&code=OAUTH_ACCOUNT_CONFLICT${emailParam}`;
|
|
1278
|
-
res.redirect(buildUiLink(
|
|
1406
|
+
res.redirect(buildUiLink(conflictOrigin, `/account-conflict${paramStr}`, config, options));
|
|
1279
1407
|
return;
|
|
1280
1408
|
}
|
|
1281
1409
|
handleError(res, err);
|
|
@@ -1294,12 +1422,29 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1294
1422
|
const resolved = resolveSiteUrl(req, config, allowedOrigins);
|
|
1295
1423
|
const returnPath = typeof req.query['return_path'] === 'string' ? req.query['return_path'] : undefined;
|
|
1296
1424
|
const state = resolved ? encodeOAuthState(nonce, resolved, returnPath) : nonce;
|
|
1425
|
+
res.cookie('oauth_nonce_github', nonce, {
|
|
1426
|
+
httpOnly: true,
|
|
1427
|
+
sameSite: 'lax',
|
|
1428
|
+
secure: config.cookieOptions?.secure ?? (req.secure || req.headers['x-forwarded-proto'] === 'https'),
|
|
1429
|
+
maxAge: 10 * 60 * 1000,
|
|
1430
|
+
path: '/',
|
|
1431
|
+
});
|
|
1297
1432
|
const url = githubStrategy.getAuthorizationUrl(state);
|
|
1298
1433
|
res.redirect(url);
|
|
1299
1434
|
});
|
|
1300
1435
|
router.get('/oauth/github/callback', ...rl, async (req, res) => {
|
|
1301
1436
|
try {
|
|
1302
1437
|
const { code, state } = req.query;
|
|
1438
|
+
const cookieNonce = req.cookies?.['oauth_nonce_github']
|
|
1439
|
+
?? tokenService.extractTokenFromCookie(req, 'oauth_nonce_github');
|
|
1440
|
+
res.clearCookie('oauth_nonce_github', { path: '/' });
|
|
1441
|
+
const stateNonce = decodeOAuthStateNonce(state);
|
|
1442
|
+
if (cookieNonce || stateNonce) {
|
|
1443
|
+
if (!cookieNonce || !stateNonce || cookieNonce !== stateNonce) {
|
|
1444
|
+
res.status(400).json({ error: 'Invalid or missing OAuth state nonce', code: 'INVALID_OAUTH_STATE' });
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1303
1448
|
const redirectTo = resolveOAuthRedirect(state, config, allowedOrigins);
|
|
1304
1449
|
const user = await githubStrategy.handleCallback(code, state);
|
|
1305
1450
|
// Link the account if linkedAccountsStore is configured
|
|
@@ -1319,14 +1464,17 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1319
1464
|
(0, router_events_1.publishRequestEvent)(eventBus, auth_event_names_1.AuthEventNames.AUTH_OAUTH_CONFLICT, req, {
|
|
1320
1465
|
data: (0, router_events_1.oauthConflictEventData)('github', err.data),
|
|
1321
1466
|
});
|
|
1322
|
-
const
|
|
1467
|
+
const rawOrigin = decodeOAuthStateOrigin(req.query.state);
|
|
1468
|
+
const conflictOrigin = (rawOrigin && (allowedOrigins.length === 0 || allowedOrigins.includes(rawOrigin)))
|
|
1469
|
+
? rawOrigin
|
|
1470
|
+
: getDefaultSiteUrl(config);
|
|
1323
1471
|
const { email, providerAccountId } = (err.data ?? {});
|
|
1324
1472
|
if (options.pendingLinkStore && email && providerAccountId) {
|
|
1325
1473
|
await options.pendingLinkStore.stash(email, 'github', providerAccountId).catch((e) => { console.error('[node-auth] pendingLinkStore.stash error (github):', e); });
|
|
1326
1474
|
}
|
|
1327
1475
|
const emailParam = email ? `&email=${encodeURIComponent(email)}` : '';
|
|
1328
1476
|
const paramStr = `?provider=github&code=OAUTH_ACCOUNT_CONFLICT${emailParam}`;
|
|
1329
|
-
res.redirect(buildUiLink(
|
|
1477
|
+
res.redirect(buildUiLink(conflictOrigin, `/account-conflict${paramStr}`, config, options));
|
|
1330
1478
|
return;
|
|
1331
1479
|
}
|
|
1332
1480
|
handleError(res, err);
|
|
@@ -1346,11 +1494,27 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1346
1494
|
const resolved = resolveSiteUrl(req, config, allowedOrigins);
|
|
1347
1495
|
const returnPath = typeof req.query['return_path'] === 'string' ? req.query['return_path'] : undefined;
|
|
1348
1496
|
const state = resolved ? encodeOAuthState(nonce, resolved, returnPath) : nonce;
|
|
1497
|
+
res.cookie(`oauth_nonce_${s.name}`, nonce, {
|
|
1498
|
+
httpOnly: true,
|
|
1499
|
+
sameSite: 'lax',
|
|
1500
|
+
secure: config.cookieOptions?.secure ?? (req.secure || req.headers['x-forwarded-proto'] === 'https'),
|
|
1501
|
+
maxAge: 10 * 60 * 1000,
|
|
1502
|
+
path: '/',
|
|
1503
|
+
});
|
|
1349
1504
|
res.redirect(s.getAuthorizationUrl(state));
|
|
1350
1505
|
});
|
|
1351
1506
|
router.get(`/oauth/${s.name}/callback`, ...rl, async (req, res) => {
|
|
1352
1507
|
try {
|
|
1353
1508
|
const { code, state } = req.query;
|
|
1509
|
+
const cookieName = `oauth_nonce_${s.name}`;
|
|
1510
|
+
const cookieNonce = req.cookies?.[cookieName]
|
|
1511
|
+
?? tokenService.extractTokenFromCookie(req, cookieName);
|
|
1512
|
+
res.clearCookie(cookieName, { path: '/' });
|
|
1513
|
+
const stateNonce = decodeOAuthStateNonce(state);
|
|
1514
|
+
if (cookieNonce && (!stateNonce || cookieNonce !== stateNonce)) {
|
|
1515
|
+
res.status(400).json({ error: 'Invalid or missing OAuth state nonce', code: 'INVALID_OAUTH_STATE' });
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1354
1518
|
const redirectTo = resolveOAuthRedirect(state, config, allowedOrigins);
|
|
1355
1519
|
const user = await s.handleCallback(code, state);
|
|
1356
1520
|
// Link the account if linkedAccountsStore is configured
|
|
@@ -1370,14 +1534,17 @@ function createAuthRouter(userStore, config, options = {}) {
|
|
|
1370
1534
|
(0, router_events_1.publishRequestEvent)(eventBus, auth_event_names_1.AuthEventNames.AUTH_OAUTH_CONFLICT, req, {
|
|
1371
1535
|
data: (0, router_events_1.oauthConflictEventData)(s.name, err.data),
|
|
1372
1536
|
});
|
|
1373
|
-
const
|
|
1537
|
+
const rawOrigin = decodeOAuthStateOrigin(req.query.state);
|
|
1538
|
+
const conflictOrigin = (rawOrigin && (allowedOrigins.length === 0 || allowedOrigins.includes(rawOrigin)))
|
|
1539
|
+
? rawOrigin
|
|
1540
|
+
: getDefaultSiteUrl(config);
|
|
1374
1541
|
const { email, providerAccountId } = (err.data ?? {});
|
|
1375
1542
|
if (options.pendingLinkStore && email && providerAccountId) {
|
|
1376
1543
|
await options.pendingLinkStore.stash(email, s.name, providerAccountId).catch((e) => { console.error(`[node-auth] pendingLinkStore.stash error (${s.name}):`, e); });
|
|
1377
1544
|
}
|
|
1378
1545
|
const emailParam = email ? `&email=${encodeURIComponent(email)}` : '';
|
|
1379
1546
|
const paramStr = `?provider=${encodeURIComponent(s.name)}&code=OAUTH_ACCOUNT_CONFLICT${emailParam}`;
|
|
1380
|
-
res.redirect(buildUiLink(
|
|
1547
|
+
res.redirect(buildUiLink(conflictOrigin, `/account-conflict${paramStr}`, config, options));
|
|
1381
1548
|
return;
|
|
1382
1549
|
}
|
|
1383
1550
|
handleError(res, err);
|