najm-auth 2.0.3 → 2.0.5
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/README.md +8 -2
- package/dist/client/edge.d.ts +19 -6
- package/dist/client/edge.js +281 -30
- package/dist/client/server/index.d.ts +27 -40
- package/dist/client/server/index.js +359 -182
- package/dist/index.d.ts +31 -2
- package/dist/index.js +134 -30
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { User, NewUser, RoleEntity, NewRoleEntity, Permission, NewPermission, Ro
|
|
|
9
9
|
export { NewOAuthAccount, NewRolePermission, NewToken, OAuthAccount, Token, authSchema, baseFields, oauthAccountsTable, permissionsTable, rolePermissionsTable, rolesTable, tokenStatusEnum, tokenTypeEnum, tokensTable, userStatusEnum, usersTable } from './schema/pg.js';
|
|
10
10
|
import { CacheService } from 'najm-cache';
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
+
import { Context } from 'hono';
|
|
12
13
|
import { GuardResult } from 'najm-guard';
|
|
13
14
|
import 'drizzle-orm';
|
|
14
15
|
import 'drizzle-orm/pg-core';
|
|
@@ -42,7 +43,7 @@ interface SessionCookieConfig {
|
|
|
42
43
|
name: string;
|
|
43
44
|
/** Max age in seconds (default: 300 = 5 min) */
|
|
44
45
|
maxAge: number;
|
|
45
|
-
/**
|
|
46
|
+
/** HMAC secret. Falls back to NAJM_SESSION_SECRET, then jwt.accessSecret. */
|
|
46
47
|
secret?: string;
|
|
47
48
|
}
|
|
48
49
|
type OAuthProvider = 'google';
|
|
@@ -279,6 +280,7 @@ var auth = {
|
|
|
279
280
|
passwordReset: "Password has been reset successfully",
|
|
280
281
|
accountInviteSent: "Invitation sent successfully",
|
|
281
282
|
tokenRefreshed: "Token refreshed successfully",
|
|
283
|
+
sessionRecovered: "Session recovered successfully",
|
|
282
284
|
oauthLogin: "Google sign-in successful",
|
|
283
285
|
oauthLinked: "Google account linked successfully"
|
|
284
286
|
},
|
|
@@ -396,6 +398,7 @@ declare const AUTH_LOCALES: {
|
|
|
396
398
|
passwordReset: string;
|
|
397
399
|
accountInviteSent: string;
|
|
398
400
|
tokenRefreshed: string;
|
|
401
|
+
sessionRecovered: string;
|
|
399
402
|
oauthLogin: string;
|
|
400
403
|
oauthLinked: string;
|
|
401
404
|
};
|
|
@@ -518,7 +521,7 @@ declare class CookieManager {
|
|
|
518
521
|
getCookieName(): string;
|
|
519
522
|
/**
|
|
520
523
|
* Write a signed session cookie containing user data, roles, and permissions.
|
|
521
|
-
* The cookie is HMAC-signed with the
|
|
524
|
+
* The cookie is HMAC-signed with the configured session secret so it is tamper-proof
|
|
522
525
|
* but readable without a database query. Short TTL (5 min) ensures freshness.
|
|
523
526
|
*/
|
|
524
527
|
setSessionCookie(data: Omit<SessionCookieData, 'iat'>): void;
|
|
@@ -932,7 +935,21 @@ declare class TokenService {
|
|
|
932
935
|
* the active session. Reuse detection and revocation belong to the
|
|
933
936
|
* rotation path only.
|
|
934
937
|
*/
|
|
938
|
+
private resolveRefreshSessionFromCookie;
|
|
935
939
|
resolveUserFromCookie(): Promise<string>;
|
|
940
|
+
/**
|
|
941
|
+
* Resolve authoritative claims for signed-session recovery.
|
|
942
|
+
*
|
|
943
|
+
* This deliberately bypasses the 30-second user cache so status, role, and
|
|
944
|
+
* permission changes are reflected when the short session snapshot expires.
|
|
945
|
+
* It validates but never rotates or consumes the refresh token.
|
|
946
|
+
*/
|
|
947
|
+
recoverSessionFromCookie(): Promise<{
|
|
948
|
+
user: any;
|
|
949
|
+
roles: any[];
|
|
950
|
+
permissions: any;
|
|
951
|
+
sessionVersion: number;
|
|
952
|
+
}>;
|
|
936
953
|
getUser(auth: string): Promise<any>;
|
|
937
954
|
getUserById(userId: string): Promise<any>;
|
|
938
955
|
private hashToken;
|
|
@@ -1009,6 +1026,7 @@ declare class TokenService {
|
|
|
1009
1026
|
accessTokenExpiresAt: number;
|
|
1010
1027
|
refreshTokenExpiresAt: number;
|
|
1011
1028
|
}>;
|
|
1029
|
+
private requireActiveRefreshUser;
|
|
1012
1030
|
/** Revoke every refresh session for a user (password change/reset, logout-all). */
|
|
1013
1031
|
revokeAllForUser(userId: string): Promise<any>;
|
|
1014
1032
|
/** Revoke a single refresh session (one family). */
|
|
@@ -1242,6 +1260,14 @@ declare class AuthService {
|
|
|
1242
1260
|
user: SanitizedUser;
|
|
1243
1261
|
}>;
|
|
1244
1262
|
refreshTokens(): Promise<TokenPair>;
|
|
1263
|
+
/**
|
|
1264
|
+
* Reissue the short-lived signed session snapshot from a fully validated
|
|
1265
|
+
* refresh session. This path never creates or returns access/refresh tokens
|
|
1266
|
+
* and never rotates the refresh family.
|
|
1267
|
+
*/
|
|
1268
|
+
recoverSession(): Promise<{
|
|
1269
|
+
recovered: true;
|
|
1270
|
+
}>;
|
|
1245
1271
|
logoutUser(userId: string, authorization?: string): Promise<{
|
|
1246
1272
|
data: any;
|
|
1247
1273
|
message: string;
|
|
@@ -1310,6 +1336,9 @@ declare class AuthController {
|
|
|
1310
1336
|
emailSent: boolean;
|
|
1311
1337
|
}>;
|
|
1312
1338
|
refreshTokens(): Promise<TokenPair>;
|
|
1339
|
+
recoverSession(recoveryRequest: string | undefined, ctx: Context): Promise<{
|
|
1340
|
+
recovered: true;
|
|
1341
|
+
}>;
|
|
1313
1342
|
logoutUser(userId: string, authorization?: string): Promise<{
|
|
1314
1343
|
data: any;
|
|
1315
1344
|
message: string;
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/AuthPlugin.ts
|
|
9
|
-
import { Err as
|
|
9
|
+
import { Err as Err11, plugin } from "najm-core";
|
|
10
10
|
import { cache } from "najm-cache";
|
|
11
11
|
|
|
12
12
|
// src/auth.tokens.ts
|
|
@@ -293,6 +293,47 @@ EncryptionService = __decorate([
|
|
|
293
293
|
import { Service, Inject as Inject2 } from "najm-core";
|
|
294
294
|
import { CookieService } from "najm-cookies";
|
|
295
295
|
import timestring from "timestring";
|
|
296
|
+
|
|
297
|
+
// src/client/sessionCookie.ts
|
|
298
|
+
var DEFAULT_SESSION_MAX_AGE_SECONDS = 300;
|
|
299
|
+
var MAX_CLOCK_SKEW_MS = 3e4;
|
|
300
|
+
function parseSessionCookiePayload(payload, maxAgeSeconds = DEFAULT_SESSION_MAX_AGE_SECONDS, now = Date.now()) {
|
|
301
|
+
if (!Number.isFinite(maxAgeSeconds) || maxAgeSeconds <= 0) return null;
|
|
302
|
+
try {
|
|
303
|
+
const data = JSON.parse(payload);
|
|
304
|
+
if (!isRecord(data) || !isValidUser(data.user)) return null;
|
|
305
|
+
if (!isStringArray(data.roles) || !isStringArray(data.permissions)) return null;
|
|
306
|
+
if (!Number.isInteger(data.sessionVersion) || data.sessionVersion < 0) return null;
|
|
307
|
+
if (!Number.isFinite(data.iat) || !Number.isInteger(data.iat) || data.iat <= 0) return null;
|
|
308
|
+
const issuedAt = data.iat;
|
|
309
|
+
if (issuedAt > now + MAX_CLOCK_SKEW_MS) return null;
|
|
310
|
+
if (now - issuedAt >= maxAgeSeconds * 1e3) return null;
|
|
311
|
+
return {
|
|
312
|
+
user: data.user,
|
|
313
|
+
roles: [...data.roles],
|
|
314
|
+
permissions: [...data.permissions],
|
|
315
|
+
sessionVersion: data.sessionVersion,
|
|
316
|
+
iat: issuedAt
|
|
317
|
+
};
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
__name(parseSessionCookiePayload, "parseSessionCookiePayload");
|
|
323
|
+
function isRecord(value) {
|
|
324
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
325
|
+
}
|
|
326
|
+
__name(isRecord, "isRecord");
|
|
327
|
+
function isValidUser(value) {
|
|
328
|
+
return isRecord(value) && typeof value.id === "string" && value.id.length > 0 && typeof value.email === "string" && value.email.length > 0 && (value.role === void 0 || value.role === null || typeof value.role === "string");
|
|
329
|
+
}
|
|
330
|
+
__name(isValidUser, "isValidUser");
|
|
331
|
+
function isStringArray(value) {
|
|
332
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
333
|
+
}
|
|
334
|
+
__name(isStringArray, "isStringArray");
|
|
335
|
+
|
|
336
|
+
// src/auth/CookieManager.ts
|
|
296
337
|
var __decorate2 = function(decorators, target, key, desc) {
|
|
297
338
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
298
339
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -348,7 +389,7 @@ var CookieManager = class CookieManager2 {
|
|
|
348
389
|
// =========================================================================
|
|
349
390
|
/**
|
|
350
391
|
* Write a signed session cookie containing user data, roles, and permissions.
|
|
351
|
-
* The cookie is HMAC-signed with the
|
|
392
|
+
* The cookie is HMAC-signed with the configured session secret so it is tamper-proof
|
|
352
393
|
* but readable without a database query. Short TTL (5 min) ensures freshness.
|
|
353
394
|
*/
|
|
354
395
|
setSessionCookie(data) {
|
|
@@ -368,15 +409,7 @@ var CookieManager = class CookieManager2 {
|
|
|
368
409
|
const raw = this.cookieService.getSigned(this.sessionCookieName, this.sessionSecret);
|
|
369
410
|
if (!raw)
|
|
370
411
|
return null;
|
|
371
|
-
|
|
372
|
-
const data = JSON.parse(raw);
|
|
373
|
-
const age = Date.now() - data.iat;
|
|
374
|
-
if (age > this.sessionMaxAge * 1e3)
|
|
375
|
-
return null;
|
|
376
|
-
return data;
|
|
377
|
-
} catch {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
412
|
+
return parseSessionCookiePayload(raw, this.sessionMaxAge);
|
|
380
413
|
}
|
|
381
414
|
/**
|
|
382
415
|
* Clear the session cookie (on logout, password change, etc.)
|
|
@@ -398,9 +431,9 @@ CookieManager = __decorate2([
|
|
|
398
431
|
], CookieManager);
|
|
399
432
|
|
|
400
433
|
// src/auth/AuthController.ts
|
|
401
|
-
import { Controller } from "najm-core";
|
|
434
|
+
import { Controller, Err as Err9 } from "najm-core";
|
|
402
435
|
import { Get, Post, ResMsg } from "najm-core";
|
|
403
|
-
import { Body, User as User2, Headers } from "najm-core";
|
|
436
|
+
import { Body, User as User2, Headers, Ctx } from "najm-core";
|
|
404
437
|
|
|
405
438
|
// src/auth/AuthService.ts
|
|
406
439
|
import { Injectable as Injectable8, Inject as Inject8 } from "najm-core";
|
|
@@ -1605,7 +1638,7 @@ var TokenService = class TokenService2 {
|
|
|
1605
1638
|
* the active session. Reuse detection and revocation belong to the
|
|
1606
1639
|
* rotation path only.
|
|
1607
1640
|
*/
|
|
1608
|
-
async
|
|
1641
|
+
async resolveRefreshSessionFromCookie() {
|
|
1609
1642
|
const refreshToken = this.cookieManager.getRefreshToken();
|
|
1610
1643
|
if (!refreshToken) {
|
|
1611
1644
|
Err6(this.t("errors.refreshTokenMissing"));
|
|
@@ -1617,14 +1650,35 @@ var TokenService = class TokenService2 {
|
|
|
1617
1650
|
}
|
|
1618
1651
|
const presentedHash = this.hashToken(refreshToken);
|
|
1619
1652
|
if (presentedHash === stored.token) {
|
|
1620
|
-
return userId;
|
|
1653
|
+
return { userId, tokenFamily };
|
|
1621
1654
|
}
|
|
1622
1655
|
const canRecover = stored.previousHash && presentedHash === stored.previousHash && stored.previousValidUntil && new Date(stored.previousValidUntil).getTime() > Date.now() && !stored.previousUsedAt;
|
|
1623
1656
|
if (canRecover) {
|
|
1624
|
-
return userId;
|
|
1657
|
+
return { userId, tokenFamily };
|
|
1625
1658
|
}
|
|
1626
1659
|
Err6(this.t("errors.refreshTokenInvalid"));
|
|
1627
1660
|
}
|
|
1661
|
+
async resolveUserFromCookie() {
|
|
1662
|
+
return (await this.resolveRefreshSessionFromCookie()).userId;
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Resolve authoritative claims for signed-session recovery.
|
|
1666
|
+
*
|
|
1667
|
+
* This deliberately bypasses the 30-second user cache so status, role, and
|
|
1668
|
+
* permission changes are reflected when the short session snapshot expires.
|
|
1669
|
+
* It validates but never rotates or consumes the refresh token.
|
|
1670
|
+
*/
|
|
1671
|
+
async recoverSessionFromCookie() {
|
|
1672
|
+
const { userId, tokenFamily } = await this.resolveRefreshSessionFromCookie();
|
|
1673
|
+
const user = await this.requireActiveRefreshUser(userId, tokenFamily);
|
|
1674
|
+
const sessionVersion = await this.getUserSessionVersion(userId);
|
|
1675
|
+
return {
|
|
1676
|
+
user,
|
|
1677
|
+
roles: user.role ? [user.role] : [],
|
|
1678
|
+
permissions: Array.isArray(user.permissions) ? user.permissions : [],
|
|
1679
|
+
sessionVersion
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1628
1682
|
// ============ USER RETRIEVAL (MAIN METHOD) ============
|
|
1629
1683
|
async getUser(auth2) {
|
|
1630
1684
|
if (!auth2)
|
|
@@ -1787,6 +1841,7 @@ var TokenService = class TokenService2 {
|
|
|
1787
1841
|
Err6(this.t("errors.refreshTokenInvalid"));
|
|
1788
1842
|
}
|
|
1789
1843
|
const presentedHash = this.hashToken(refreshToken);
|
|
1844
|
+
await this.requireActiveRefreshUser(userId, tokenFamily);
|
|
1790
1845
|
if (presentedHash === stored.token) {
|
|
1791
1846
|
return this.generateTokens(userId, tokenFamily);
|
|
1792
1847
|
}
|
|
@@ -1801,6 +1856,14 @@ var TokenService = class TokenService2 {
|
|
|
1801
1856
|
await this.revokeSuspectRefreshFamily(userId, tokenFamily);
|
|
1802
1857
|
Err6(this.t("errors.refreshTokenInvalid"));
|
|
1803
1858
|
}
|
|
1859
|
+
async requireActiveRefreshUser(userId, tokenFamily) {
|
|
1860
|
+
const user = await this.tokenRepository.getUser(userId);
|
|
1861
|
+
if (!user || user.status !== "active") {
|
|
1862
|
+
await this.revokeFamily(tokenFamily);
|
|
1863
|
+
Err6(this.t("errors.refreshTokenInvalid"));
|
|
1864
|
+
}
|
|
1865
|
+
return user;
|
|
1866
|
+
}
|
|
1804
1867
|
/** Revoke every refresh session for a user (password change/reset, logout-all). */
|
|
1805
1868
|
async revokeAllForUser(userId) {
|
|
1806
1869
|
return this.tokenRepository.revokeAllForUser(userId);
|
|
@@ -2243,6 +2306,27 @@ var AuthService = class AuthService2 {
|
|
|
2243
2306
|
const { userId: _userId, tokenFamily: _tokenFamily, roles: _roles, permissions: _permissions, sessionVersion: _sv, ...tokens } = generated;
|
|
2244
2307
|
return tokens;
|
|
2245
2308
|
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Reissue the short-lived signed session snapshot from a fully validated
|
|
2311
|
+
* refresh session. This path never creates or returns access/refresh tokens
|
|
2312
|
+
* and never rotates the refresh family.
|
|
2313
|
+
*/
|
|
2314
|
+
async recoverSession() {
|
|
2315
|
+
const recovered = await this.tokenService.recoverSessionFromCookie();
|
|
2316
|
+
this.cookieManager.setSessionCookie({
|
|
2317
|
+
user: {
|
|
2318
|
+
id: recovered.user.id,
|
|
2319
|
+
email: recovered.user.email,
|
|
2320
|
+
name: recovered.user.name,
|
|
2321
|
+
role: recovered.user.role ?? void 0,
|
|
2322
|
+
status: recovered.user.status ?? void 0
|
|
2323
|
+
},
|
|
2324
|
+
roles: recovered.roles,
|
|
2325
|
+
permissions: recovered.permissions,
|
|
2326
|
+
sessionVersion: recovered.sessionVersion
|
|
2327
|
+
});
|
|
2328
|
+
return { recovered: true };
|
|
2329
|
+
}
|
|
2246
2330
|
async logoutUser(userId, authorization) {
|
|
2247
2331
|
await this.tokenService.logout(userId, authorization);
|
|
2248
2332
|
this.cookieManager.clearRefreshToken();
|
|
@@ -2571,6 +2655,14 @@ var AuthController = class AuthController2 {
|
|
|
2571
2655
|
async refreshTokens() {
|
|
2572
2656
|
return this.authService.refreshTokens();
|
|
2573
2657
|
}
|
|
2658
|
+
async recoverSession(recoveryRequest, ctx) {
|
|
2659
|
+
if (recoveryRequest !== "1") {
|
|
2660
|
+
Err9("Invalid session recovery request", 400);
|
|
2661
|
+
}
|
|
2662
|
+
ctx.header("Cache-Control", "private, no-store");
|
|
2663
|
+
ctx.header("Vary", "Cookie");
|
|
2664
|
+
return this.authService.recoverSession();
|
|
2665
|
+
}
|
|
2574
2666
|
async logoutUser(userId, authorization) {
|
|
2575
2667
|
return this.authService.logoutUser(userId, authorization);
|
|
2576
2668
|
}
|
|
@@ -2626,6 +2718,16 @@ __decorate15([
|
|
|
2626
2718
|
__metadata15("design:paramtypes", []),
|
|
2627
2719
|
__metadata15("design:returntype", Promise)
|
|
2628
2720
|
], AuthController.prototype, "refreshTokens", null);
|
|
2721
|
+
__decorate15([
|
|
2722
|
+
Post("/session/recover"),
|
|
2723
|
+
RateLimit({ limit: 120, window: "1m", key: cookieFingerprint() }),
|
|
2724
|
+
ResMsg("auth.success.sessionRecovered"),
|
|
2725
|
+
__param5(0, Headers("x-najm-session-recovery")),
|
|
2726
|
+
__param5(1, Ctx()),
|
|
2727
|
+
__metadata15("design:type", Function),
|
|
2728
|
+
__metadata15("design:paramtypes", [String, Object]),
|
|
2729
|
+
__metadata15("design:returntype", Promise)
|
|
2730
|
+
], AuthController.prototype, "recoverSession", null);
|
|
2629
2731
|
__decorate15([
|
|
2630
2732
|
Post("/logout"),
|
|
2631
2733
|
isAuth(),
|
|
@@ -3438,7 +3540,7 @@ import { Injectable as Injectable11 } from "najm-core";
|
|
|
3438
3540
|
// src/permissions/PermissionValidator.ts
|
|
3439
3541
|
import { Injectable as Injectable10 } from "najm-core";
|
|
3440
3542
|
import { I18n as I18n7 } from "najm-i18n";
|
|
3441
|
-
import { Err as
|
|
3543
|
+
import { Err as Err10 } from "najm-core";
|
|
3442
3544
|
var __decorate21 = function(decorators, target, key, desc) {
|
|
3443
3545
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3444
3546
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -3467,7 +3569,7 @@ var PermissionValidator = class PermissionValidator2 {
|
|
|
3467
3569
|
async checkPermissionExists(id) {
|
|
3468
3570
|
const permission = await this.permissionRepository.getById(id);
|
|
3469
3571
|
if (!permission) {
|
|
3470
|
-
|
|
3572
|
+
Err10(this.t("errors.notFound"), 404);
|
|
3471
3573
|
}
|
|
3472
3574
|
return permission;
|
|
3473
3575
|
}
|
|
@@ -3477,7 +3579,7 @@ var PermissionValidator = class PermissionValidator2 {
|
|
|
3477
3579
|
async checkPermissionExistsByName(name) {
|
|
3478
3580
|
const permission = await this.permissionRepository.getByName(name);
|
|
3479
3581
|
if (!permission) {
|
|
3480
|
-
|
|
3582
|
+
Err10(this.t("errors.notFound"), 404);
|
|
3481
3583
|
}
|
|
3482
3584
|
return permission;
|
|
3483
3585
|
}
|
|
@@ -3489,7 +3591,7 @@ var PermissionValidator = class PermissionValidator2 {
|
|
|
3489
3591
|
return;
|
|
3490
3592
|
const existingPermission = await this.permissionRepository.getByName(name);
|
|
3491
3593
|
if (existingPermission && existingPermission.id !== excludeId) {
|
|
3492
|
-
|
|
3594
|
+
Err10(this.t("errors.nameExists"), 409);
|
|
3493
3595
|
}
|
|
3494
3596
|
}
|
|
3495
3597
|
/**
|
|
@@ -3512,7 +3614,7 @@ var PermissionValidator = class PermissionValidator2 {
|
|
|
3512
3614
|
await this.checkPermissionExists(permissionId);
|
|
3513
3615
|
const hasPermission = await this.permissionRepository.checkRoleHasPermission(roleId, permissionId);
|
|
3514
3616
|
if (hasPermission) {
|
|
3515
|
-
|
|
3617
|
+
Err10(this.t("errors.roleAlreadyHasPermission"), 409);
|
|
3516
3618
|
}
|
|
3517
3619
|
}
|
|
3518
3620
|
};
|
|
@@ -4561,6 +4663,7 @@ var en_default = {
|
|
|
4561
4663
|
passwordReset: "Password has been reset successfully",
|
|
4562
4664
|
accountInviteSent: "Invitation sent successfully",
|
|
4563
4665
|
tokenRefreshed: "Token refreshed successfully",
|
|
4666
|
+
sessionRecovered: "Session recovered successfully",
|
|
4564
4667
|
oauthLogin: "Google sign-in successful",
|
|
4565
4668
|
oauthLinked: "Google account linked successfully"
|
|
4566
4669
|
},
|
|
@@ -4977,7 +5080,7 @@ OAuthAccountService = __decorate29([
|
|
|
4977
5080
|
|
|
4978
5081
|
// src/oauth/OAuthController.ts
|
|
4979
5082
|
import { createHash as createHash4 } from "crypto";
|
|
4980
|
-
import { Controller as Controller5, Ctx, Get as Get5, Post as Post5, Query as Query2, User as User5 } from "najm-core";
|
|
5083
|
+
import { Controller as Controller5, Ctx as Ctx2, Get as Get5, Post as Post5, Query as Query2, User as User5 } from "najm-core";
|
|
4981
5084
|
import { RateLimit as RateLimit2 } from "najm-rate";
|
|
4982
5085
|
|
|
4983
5086
|
// src/oauth/OAuthService.ts
|
|
@@ -5263,7 +5366,7 @@ var OAuthController = class OAuthController2 {
|
|
|
5263
5366
|
__decorate32([
|
|
5264
5367
|
Get5("/start"),
|
|
5265
5368
|
RateLimit2({ limit: 20, window: "15m", key: "ip" }),
|
|
5266
|
-
__param11(0,
|
|
5369
|
+
__param11(0, Ctx2()),
|
|
5267
5370
|
__param11(1, Query2("returnTo")),
|
|
5268
5371
|
__metadata32("design:type", Function),
|
|
5269
5372
|
__metadata32("design:paramtypes", [Object, String]),
|
|
@@ -5272,7 +5375,7 @@ __decorate32([
|
|
|
5272
5375
|
__decorate32([
|
|
5273
5376
|
Get5("/callback"),
|
|
5274
5377
|
RateLimit2({ limit: 20, window: "15m", key: callbackKey }),
|
|
5275
|
-
__param11(0,
|
|
5378
|
+
__param11(0, Ctx2()),
|
|
5276
5379
|
__param11(1, Query2("code")),
|
|
5277
5380
|
__param11(2, Query2("state")),
|
|
5278
5381
|
__param11(3, Query2("error")),
|
|
@@ -5327,9 +5430,9 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5327
5430
|
const clientId = google.clientId ?? process.env.GOOGLE_CLIENT_ID ?? "";
|
|
5328
5431
|
const clientSecret = google.clientSecret ?? process.env.GOOGLE_CLIENT_SECRET ?? "";
|
|
5329
5432
|
if (!clientId)
|
|
5330
|
-
throw
|
|
5433
|
+
throw Err11.configRequired("auth.oauth.google", "GOOGLE_CLIENT_ID");
|
|
5331
5434
|
if (!clientSecret)
|
|
5332
|
-
throw
|
|
5435
|
+
throw Err11.configRequired("auth.oauth.google", "GOOGLE_CLIENT_SECRET");
|
|
5333
5436
|
const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
|
|
5334
5437
|
const callbackUrl = google.callbackUrl ?? process.env.GOOGLE_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/google/callback`;
|
|
5335
5438
|
let callback;
|
|
@@ -5379,18 +5482,19 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
|
|
|
5379
5482
|
session: {
|
|
5380
5483
|
name: config?.session?.name ?? "najm.session",
|
|
5381
5484
|
maxAge: config?.session?.maxAge ?? 300,
|
|
5382
|
-
secret
|
|
5383
|
-
//
|
|
5485
|
+
// Keep Edge/server readers aligned with the documented secret order.
|
|
5486
|
+
// CookieManager falls back to jwt.accessSecret when this is undefined.
|
|
5487
|
+
secret: config?.session?.secret ?? process.env.NAJM_SESSION_SECRET
|
|
5384
5488
|
},
|
|
5385
5489
|
oauth: {
|
|
5386
5490
|
google: resolveGoogleConfig(config)
|
|
5387
5491
|
}
|
|
5388
5492
|
};
|
|
5389
5493
|
if (!finalConfig.jwt.accessSecret) {
|
|
5390
|
-
throw
|
|
5494
|
+
throw Err11.configRequired("auth", "JWT_ACCESS_SECRET");
|
|
5391
5495
|
}
|
|
5392
5496
|
if (!finalConfig.jwt.refreshSecret) {
|
|
5393
|
-
throw
|
|
5497
|
+
throw Err11.configRequired("auth", "JWT_REFRESH_SECRET");
|
|
5394
5498
|
}
|
|
5395
5499
|
return finalConfig;
|
|
5396
5500
|
}, "resolveAuthConfig");
|