@nsshunt/stsauthclient 1.0.41 → 1.0.43
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/stsauthclient.mjs +297 -317
- package/dist/stsauthclient.mjs.map +1 -1
- package/dist/stsauthclient.umd.js +299 -319
- package/dist/stsauthclient.umd.js.map +1 -1
- package/package.json +7 -7
- package/types/authutilsnode.d.ts +3 -3
- package/types/authutilsnode.d.ts.map +1 -1
package/dist/stsauthclient.mjs
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __typeError = (msg) => {
|
|
3
|
-
throw TypeError(msg);
|
|
4
|
-
};
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
8
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
9
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
10
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
11
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
12
|
-
var _options, _options2, _cache, _cacheTimeout, _cookiejar, _originRegex, _AuthUtilsNode_instances, LogDebugMessage_fn;
|
|
13
1
|
import axios from "axios";
|
|
14
2
|
import { STSAxiosConfig, GetErrorPayload } from "@nsshunt/stsutils";
|
|
15
|
-
import
|
|
3
|
+
import { CookieJar, Cookie } from "tough-cookie";
|
|
16
4
|
import jwt from "jsonwebtoken";
|
|
17
5
|
import { jwtDecode } from "jwt-decode";
|
|
18
6
|
import jwksClient from "jwks-rsa";
|
|
@@ -88,333 +76,165 @@ var StatusCodes;
|
|
|
88
76
|
StatusCodes2[StatusCodes2["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
|
|
89
77
|
})(StatusCodes || (StatusCodes = {}));
|
|
90
78
|
class ResourceManager {
|
|
79
|
+
#options;
|
|
91
80
|
constructor(options) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
__publicField(this, "GetErrorMessage", (status, error, detail) => {
|
|
95
|
-
return {
|
|
96
|
-
status,
|
|
97
|
-
error,
|
|
98
|
-
detail
|
|
99
|
-
};
|
|
100
|
-
});
|
|
101
|
-
__publicField(this, "GetHeaders", (access_token) => {
|
|
102
|
-
const headers = {
|
|
103
|
-
"Content-Type": "application/json"
|
|
104
|
-
};
|
|
105
|
-
if (access_token) {
|
|
106
|
-
headers["Authorization"] = `Bearer ${access_token}`;
|
|
107
|
-
}
|
|
108
|
-
return headers;
|
|
109
|
-
});
|
|
110
|
-
__publicField(this, "GetResult", async (accessToken, url, method, requestData, errorCb) => {
|
|
111
|
-
const axiosConfig = new STSAxiosConfig(url, method, this.GetHeaders(accessToken), this.options.timeout);
|
|
112
|
-
if (__privateGet(this, _options).agentManager) {
|
|
113
|
-
axiosConfig.withAgentManager(__privateGet(this, _options).agentManager);
|
|
114
|
-
}
|
|
115
|
-
if (requestData !== null) {
|
|
116
|
-
axiosConfig.withData(requestData);
|
|
117
|
-
}
|
|
118
|
-
const data = await axios(axiosConfig.config);
|
|
119
|
-
if (data.data.status === StatusCodes.OK || data.data.status === StatusCodes.CREATED) {
|
|
120
|
-
const sessionDataRaw = data.data.detail;
|
|
121
|
-
if (sessionDataRaw) {
|
|
122
|
-
try {
|
|
123
|
-
const sessionData = JSON.parse(sessionDataRaw);
|
|
124
|
-
return sessionData;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, "SessionManager:GetResult(): Could not parse session data.", error));
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, "SessionManager:GetResult(): No session data returned.", null));
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
const { status, error, detail } = data.data;
|
|
135
|
-
errorCb(this.GetErrorMessage(status, `SessionManager:GetResult(): Status not OK. Error: [${error}]`, detail));
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
__privateSet(this, _options, options);
|
|
140
|
-
this.LogDebugMessage(`STSOAuth2Worker:constructor:#options: [${JSON.stringify(__privateGet(this, _options))}]`);
|
|
81
|
+
this.#options = options;
|
|
82
|
+
this.LogDebugMessage(`STSOAuth2Worker:constructor:#options: [${JSON.stringify(this.#options)}]`);
|
|
141
83
|
}
|
|
142
84
|
get agentManager() {
|
|
143
|
-
if (
|
|
144
|
-
return
|
|
85
|
+
if (this.#options.agentManager) {
|
|
86
|
+
return this.#options.agentManager;
|
|
145
87
|
} else {
|
|
146
88
|
return null;
|
|
147
89
|
}
|
|
148
90
|
}
|
|
149
91
|
get options() {
|
|
150
|
-
return
|
|
92
|
+
return this.#options;
|
|
151
93
|
}
|
|
152
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
95
|
LogDebugMessage(message) {
|
|
154
|
-
|
|
96
|
+
this.#options.logger.debug(message);
|
|
155
97
|
}
|
|
156
98
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
157
99
|
LogInfoMessage(message) {
|
|
158
|
-
|
|
100
|
+
this.#options.logger.info(message);
|
|
159
101
|
}
|
|
160
102
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
103
|
LogErrorMessage(message) {
|
|
162
|
-
|
|
104
|
+
this.#options.logger.error(message);
|
|
163
105
|
}
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
GetErrorMessage = (status, error, detail) => {
|
|
108
|
+
return {
|
|
109
|
+
status,
|
|
110
|
+
error,
|
|
111
|
+
detail
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
GetHeaders = (access_token) => {
|
|
115
|
+
const headers = {
|
|
116
|
+
"Content-Type": "application/json"
|
|
117
|
+
};
|
|
118
|
+
if (access_token) {
|
|
119
|
+
headers["Authorization"] = `Bearer ${access_token}`;
|
|
120
|
+
}
|
|
121
|
+
return headers;
|
|
122
|
+
};
|
|
123
|
+
GetResult = async (accessToken, url, method, requestData, errorCb) => {
|
|
124
|
+
const axiosConfig = new STSAxiosConfig(url, method, this.GetHeaders(accessToken), this.options.timeout);
|
|
125
|
+
if (this.#options.agentManager) {
|
|
126
|
+
axiosConfig.withAgentManager(this.#options.agentManager);
|
|
127
|
+
}
|
|
128
|
+
if (requestData !== null) {
|
|
129
|
+
axiosConfig.withData(requestData);
|
|
130
|
+
}
|
|
131
|
+
const data = await axios(axiosConfig.config);
|
|
132
|
+
if (data.data.status === StatusCodes.OK || data.data.status === StatusCodes.CREATED) {
|
|
133
|
+
const sessionDataRaw = data.data.detail;
|
|
134
|
+
if (sessionDataRaw) {
|
|
135
|
+
try {
|
|
136
|
+
const sessionData = JSON.parse(sessionDataRaw);
|
|
137
|
+
return sessionData;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, "SessionManager:GetResult(): Could not parse session data.", error));
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, "SessionManager:GetResult(): No session data returned.", null));
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
const { status, error, detail } = data.data;
|
|
148
|
+
errorCb(this.GetErrorMessage(status, `SessionManager:GetResult(): Status not OK. Error: [${error}]`, detail));
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
164
152
|
}
|
|
165
|
-
_options = new WeakMap();
|
|
166
153
|
class SessionManager extends ResourceManager {
|
|
167
154
|
constructor(options) {
|
|
168
155
|
super(options);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
return this.GetResult(access_token, `${this.options.asendpoint}/session/${encodeURIComponent(session.sessionId)}`, "patch", session, errorCb);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, `SessionManager:GetSession(): Could not process session. Error: [${error}]`, error));
|
|
156
|
+
}
|
|
157
|
+
GetSession = async (access_token, sessionId, errorCb) => {
|
|
158
|
+
try {
|
|
159
|
+
return this.GetResult(access_token, `${this.options.asendpoint}/session/${encodeURIComponent(sessionId)}`, "get", null, errorCb);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, `SessionManager:GetSession(): Could not process session. Error: [${error}]`, error));
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
PatchSession = async (access_token, session, errorCb) => {
|
|
166
|
+
try {
|
|
167
|
+
if (!session.sessionId) {
|
|
168
|
+
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, "SessionManager:GetSession(): sessionId not provided.", null));
|
|
186
169
|
return null;
|
|
187
170
|
}
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
return this.GetResult(access_token, `${this.options.asendpoint}/session/${encodeURIComponent(session.sessionId)}`, "patch", session, errorCb);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
errorCb(this.GetErrorMessage(StatusCodes.INTERNAL_SERVER_ERROR, `SessionManager:GetSession(): Could not process session. Error: [${error}]`, error));
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
190
177
|
}
|
|
191
178
|
class AuthUtilsNode {
|
|
179
|
+
#options;
|
|
180
|
+
#cache = {};
|
|
181
|
+
#cacheTimeout = 1e3;
|
|
182
|
+
#cookiejar;
|
|
183
|
+
// Regular expression to match the origin
|
|
184
|
+
#originRegex = /^(api:\/\/\w+)/;
|
|
192
185
|
constructor(options) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
for (let i = 0; i < options.permissions.length; i++) {
|
|
217
|
-
const permission = options.permissions[i];
|
|
218
|
-
if (!scopes.includes(permission)) {
|
|
219
|
-
requiredPermissions.push(permission);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (requiredPermissions.length > 0) {
|
|
223
|
-
const errorPayload = GetErrorPayload(STSAuthClientErrorCode.STS_AC_MISSING_PERMISSION, requiredPermissions);
|
|
224
|
-
res.status(StatusCodes.UNAUTHORIZED).send({ status: StatusCodes.UNAUTHORIZED, error: errorPayload });
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
if (!__privateGet(this, _cache)[permissionsKey]) {
|
|
228
|
-
__privateGet(this, _cache)[permissionsKey] = {
|
|
229
|
-
scopes: {}
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
__privateGet(this, _cache)[permissionsKey].scopes[scopeKey] = {
|
|
233
|
-
scope: scopeKey,
|
|
234
|
-
timeout: setTimeout(() => {
|
|
235
|
-
delete __privateGet(this, _cache)[permissionsKey].scopes[scopeKey];
|
|
236
|
-
}, __privateGet(this, _cacheTimeout)).unref()
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
next();
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
243
|
-
__publicField(this, "SetCookiesToJar", async (headers, endpoint) => {
|
|
244
|
-
if (headers["set-cookie"]) {
|
|
245
|
-
headers["set-cookie"].map((headerCookie) => {
|
|
246
|
-
const cookie = tough.Cookie.parse(headerCookie);
|
|
247
|
-
__privateGet(this, _cookiejar).setCookieSync(cookie, endpoint);
|
|
248
|
-
});
|
|
249
|
-
} else {
|
|
250
|
-
const cookie = tough.Cookie.parse(headers["set-cookie"]);
|
|
251
|
-
__privateGet(this, _cookiejar).setCookieSync(cookie, endpoint);
|
|
252
|
-
}
|
|
253
|
-
return __privateGet(this, _cookiejar).getCookies(endpoint);
|
|
254
|
-
});
|
|
255
|
-
__publicField(this, "GetCookiesFromJar", async (endpoint) => {
|
|
256
|
-
return __privateGet(this, _cookiejar).getCookies(endpoint);
|
|
257
|
-
});
|
|
258
|
-
__publicField(this, "ValidateJWT", async (token, audience, endpoint) => {
|
|
259
|
-
const jwksClientUri = endpoint ? `${endpoint}${goptions.asoauthapiroot}${goptions.asjwksjsonpath}` : `${goptions.asendpoint}:${goptions.asport}${goptions.asoauthapiroot}${goptions.asjwksjsonpath}`;
|
|
260
|
-
const jwksClientOptions = {
|
|
261
|
-
cache: true,
|
|
262
|
-
//@@ all config items
|
|
263
|
-
cacheMaxEntries: 5,
|
|
264
|
-
// Default value
|
|
265
|
-
cacheMaxAge: 6e5,
|
|
266
|
-
// Defaults to 10m
|
|
267
|
-
rateLimit: true,
|
|
268
|
-
jwksRequestsPerMinute: 10,
|
|
269
|
-
// Default value
|
|
270
|
-
jwksUri: jwksClientUri,
|
|
271
|
-
timeout: 3e4
|
|
272
|
-
//@@ config
|
|
273
|
-
};
|
|
274
|
-
if (__privateGet(this, _options2).agentManager) {
|
|
275
|
-
jwksClientOptions.requestAgent = __privateGet(this, _options2).agentManager.GetAgent(jwksClientUri);
|
|
276
|
-
}
|
|
277
|
-
const jwks = jwksClient(jwksClientOptions);
|
|
278
|
-
const decodedRefreshToken = jwtDecode(token, { header: true });
|
|
279
|
-
const kid = decodedRefreshToken.kid;
|
|
280
|
-
const key = await jwks.getSigningKey(kid);
|
|
281
|
-
const signingKey = key.getPublicKey();
|
|
282
|
-
const verifyOptions = {
|
|
283
|
-
issuer: iss,
|
|
284
|
-
//subject: s,
|
|
285
|
-
audience,
|
|
286
|
-
//expiresIn: 600, // 10 minutes
|
|
287
|
-
algorithm: ["RS256"]
|
|
288
|
-
// RSASSA [ "RS256", "RS384", "RS512" ]
|
|
289
|
-
};
|
|
290
|
-
return jwt.verify(token, signingKey, verifyOptions);
|
|
291
|
-
});
|
|
292
|
-
// Function to extract the origin from a URI
|
|
293
|
-
__publicField(this, "ExtractOrigin", (uri) => {
|
|
294
|
-
const match = uri.match(__privateGet(this, _originRegex));
|
|
295
|
-
return match ? match[1] : null;
|
|
296
|
-
});
|
|
297
|
-
__publicField(this, "GetAPITokenFromAuthServerUsingScope", async (options, errorCb) => {
|
|
298
|
-
const { scope, clientId, authClientSecret, endPoint, instrumentController, outputErrorsToConsole } = options;
|
|
299
|
-
let stage = "1";
|
|
300
|
-
const invokeErrorCb = (error) => {
|
|
301
|
-
__privateMethod(this, _AuthUtilsNode_instances, LogDebugMessage_fn).call(this, error);
|
|
302
|
-
if (instrumentController) {
|
|
303
|
-
instrumentController.UpdateInstrument(Gauge.AUTHENTICATION_ERROR_COUNT_GAUGE, {
|
|
304
|
-
// auth error
|
|
305
|
-
Inc: 1
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
errorCb(error);
|
|
309
|
-
};
|
|
310
|
-
try {
|
|
311
|
-
stage = "2";
|
|
312
|
-
const scopes = scope.split(" ");
|
|
313
|
-
let origin = null;
|
|
314
|
-
let error = null;
|
|
315
|
-
stage = "3";
|
|
316
|
-
for (let i = 0; i < scopes.length; i++) {
|
|
317
|
-
const s = scopes[i];
|
|
318
|
-
if (!origin) {
|
|
319
|
-
origin = this.ExtractOrigin(s);
|
|
320
|
-
if (!origin) {
|
|
321
|
-
error = new Error(`Scope: [${scope}] not in required format. Must use (space seperated) api://<client id>[/<resource>.<permission>].`);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
const nextOrigin = this.ExtractOrigin(s);
|
|
326
|
-
if (!nextOrigin) {
|
|
327
|
-
error = new Error(`Scope: [${scope}] not in required format. Must use (space seperated) api://<client id>[/<resource>.<permission>].`);
|
|
328
|
-
break;
|
|
329
|
-
} else {
|
|
330
|
-
if (origin.localeCompare(nextOrigin) !== 0) {
|
|
331
|
-
error = new Error(`Scope: [${scope}] not all from the same client API. All scopes must come from the same client API.`);
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
stage = "4";
|
|
338
|
-
if (error) {
|
|
339
|
-
invokeErrorCb(error);
|
|
340
|
-
return "";
|
|
341
|
-
}
|
|
342
|
-
stage = "5";
|
|
343
|
-
const payload = {
|
|
344
|
-
//@@ make a type
|
|
345
|
-
client_id: clientId,
|
|
346
|
-
// The service calling this method
|
|
347
|
-
client_secret: authClientSecret,
|
|
348
|
-
// Auth service client secret
|
|
349
|
-
//client_secret: goptions.brokerclientsecret, // Broker service client secret
|
|
350
|
-
scope,
|
|
351
|
-
// required API
|
|
352
|
-
//@@ remove audience
|
|
353
|
-
//@@ need scope to be the API identifier
|
|
354
|
-
grant_type: "client_credentials"
|
|
355
|
-
};
|
|
356
|
-
stage = "6";
|
|
357
|
-
const url = endPoint ? `${endPoint}${goptions.asoauthapiroot}/token` : `${goptions.asendpoint}:${goptions.asport}${goptions.asoauthapiroot}/token`;
|
|
358
|
-
stage = `6.5: url: [${url}] payload: [${JSON.stringify(payload)}]`;
|
|
359
|
-
const axiosConfig = new STSAxiosConfig(url, "post").withDefaultHeaders().withData(payload);
|
|
360
|
-
if (__privateGet(this, _options2).agentManager) {
|
|
361
|
-
axiosConfig.withAgentManager(__privateGet(this, _options2).agentManager);
|
|
362
|
-
}
|
|
363
|
-
const retVal = await axios(axiosConfig.config);
|
|
364
|
-
stage = "7";
|
|
365
|
-
if (retVal.status) {
|
|
366
|
-
if (retVal.status !== 200) {
|
|
367
|
-
__privateMethod(this, _AuthUtilsNode_instances, LogDebugMessage_fn).call(this, chalk.magenta(`Error (AuthUtilsNode:GetAPITokenFromServer): Invalid response from server: [${retVal.status}]`));
|
|
368
|
-
}
|
|
369
|
-
} else {
|
|
370
|
-
invokeErrorCb(new Error(chalk.red(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.status)`)));
|
|
371
|
-
return "";
|
|
186
|
+
this.#options = options;
|
|
187
|
+
this.#cookiejar = new CookieJar();
|
|
188
|
+
}
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
+
#LogDebugMessage(message) {
|
|
191
|
+
this.#options.logger.debug(message);
|
|
192
|
+
}
|
|
193
|
+
get agentManager() {
|
|
194
|
+
return this.#options.agentManager;
|
|
195
|
+
}
|
|
196
|
+
ResetAgent = () => {
|
|
197
|
+
if (this.#options.agentManager) {
|
|
198
|
+
this.#options.agentManager.ResetAgent();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
VerifyRequestMiddlewareFactory = (options) => {
|
|
202
|
+
return async (req, res, next) => {
|
|
203
|
+
if (options.permissions) {
|
|
204
|
+
const permissionsKey = options.permissions.join("_");
|
|
205
|
+
const scopeKey = req.auth.scope.split(" ").join("_");
|
|
206
|
+
if (this.#cache[permissionsKey] && this.#cache[permissionsKey].scopes[scopeKey]) {
|
|
207
|
+
next();
|
|
208
|
+
return;
|
|
372
209
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
stage = "11";
|
|
380
|
-
instrumentController.UpdateInstrument(Gauge.AUTHENTICATION_COUNT_GAUGE, {
|
|
381
|
-
Inc: 1
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
stage = "12";
|
|
385
|
-
return retVal.data.access_token;
|
|
386
|
-
} else {
|
|
387
|
-
stage = "13";
|
|
388
|
-
invokeErrorCb(new Error(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.data.access_token)`));
|
|
389
|
-
return "";
|
|
210
|
+
const scopes = req.auth.scope.split(" ");
|
|
211
|
+
const requiredPermissions = [];
|
|
212
|
+
for (let i = 0; i < options.permissions.length; i++) {
|
|
213
|
+
const permission = options.permissions[i];
|
|
214
|
+
if (!scopes.includes(permission)) {
|
|
215
|
+
requiredPermissions.push(permission);
|
|
390
216
|
}
|
|
391
|
-
} else {
|
|
392
|
-
stage = "14";
|
|
393
|
-
invokeErrorCb(new Error(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.data)`));
|
|
394
|
-
return "";
|
|
395
217
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
218
|
+
if (requiredPermissions.length > 0) {
|
|
219
|
+
const errorPayload = GetErrorPayload(STSAuthClientErrorCode.STS_AC_MISSING_PERMISSION, requiredPermissions);
|
|
220
|
+
res.status(StatusCodes.UNAUTHORIZED).send({ status: StatusCodes.UNAUTHORIZED, error: errorPayload });
|
|
221
|
+
return;
|
|
399
222
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
} catch (error2) {
|
|
405
|
-
details = `Could not JSON.stringify(error.response.data)`;
|
|
406
|
-
}
|
|
223
|
+
if (!this.#cache[permissionsKey]) {
|
|
224
|
+
this.#cache[permissionsKey] = {
|
|
225
|
+
scopes: {}
|
|
226
|
+
};
|
|
407
227
|
}
|
|
408
|
-
|
|
409
|
-
|
|
228
|
+
this.#cache[permissionsKey].scopes[scopeKey] = {
|
|
229
|
+
scope: scopeKey,
|
|
230
|
+
timeout: setTimeout(() => {
|
|
231
|
+
delete this.#cache[permissionsKey].scopes[scopeKey];
|
|
232
|
+
}, this.#cacheTimeout).unref()
|
|
233
|
+
};
|
|
410
234
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
get agentManager() {
|
|
416
|
-
return __privateGet(this, _options2).agentManager;
|
|
417
|
-
}
|
|
235
|
+
next();
|
|
236
|
+
};
|
|
237
|
+
};
|
|
418
238
|
/*
|
|
419
239
|
let cookies = await this.GetCookiesFromJar();
|
|
420
240
|
const valid = this.#ValidateCookies(cookies);
|
|
@@ -435,17 +255,177 @@ class AuthUtilsNode {
|
|
|
435
255
|
async verifyRequestMiddleware(req, res, next) {
|
|
436
256
|
next();
|
|
437
257
|
}
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
259
|
+
SetCookiesToJar = async (headers, endpoint) => {
|
|
260
|
+
if (headers["set-cookie"]) {
|
|
261
|
+
headers["set-cookie"].map((headerCookie) => {
|
|
262
|
+
const cookie = Cookie.parse(headerCookie);
|
|
263
|
+
this.#cookiejar.setCookieSync(cookie, endpoint);
|
|
264
|
+
});
|
|
265
|
+
} else {
|
|
266
|
+
const cookie = Cookie.parse(headers["set-cookie"]);
|
|
267
|
+
this.#cookiejar.setCookieSync(cookie, endpoint);
|
|
268
|
+
}
|
|
269
|
+
return this.#cookiejar.getCookies(endpoint);
|
|
270
|
+
};
|
|
271
|
+
GetCookiesFromJar = async (endpoint) => {
|
|
272
|
+
return this.#cookiejar.getCookies(endpoint);
|
|
273
|
+
};
|
|
274
|
+
ValidateJWT = async (token, audience, endpoint) => {
|
|
275
|
+
const jwksClientUri = endpoint ? `${endpoint}${goptions.asoauthapiroot}${goptions.asjwksjsonpath}` : `${goptions.asendpoint}:${goptions.asport}${goptions.asoauthapiroot}${goptions.asjwksjsonpath}`;
|
|
276
|
+
const jwksClientOptions = {
|
|
277
|
+
cache: true,
|
|
278
|
+
//@@ all config items
|
|
279
|
+
cacheMaxEntries: 5,
|
|
280
|
+
// Default value
|
|
281
|
+
cacheMaxAge: 6e5,
|
|
282
|
+
// Defaults to 10m
|
|
283
|
+
rateLimit: true,
|
|
284
|
+
jwksRequestsPerMinute: 10,
|
|
285
|
+
// Default value
|
|
286
|
+
jwksUri: jwksClientUri,
|
|
287
|
+
timeout: 3e4
|
|
288
|
+
//@@ config
|
|
289
|
+
};
|
|
290
|
+
if (this.#options.agentManager) {
|
|
291
|
+
jwksClientOptions.requestAgent = this.#options.agentManager.GetAgent(jwksClientUri);
|
|
292
|
+
}
|
|
293
|
+
const jwks = jwksClient(jwksClientOptions);
|
|
294
|
+
const decodedRefreshToken = jwtDecode(token, { header: true });
|
|
295
|
+
const kid = decodedRefreshToken.kid;
|
|
296
|
+
const key = await jwks.getSigningKey(kid);
|
|
297
|
+
const signingKey = key.getPublicKey();
|
|
298
|
+
const verifyOptions = {
|
|
299
|
+
issuer: iss,
|
|
300
|
+
//subject: s,
|
|
301
|
+
audience,
|
|
302
|
+
//expiresIn: 600, // 10 minutes
|
|
303
|
+
algorithm: ["RS256"]
|
|
304
|
+
// RSASSA [ "RS256", "RS384", "RS512" ]
|
|
305
|
+
};
|
|
306
|
+
return jwt.verify(token, signingKey, verifyOptions);
|
|
307
|
+
};
|
|
308
|
+
// Function to extract the origin from a URI
|
|
309
|
+
ExtractOrigin = (uri) => {
|
|
310
|
+
const match = uri.match(this.#originRegex);
|
|
311
|
+
return match ? match[1] : null;
|
|
312
|
+
};
|
|
313
|
+
GetAPITokenFromAuthServerUsingScope = async (options, errorCb) => {
|
|
314
|
+
const { scope, clientId, authClientSecret, endPoint, instrumentController, outputErrorsToConsole } = options;
|
|
315
|
+
let stage = "1";
|
|
316
|
+
const invokeErrorCb = (error) => {
|
|
317
|
+
this.#LogDebugMessage(error);
|
|
318
|
+
if (instrumentController) {
|
|
319
|
+
instrumentController.UpdateInstrument(Gauge.AUTHENTICATION_ERROR_COUNT_GAUGE, {
|
|
320
|
+
// auth error
|
|
321
|
+
Inc: 1
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
errorCb(error);
|
|
325
|
+
};
|
|
326
|
+
try {
|
|
327
|
+
stage = "2";
|
|
328
|
+
const scopes = scope.split(" ");
|
|
329
|
+
let origin = null;
|
|
330
|
+
let error = null;
|
|
331
|
+
stage = "3";
|
|
332
|
+
for (let i = 0; i < scopes.length; i++) {
|
|
333
|
+
const s = scopes[i];
|
|
334
|
+
if (!origin) {
|
|
335
|
+
origin = this.ExtractOrigin(s);
|
|
336
|
+
if (!origin) {
|
|
337
|
+
error = new Error(`Scope: [${scope}] not in required format. Must use (space seperated) api://<client id>[/<resource>.<permission>].`);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
const nextOrigin = this.ExtractOrigin(s);
|
|
342
|
+
if (!nextOrigin) {
|
|
343
|
+
error = new Error(`Scope: [${scope}] not in required format. Must use (space seperated) api://<client id>[/<resource>.<permission>].`);
|
|
344
|
+
break;
|
|
345
|
+
} else {
|
|
346
|
+
if (origin.localeCompare(nextOrigin) !== 0) {
|
|
347
|
+
error = new Error(`Scope: [${scope}] not all from the same client API. All scopes must come from the same client API.`);
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
stage = "4";
|
|
354
|
+
if (error) {
|
|
355
|
+
invokeErrorCb(error);
|
|
356
|
+
return "";
|
|
357
|
+
}
|
|
358
|
+
stage = "5";
|
|
359
|
+
const payload = {
|
|
360
|
+
//@@ make a type
|
|
361
|
+
client_id: clientId,
|
|
362
|
+
// The service calling this method
|
|
363
|
+
client_secret: authClientSecret,
|
|
364
|
+
// Auth service client secret
|
|
365
|
+
//client_secret: goptions.brokerclientsecret, // Broker service client secret
|
|
366
|
+
scope,
|
|
367
|
+
// required API
|
|
368
|
+
//@@ remove audience
|
|
369
|
+
//@@ need scope to be the API identifier
|
|
370
|
+
grant_type: "client_credentials"
|
|
371
|
+
};
|
|
372
|
+
stage = "6";
|
|
373
|
+
const url = endPoint ? `${endPoint}${goptions.asoauthapiroot}/token` : `${goptions.asendpoint}:${goptions.asport}${goptions.asoauthapiroot}/token`;
|
|
374
|
+
stage = `6.5: url: [${url}] payload: [${JSON.stringify(payload)}]`;
|
|
375
|
+
const axiosConfig = new STSAxiosConfig(url, "post").withDefaultHeaders().withData(payload);
|
|
376
|
+
if (this.#options.agentManager) {
|
|
377
|
+
axiosConfig.withAgentManager(this.#options.agentManager);
|
|
378
|
+
}
|
|
379
|
+
const retVal = await axios(axiosConfig.config);
|
|
380
|
+
stage = "7";
|
|
381
|
+
if (retVal.status) {
|
|
382
|
+
if (retVal.status !== 200) {
|
|
383
|
+
this.#LogDebugMessage(chalk.magenta(`Error (AuthUtilsNode:GetAPITokenFromServer): Invalid response from server: [${retVal.status}]`));
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
invokeErrorCb(new Error(chalk.red(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.status)`)));
|
|
387
|
+
return "";
|
|
388
|
+
}
|
|
389
|
+
stage = "8";
|
|
390
|
+
if (retVal.data) {
|
|
391
|
+
stage = "9";
|
|
392
|
+
if (retVal.data.access_token) {
|
|
393
|
+
stage = "10";
|
|
394
|
+
if (instrumentController) {
|
|
395
|
+
stage = "11";
|
|
396
|
+
instrumentController.UpdateInstrument(Gauge.AUTHENTICATION_COUNT_GAUGE, {
|
|
397
|
+
Inc: 1
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
stage = "12";
|
|
401
|
+
return retVal.data.access_token;
|
|
402
|
+
} else {
|
|
403
|
+
stage = "13";
|
|
404
|
+
invokeErrorCb(new Error(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.data.access_token)`));
|
|
405
|
+
return "";
|
|
406
|
+
}
|
|
407
|
+
} else {
|
|
408
|
+
stage = "14";
|
|
409
|
+
invokeErrorCb(new Error(`Error (AuthUtilsNode:GetAPITokenFromServer:No retVal.data)`));
|
|
410
|
+
return "";
|
|
411
|
+
}
|
|
412
|
+
} catch (error) {
|
|
413
|
+
if (outputErrorsToConsole === true) {
|
|
414
|
+
console.error(error);
|
|
415
|
+
}
|
|
416
|
+
let details = "None available.";
|
|
417
|
+
if (error.response && error.response.data) {
|
|
418
|
+
try {
|
|
419
|
+
details = JSON.stringify(error.response.data);
|
|
420
|
+
} catch (error2) {
|
|
421
|
+
details = `Could not JSON.stringify(error.response.data)`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
invokeErrorCb(new Error(`Error (AuthUtilsNode:GetAPITokenFromServer:catch): [${error}], Stage: [${stage}], Details: [${details}]`));
|
|
425
|
+
return "";
|
|
426
|
+
}
|
|
427
|
+
};
|
|
438
428
|
}
|
|
439
|
-
_options2 = new WeakMap();
|
|
440
|
-
_cache = new WeakMap();
|
|
441
|
-
_cacheTimeout = new WeakMap();
|
|
442
|
-
_cookiejar = new WeakMap();
|
|
443
|
-
_originRegex = new WeakMap();
|
|
444
|
-
_AuthUtilsNode_instances = new WeakSet();
|
|
445
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
446
|
-
LogDebugMessage_fn = function(message) {
|
|
447
|
-
__privateGet(this, _options2).logger.debug(message);
|
|
448
|
-
};
|
|
449
429
|
export {
|
|
450
430
|
AuthUtilsNode,
|
|
451
431
|
STSAuthClientErrorCode,
|