najm-auth 3.3.0 → 3.3.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/{NajmAuthClient-ZtXTIUSF.d.ts → NajmAuthClient-BBl-GRis.d.ts} +1 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +6 -2
- package/dist/client/react/index.d.ts +1 -1
- package/dist/client/server/index.d.ts +1 -1
- package/dist/client/server/index.js +6 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +41 -27
- package/package.json +6 -6
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { F as FetchClient, H as HydrateSession, N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-
|
|
1
|
+
export { F as FetchClient, H as HydrateSession, N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-BBl-GRis.js';
|
|
2
2
|
import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../types-BaSfgxqE.js';
|
|
3
3
|
export { b as AuthClientConfig, h as AuthError, f as AuthEvent, g as AuthEventHandler, i as AuthEventMap, e as AuthState, A as AuthUser, j as AuthenticatedLogin, C as CredentialSetupPending, L as LoginCredentials, c as LoginResult, d as OAuthLoginOptions, O as OAuthProvider, a as RequestOptions, R as RetryConfig, k as ServerResponse, l as TokenPair } from '../types-BaSfgxqE.js';
|
|
4
4
|
|
package/dist/client/index.js
CHANGED
|
@@ -364,7 +364,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
364
364
|
this.emit("logout", null);
|
|
365
365
|
try {
|
|
366
366
|
await pendingRefresh?.catch(() => void 0);
|
|
367
|
-
await this.
|
|
367
|
+
await this.requestServerLogout();
|
|
368
368
|
} catch (err) {
|
|
369
369
|
this.emit("logoutError", err);
|
|
370
370
|
}
|
|
@@ -549,6 +549,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
549
549
|
if (generation !== this.authGeneration) return;
|
|
550
550
|
const shouldOpenCircuit = this.registerRefreshFailure(err);
|
|
551
551
|
if (shouldOpenCircuit) {
|
|
552
|
+
await this.requestServerLogout().catch(() => void 0);
|
|
552
553
|
this.resetState();
|
|
553
554
|
this.emit("sessionExpired", null);
|
|
554
555
|
if (err instanceof AuthError && err.status === 401) {
|
|
@@ -578,6 +579,9 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
578
579
|
return null;
|
|
579
580
|
}
|
|
580
581
|
}
|
|
582
|
+
async requestServerLogout() {
|
|
583
|
+
await this.api.post(`${this.prefix}/logout`, { skipAuth: true });
|
|
584
|
+
}
|
|
581
585
|
applyTokens(tokens) {
|
|
582
586
|
this.resetRefreshFailures();
|
|
583
587
|
const decoded = decodeToken(tokens.accessToken);
|
|
@@ -618,7 +622,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
618
622
|
this.clearRefreshCircuitTimer();
|
|
619
623
|
}
|
|
620
624
|
registerRefreshFailure(err) {
|
|
621
|
-
if (err instanceof AuthError && err.status
|
|
625
|
+
if (err instanceof AuthError && [401, 403, 429].includes(err.status)) {
|
|
622
626
|
this.refreshFailures = _NajmAuthClient.MAX_REFRESH_FAILURES;
|
|
623
627
|
} else {
|
|
624
628
|
this.refreshFailures += 1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, CSSProperties, ReactElement } from 'react';
|
|
3
|
-
import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-
|
|
3
|
+
import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-BBl-GRis.js';
|
|
4
4
|
import { e as AuthState, A as AuthUser, c as LoginResult, h as AuthError, L as LoginCredentials, d as OAuthLoginOptions, f as AuthEvent, i as AuthEventMap } from '../../types-BaSfgxqE.js';
|
|
5
5
|
|
|
6
6
|
interface AuthProviderProps {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { A as AuthUser, R as RetryConfig } from '../../types-BaSfgxqE.js';
|
|
2
|
-
import { F as FetchClient, N as NajmAuthClient } from '../../NajmAuthClient-
|
|
2
|
+
import { F as FetchClient, N as NajmAuthClient } from '../../NajmAuthClient-BBl-GRis.js';
|
|
3
3
|
import { ProxySessionMode } from '../edge.js';
|
|
4
4
|
export { withAuthMiddleware } from '../edge.js';
|
|
5
5
|
import { G as GetSessionConfig, S as ServerSession$1 } from '../../getSession-BthP85UA.js';
|
|
@@ -1075,7 +1075,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
1075
1075
|
this.emit("logout", null);
|
|
1076
1076
|
try {
|
|
1077
1077
|
await pendingRefresh?.catch(() => void 0);
|
|
1078
|
-
await this.
|
|
1078
|
+
await this.requestServerLogout();
|
|
1079
1079
|
} catch (err) {
|
|
1080
1080
|
this.emit("logoutError", err);
|
|
1081
1081
|
}
|
|
@@ -1260,6 +1260,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
1260
1260
|
if (generation !== this.authGeneration) return;
|
|
1261
1261
|
const shouldOpenCircuit = this.registerRefreshFailure(err);
|
|
1262
1262
|
if (shouldOpenCircuit) {
|
|
1263
|
+
await this.requestServerLogout().catch(() => void 0);
|
|
1263
1264
|
this.resetState();
|
|
1264
1265
|
this.emit("sessionExpired", null);
|
|
1265
1266
|
if (err instanceof AuthError && err.status === 401) {
|
|
@@ -1289,6 +1290,9 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
1289
1290
|
return null;
|
|
1290
1291
|
}
|
|
1291
1292
|
}
|
|
1293
|
+
async requestServerLogout() {
|
|
1294
|
+
await this.api.post(`${this.prefix}/logout`, { skipAuth: true });
|
|
1295
|
+
}
|
|
1292
1296
|
applyTokens(tokens) {
|
|
1293
1297
|
this.resetRefreshFailures();
|
|
1294
1298
|
const decoded = decodeToken(tokens.accessToken);
|
|
@@ -1329,7 +1333,7 @@ var NajmAuthClient = class _NajmAuthClient {
|
|
|
1329
1333
|
this.clearRefreshCircuitTimer();
|
|
1330
1334
|
}
|
|
1331
1335
|
registerRefreshFailure(err) {
|
|
1332
|
-
if (err instanceof AuthError && err.status
|
|
1336
|
+
if (err instanceof AuthError && [401, 403, 429].includes(err.status)) {
|
|
1333
1337
|
this.refreshFailures = _NajmAuthClient.MAX_REFRESH_FAILURES;
|
|
1334
1338
|
} else {
|
|
1335
1339
|
this.refreshFailures += 1;
|
package/dist/index.d.ts
CHANGED
|
@@ -1084,16 +1084,19 @@ declare class TokenService {
|
|
|
1084
1084
|
tokenFamily: string;
|
|
1085
1085
|
};
|
|
1086
1086
|
private static readonly PREVIOUS_GRACE_SECONDS;
|
|
1087
|
+
private clearRefreshSessionCookies;
|
|
1088
|
+
private rejectRefreshSession;
|
|
1089
|
+
private readRefreshSessionCookie;
|
|
1087
1090
|
/**
|
|
1088
1091
|
* Read the refresh cookie and return the userId it belongs to.
|
|
1089
1092
|
* Validates against current/previous token state with bounded recovery.
|
|
1090
1093
|
* Throws if the cookie is missing, invalid, or outside the grace window.
|
|
1091
1094
|
*
|
|
1092
|
-
* Intentionally
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1095
|
+
* Intentionally does not revoke on a mismatch: unlike refreshTokens(), this
|
|
1096
|
+
* is a read path (e.g. GET /auth/me), so a stray stale cookie must not be
|
|
1097
|
+
* able to destroy server-side session state. Definitively invalid browser
|
|
1098
|
+
* cookies are still cleared so a signed snapshot cannot cause a redirect
|
|
1099
|
+
* loop.
|
|
1097
1100
|
*/
|
|
1098
1101
|
private resolveRefreshSessionFromCookie;
|
|
1099
1102
|
resolveUserFromCookie(): Promise<string>;
|
|
@@ -1623,7 +1626,7 @@ declare class AuthService {
|
|
|
1623
1626
|
recoverSession(): Promise<{
|
|
1624
1627
|
recovered: true;
|
|
1625
1628
|
}>;
|
|
1626
|
-
logoutUser(userId: string, authorization?: string): Promise<{
|
|
1629
|
+
logoutUser(userId: string | undefined, authorization?: string): Promise<{
|
|
1627
1630
|
data: any;
|
|
1628
1631
|
message: string;
|
|
1629
1632
|
}>;
|
|
@@ -1700,7 +1703,7 @@ declare class AuthController {
|
|
|
1700
1703
|
recoverSession(recoveryRequest: string | undefined, ctx: Context): Promise<{
|
|
1701
1704
|
recovered: true;
|
|
1702
1705
|
}>;
|
|
1703
|
-
logoutUser(userId: string, authorization?: string): Promise<{
|
|
1706
|
+
logoutUser(userId: string | undefined, authorization?: string): Promise<{
|
|
1704
1707
|
data: any;
|
|
1705
1708
|
message: string;
|
|
1706
1709
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -1914,26 +1914,42 @@ var TokenService = class TokenService2 {
|
|
|
1914
1914
|
return { userId: decoded.userId, tokenFamily: decoded.tokenFamily };
|
|
1915
1915
|
}
|
|
1916
1916
|
static PREVIOUS_GRACE_SECONDS = 120;
|
|
1917
|
+
clearRefreshSessionCookies() {
|
|
1918
|
+
this.cookieManager.clearRefreshToken();
|
|
1919
|
+
this.cookieManager.clearSessionCookie();
|
|
1920
|
+
}
|
|
1921
|
+
rejectRefreshSession(messageKey = "errors.refreshTokenInvalid") {
|
|
1922
|
+
this.clearRefreshSessionCookies();
|
|
1923
|
+
Err7(this.t(messageKey), 401);
|
|
1924
|
+
}
|
|
1925
|
+
readRefreshSessionCookie() {
|
|
1926
|
+
const refreshToken = this.cookieManager.getRefreshToken();
|
|
1927
|
+
if (!refreshToken) {
|
|
1928
|
+
this.rejectRefreshSession("errors.refreshTokenMissing");
|
|
1929
|
+
}
|
|
1930
|
+
try {
|
|
1931
|
+
return { refreshToken, ...this.verifyRefreshToken(refreshToken) };
|
|
1932
|
+
} catch (error) {
|
|
1933
|
+
this.clearRefreshSessionCookies();
|
|
1934
|
+
throw error;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1917
1937
|
/**
|
|
1918
1938
|
* Read the refresh cookie and return the userId it belongs to.
|
|
1919
1939
|
* Validates against current/previous token state with bounded recovery.
|
|
1920
1940
|
* Throws if the cookie is missing, invalid, or outside the grace window.
|
|
1921
1941
|
*
|
|
1922
|
-
* Intentionally
|
|
1923
|
-
*
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1942
|
+
* Intentionally does not revoke on a mismatch: unlike refreshTokens(), this
|
|
1943
|
+
* is a read path (e.g. GET /auth/me), so a stray stale cookie must not be
|
|
1944
|
+
* able to destroy server-side session state. Definitively invalid browser
|
|
1945
|
+
* cookies are still cleared so a signed snapshot cannot cause a redirect
|
|
1946
|
+
* loop.
|
|
1927
1947
|
*/
|
|
1928
1948
|
async resolveRefreshSessionFromCookie() {
|
|
1929
|
-
const refreshToken = this.
|
|
1930
|
-
if (!refreshToken) {
|
|
1931
|
-
Err7(this.t("errors.refreshTokenMissing"), 401);
|
|
1932
|
-
}
|
|
1933
|
-
const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
|
|
1949
|
+
const { refreshToken, userId, tokenFamily } = this.readRefreshSessionCookie();
|
|
1934
1950
|
const stored = await this.tokenRepository.getByFamily(tokenFamily);
|
|
1935
1951
|
if (!stored || stored.userId !== userId) {
|
|
1936
|
-
|
|
1952
|
+
this.rejectRefreshSession();
|
|
1937
1953
|
}
|
|
1938
1954
|
const presentedHash = this.hashToken(refreshToken);
|
|
1939
1955
|
if (presentedHash === stored.token) {
|
|
@@ -1943,7 +1959,7 @@ var TokenService = class TokenService2 {
|
|
|
1943
1959
|
if (canRecover) {
|
|
1944
1960
|
return { userId, tokenFamily };
|
|
1945
1961
|
}
|
|
1946
|
-
|
|
1962
|
+
this.rejectRefreshSession();
|
|
1947
1963
|
}
|
|
1948
1964
|
async resolveUserFromCookie() {
|
|
1949
1965
|
return (await this.resolveRefreshSessionFromCookie()).userId;
|
|
@@ -2143,14 +2159,10 @@ var TokenService = class TokenService2 {
|
|
|
2143
2159
|
* Compares provided token with hashed version in database
|
|
2144
2160
|
*/
|
|
2145
2161
|
async refreshTokens() {
|
|
2146
|
-
const refreshToken = this.
|
|
2147
|
-
if (!refreshToken) {
|
|
2148
|
-
Err7(this.t("errors.refreshTokenMissing"), 401);
|
|
2149
|
-
}
|
|
2150
|
-
const { userId, tokenFamily } = this.verifyRefreshToken(refreshToken);
|
|
2162
|
+
const { refreshToken, userId, tokenFamily } = this.readRefreshSessionCookie();
|
|
2151
2163
|
const stored = await this.tokenRepository.getByFamily(tokenFamily);
|
|
2152
2164
|
if (!stored || stored.userId !== userId) {
|
|
2153
|
-
|
|
2165
|
+
this.rejectRefreshSession();
|
|
2154
2166
|
}
|
|
2155
2167
|
const presentedHash = this.hashToken(refreshToken);
|
|
2156
2168
|
await this.requireActiveRefreshUser(userId, tokenFamily);
|
|
@@ -2166,19 +2178,18 @@ var TokenService = class TokenService2 {
|
|
|
2166
2178
|
return this.rotateTokens(userId, tokenFamily, stored.token);
|
|
2167
2179
|
}
|
|
2168
2180
|
await this.revokeSuspectRefreshFamily(userId, tokenFamily);
|
|
2169
|
-
|
|
2181
|
+
this.rejectRefreshSession();
|
|
2170
2182
|
}
|
|
2171
2183
|
async requireActiveRefreshUser(userId, tokenFamily) {
|
|
2172
2184
|
const user = await this.tokenRepository.getUser(userId);
|
|
2173
2185
|
if (!user || user.status !== "active") {
|
|
2174
2186
|
await this.revokeFamily(tokenFamily);
|
|
2175
|
-
|
|
2187
|
+
this.rejectRefreshSession();
|
|
2176
2188
|
}
|
|
2177
2189
|
const requirement = await this.credentialSetupRequirements?.find(userId, PASSWORD_SETUP_PURPOSE);
|
|
2178
2190
|
if (requirement?.required) {
|
|
2179
2191
|
await this.revokeFamily(tokenFamily);
|
|
2180
|
-
this.
|
|
2181
|
-
this.cookieManager.clearSessionCookie();
|
|
2192
|
+
this.clearRefreshSessionCookies();
|
|
2182
2193
|
Err7(this.t("errors.credentialSetupRequired"), 403);
|
|
2183
2194
|
}
|
|
2184
2195
|
return user;
|
|
@@ -3350,9 +3361,14 @@ var AuthService = class AuthService2 {
|
|
|
3350
3361
|
return { recovered: true };
|
|
3351
3362
|
}
|
|
3352
3363
|
async logoutUser(userId, authorization) {
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3364
|
+
try {
|
|
3365
|
+
if (userId) {
|
|
3366
|
+
await this.tokenService.logout(userId, authorization);
|
|
3367
|
+
}
|
|
3368
|
+
} finally {
|
|
3369
|
+
this.cookieManager.clearRefreshToken();
|
|
3370
|
+
this.cookieManager.clearSessionCookie();
|
|
3371
|
+
}
|
|
3356
3372
|
return { data: null, message: this.t("auth.success.logout") };
|
|
3357
3373
|
}
|
|
3358
3374
|
/**
|
|
@@ -3846,8 +3862,6 @@ __decorate20([
|
|
|
3846
3862
|
], AuthController.prototype, "recoverSession", null);
|
|
3847
3863
|
__decorate20([
|
|
3848
3864
|
Post("/logout"),
|
|
3849
|
-
isAuth(),
|
|
3850
|
-
RateLimit({ limit: 10, window: "15m", key: "user" }),
|
|
3851
3865
|
__param5(0, User2("id")),
|
|
3852
3866
|
__param5(1, Headers("authorization")),
|
|
3853
3867
|
__metadata20("design:type", Function),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-auth",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "Authentication and authorization library for najm framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -91,13 +91,13 @@
|
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"bcryptjs": "^3.0.2",
|
|
93
93
|
"najm-cookies": "^2.0.2",
|
|
94
|
-
"najm-core": "^2.0.
|
|
95
|
-
"najm-database": "^2.0.
|
|
94
|
+
"najm-core": "^2.0.6",
|
|
95
|
+
"najm-database": "^2.0.4",
|
|
96
96
|
"najm-guard": "^2.0.2",
|
|
97
|
-
"najm-i18n": "^2.
|
|
98
|
-
"najm-cache": "^2.1.
|
|
97
|
+
"najm-i18n": "^2.1.1",
|
|
98
|
+
"najm-cache": "^2.1.2",
|
|
99
99
|
"najm-email": "^2.0.2",
|
|
100
|
-
"najm-rate": "^2.1.
|
|
100
|
+
"najm-rate": "^2.1.1",
|
|
101
101
|
"najm-validation": "^2.0.2",
|
|
102
102
|
"jsonwebtoken": "^9.0.3",
|
|
103
103
|
"jose": "^6.1.3",
|