@discover-cloud/shared 1.2.6 → 1.2.7
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.
|
@@ -12,8 +12,9 @@ class MachineTokenClient {
|
|
|
12
12
|
this.fetchPromise = null;
|
|
13
13
|
}
|
|
14
14
|
async getToken(requestId) {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const cachedToken = this.getValidCachedToken();
|
|
16
|
+
if (cachedToken) {
|
|
17
|
+
return cachedToken.token;
|
|
17
18
|
}
|
|
18
19
|
if (!this.fetchPromise) {
|
|
19
20
|
this.fetchPromise = this.fetchToken(requestId).finally(() => {
|
|
@@ -22,11 +23,15 @@ class MachineTokenClient {
|
|
|
22
23
|
}
|
|
23
24
|
return this.fetchPromise;
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
if (!this.cache)
|
|
27
|
-
return
|
|
26
|
+
getValidCachedToken() {
|
|
27
|
+
if (!this.cache) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
28
30
|
const nowS = Math.floor(Date.now() / 1000);
|
|
29
|
-
|
|
31
|
+
if (this.cache.expiresAt - REFRESH_BUFFER_S <= nowS) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return this.cache;
|
|
30
35
|
}
|
|
31
36
|
async fetchToken(requestId) {
|
|
32
37
|
const url = `${this.gatewayUrl}/internal/machine-token`;
|
|
@@ -36,7 +41,10 @@ class MachineTokenClient {
|
|
|
36
41
|
headers: {
|
|
37
42
|
"content-type": "application/json",
|
|
38
43
|
},
|
|
39
|
-
body: JSON.stringify({
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
serviceId: this.serviceId,
|
|
46
|
+
requestId,
|
|
47
|
+
}),
|
|
40
48
|
});
|
|
41
49
|
if (!response.ok) {
|
|
42
50
|
const text = await response.text().catch(() => "(unreadable)");
|
|
@@ -48,7 +56,10 @@ class MachineTokenClient {
|
|
|
48
56
|
token: body.data.token,
|
|
49
57
|
expiresAt: nowS + body.data.expiresIn,
|
|
50
58
|
};
|
|
51
|
-
this.logger.info({
|
|
59
|
+
this.logger.info({
|
|
60
|
+
serviceId: this.serviceId,
|
|
61
|
+
expiresAt: this.cache.expiresAt,
|
|
62
|
+
}, "Machine token cached");
|
|
52
63
|
return this.cache.token;
|
|
53
64
|
}
|
|
54
65
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { AccountRole } from "../enums";
|
|
3
|
-
import { GlobalPermission } from "../enums";
|
|
2
|
+
import { AccountRole, GlobalPermission } from "../enums";
|
|
4
3
|
import { ILogger } from "../utils";
|
|
5
4
|
import { InternalJwtVerifier } from "../jwt/internal-jwt-verifier";
|
|
6
5
|
/**
|
|
@@ -4,7 +4,6 @@ exports.RequireAuthMiddleware = void 0;
|
|
|
4
4
|
const jose_1 = require("jose");
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const internal_jwt_verifier_1 = require("../jwt/internal-jwt-verifier");
|
|
7
|
-
const utils_2 = require("../utils");
|
|
8
7
|
/**
|
|
9
8
|
* REQUIRE AUTH MIDDLEWARE (@discover-cloud/shared)
|
|
10
9
|
* ─────────────────────────────────────────────────────────────────────────
|
|
@@ -53,7 +52,7 @@ class RequireAuthMiddleware {
|
|
|
53
52
|
this.handle = async (req, res, next) => {
|
|
54
53
|
const token = this.extractBearer(req);
|
|
55
54
|
if (!token) {
|
|
56
|
-
(0,
|
|
55
|
+
(0, utils_1.failure)(res, req, "Missing Authorization header", "UNAUTHORIZED", 401);
|
|
57
56
|
return;
|
|
58
57
|
}
|
|
59
58
|
try {
|
|
@@ -83,11 +82,11 @@ class RequireAuthMiddleware {
|
|
|
83
82
|
const ctx = req.accessContext;
|
|
84
83
|
if (!ctx) {
|
|
85
84
|
// Should not happen after handle() — indicates middleware ordering bug
|
|
86
|
-
(0,
|
|
85
|
+
(0, utils_1.failure)(res, req, "No access context — ensure requireAuth runs first", "UNAUTHORIZED", 401);
|
|
87
86
|
return;
|
|
88
87
|
}
|
|
89
88
|
if (!internal_jwt_verifier_1.InternalJwtVerifier.hasPermission(ctx, permission)) {
|
|
90
|
-
(0,
|
|
89
|
+
(0, utils_1.failure)(res, req, `Missing required permission: ${permission}`, "FORBIDDEN", 403);
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
92
|
next();
|
|
@@ -101,11 +100,11 @@ class RequireAuthMiddleware {
|
|
|
101
100
|
this.requireRole = (role) => (req, res, next) => {
|
|
102
101
|
const ctx = req.accessContext;
|
|
103
102
|
if (!ctx) {
|
|
104
|
-
(0,
|
|
103
|
+
(0, utils_1.failure)(res, req, "No access context — ensure requireAuth runs first", "UNAUTHORIZED", 401);
|
|
105
104
|
return;
|
|
106
105
|
}
|
|
107
106
|
if (!internal_jwt_verifier_1.InternalJwtVerifier.hasRole(ctx, role)) {
|
|
108
|
-
(0,
|
|
107
|
+
(0, utils_1.failure)(res, req, `Missing required role: ${role}`, "FORBIDDEN", 403);
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
110
|
next();
|
|
@@ -145,19 +144,19 @@ class RequireAuthMiddleware {
|
|
|
145
144
|
*/
|
|
146
145
|
handleJwtError(err, req, res) {
|
|
147
146
|
if (err instanceof jose_1.errors.JWTExpired) {
|
|
148
|
-
(0,
|
|
147
|
+
(0, utils_1.failure)(res, req, "Your session has expired. Please log in again.", "TOKEN_EXPIRED", 401);
|
|
149
148
|
return;
|
|
150
149
|
}
|
|
151
150
|
if (err instanceof jose_1.errors.JWTClaimValidationFailed ||
|
|
152
151
|
err instanceof jose_1.errors.JWSSignatureVerificationFailed ||
|
|
153
152
|
err instanceof jose_1.errors.JWSInvalid ||
|
|
154
153
|
err instanceof jose_1.errors.JWTInvalid) {
|
|
155
|
-
(0,
|
|
154
|
+
(0, utils_1.failure)(res, req, "Token is invalid or has been tampered with.", "INVALID_TOKEN", 401);
|
|
156
155
|
return;
|
|
157
156
|
}
|
|
158
157
|
// Unexpected error — log for investigation, return a safe 503
|
|
159
158
|
this.logger.error({ err, requestId: req.id }, "RequireAuthMiddleware: unexpected error during JWT verification");
|
|
160
|
-
(0,
|
|
159
|
+
(0, utils_1.failure)(res, req, "Authentication service is temporarily unavailable.", "SERVICE_UNAVAILABLE", 503);
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
exports.RequireAuthMiddleware = RequireAuthMiddleware;
|