@getstrata/core 2.2.29 → 2.2.31
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/CHANGELOG.md +8 -0
- package/dist/core/auth/guard.d.ts +1 -0
- package/dist/entries/auth/jwt.js +3 -3
- package/dist/entries/auth/jwtGuard.js +3 -3
- package/dist/index.js +24 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.31
|
|
4
|
+
|
|
5
|
+
Preserve JWT issuance precision across millisecond revocation cutoffs without changing expiration. Generated MFA setup rejects stale concurrent state changes and atomically commits credential revocation in SQLite/non-RLS as well as Postgres RLS. Adds real HTTP initial/replacement races, revocation rollback and withheld-success recovery regressions; intentional fresh replacement remains supported.
|
|
6
|
+
|
|
7
|
+
## 2.2.30
|
|
8
|
+
|
|
9
|
+
Basic authentication resolution is reused only within the same Request and unchanged credentials, preventing middleware/controller lookups from consuming MFA recovery proofs twice. Adds real HTTP replay/concurrency and credential/guard invalidation regressions.
|
|
10
|
+
|
|
3
11
|
## 2.2.29
|
|
4
12
|
|
|
5
13
|
MFA recovery-code persistence atomically claims the validated snapshot and rejects stale requests before credential issuance. Basic authentication fails closed and consumes under the verified RLS identifier scope. Adds native SQLite/Postgres/MySQL concurrency and restricted-role regressions.
|
|
@@ -26,6 +26,7 @@ declare class CompositeGuard implements AuthGuard {
|
|
|
26
26
|
}
|
|
27
27
|
declare class AuthManager {
|
|
28
28
|
private readonly guard;
|
|
29
|
+
private basicResolutions;
|
|
29
30
|
private readonly namedGuards;
|
|
30
31
|
constructor(guard: AuthGuard);
|
|
31
32
|
registerGuard(name: string, next: AuthGuard): this;
|
package/dist/entries/auth/jwt.js
CHANGED
|
@@ -124,11 +124,11 @@ function signaturesMatch(left, right) {
|
|
|
124
124
|
return timingSafeEqual(leftBuffer, rightBuffer);
|
|
125
125
|
}
|
|
126
126
|
function signJwt(payload, options = {}) {
|
|
127
|
-
const
|
|
127
|
+
const issuedAt = Date.now() / 1000;
|
|
128
128
|
const body = {
|
|
129
129
|
...payload,
|
|
130
|
-
iat:
|
|
131
|
-
exp:
|
|
130
|
+
iat: issuedAt,
|
|
131
|
+
exp: Math.floor(issuedAt) + jwtTtlSeconds(options.ttlSeconds)
|
|
132
132
|
};
|
|
133
133
|
const header = encodeJson({ alg: "HS256", typ: "JWT" });
|
|
134
134
|
const data = encodeJson(body);
|
|
@@ -292,11 +292,11 @@ function signaturesMatch(left, right) {
|
|
|
292
292
|
return timingSafeEqual(leftBuffer, rightBuffer);
|
|
293
293
|
}
|
|
294
294
|
function signJwt(payload, options = {}) {
|
|
295
|
-
const
|
|
295
|
+
const issuedAt = Date.now() / 1000;
|
|
296
296
|
const body = {
|
|
297
297
|
...payload,
|
|
298
|
-
iat:
|
|
299
|
-
exp:
|
|
298
|
+
iat: issuedAt,
|
|
299
|
+
exp: Math.floor(issuedAt) + jwtTtlSeconds(options.ttlSeconds)
|
|
300
300
|
};
|
|
301
301
|
const header = encodeJson({ alg: "HS256", typ: "JWT" });
|
|
302
302
|
const data = encodeJson(body);
|
package/dist/index.js
CHANGED
|
@@ -1920,11 +1920,11 @@ function signaturesMatch(left, right) {
|
|
|
1920
1920
|
return timingSafeEqual2(leftBuffer, rightBuffer);
|
|
1921
1921
|
}
|
|
1922
1922
|
function signJwt(payload, options = {}) {
|
|
1923
|
-
const
|
|
1923
|
+
const issuedAt = Date.now() / 1000;
|
|
1924
1924
|
const body = {
|
|
1925
1925
|
...payload,
|
|
1926
|
-
iat:
|
|
1927
|
-
exp:
|
|
1926
|
+
iat: issuedAt,
|
|
1927
|
+
exp: Math.floor(issuedAt) + jwtTtlSeconds(options.ttlSeconds)
|
|
1928
1928
|
};
|
|
1929
1929
|
const header = encodeJson({ alg: "HS256", typ: "JWT" });
|
|
1930
1930
|
const data = encodeJson(body);
|
|
@@ -2117,6 +2117,7 @@ var SESSION_GUARD_NAMES = ["web", "session", "default"];
|
|
|
2117
2117
|
|
|
2118
2118
|
class AuthManager {
|
|
2119
2119
|
guard;
|
|
2120
|
+
basicResolutions = new WeakMap;
|
|
2120
2121
|
namedGuards = new Map;
|
|
2121
2122
|
constructor(guard) {
|
|
2122
2123
|
this.guard = guard;
|
|
@@ -2128,6 +2129,7 @@ class AuthManager {
|
|
|
2128
2129
|
throw new Error("Auth guard name must not be empty.");
|
|
2129
2130
|
}
|
|
2130
2131
|
this.namedGuards.set(trimmed, next);
|
|
2132
|
+
this.basicResolutions = new WeakMap;
|
|
2131
2133
|
return this;
|
|
2132
2134
|
}
|
|
2133
2135
|
use(name = "default") {
|
|
@@ -2148,7 +2150,25 @@ class AuthManager {
|
|
|
2148
2150
|
if (!request) {
|
|
2149
2151
|
return { user: currentAuthUser(), credentialSource: null };
|
|
2150
2152
|
}
|
|
2151
|
-
|
|
2153
|
+
if (authorizationScheme(request) !== "basic") {
|
|
2154
|
+
this.basicResolutions.delete(request);
|
|
2155
|
+
return await this.authenticateRequest(request);
|
|
2156
|
+
}
|
|
2157
|
+
const authorization = request.headers.get("authorization");
|
|
2158
|
+
const mfaCode = request.headers.get("x-mfa-code");
|
|
2159
|
+
const cached = this.basicResolutions.get(request);
|
|
2160
|
+
if (cached && cached.authorization === authorization && cached.mfaCode === mfaCode) {
|
|
2161
|
+
return await cached.result;
|
|
2162
|
+
}
|
|
2163
|
+
const entry = { authorization, mfaCode, result: this.authenticateRequest(request) };
|
|
2164
|
+
this.basicResolutions.set(request, entry);
|
|
2165
|
+
try {
|
|
2166
|
+
return await entry.result;
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
if (this.basicResolutions.get(request) === entry)
|
|
2169
|
+
this.basicResolutions.delete(request);
|
|
2170
|
+
throw error;
|
|
2171
|
+
}
|
|
2152
2172
|
}
|
|
2153
2173
|
user(request) {
|
|
2154
2174
|
return this.resolve(request);
|