najm-auth 3.3.0 → 3.3.1
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/index.d.ts +8 -5
- package/dist/index.js +33 -22
- package/package.json +6 -6
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>;
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-auth",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.1",
|
|
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",
|