1 | "use strict";
|
2 | var __assign = (this && this.__assign) || function () {
|
3 | __assign = Object.assign || function(t) {
|
4 | for (var s, i = 1, n = arguments.length; i < n; i++) {
|
5 | s = arguments[i];
|
6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
7 | t[p] = s[p];
|
8 | }
|
9 | return t;
|
10 | };
|
11 | return __assign.apply(this, arguments);
|
12 | };
|
13 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
14 | if (k2 === undefined) k2 = k;
|
15 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
16 | }) : (function(o, m, k, k2) {
|
17 | if (k2 === undefined) k2 = k;
|
18 | o[k2] = m[k];
|
19 | }));
|
20 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
21 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
22 | }) : function(o, v) {
|
23 | o["default"] = v;
|
24 | });
|
25 | var __importStar = (this && this.__importStar) || function (mod) {
|
26 | if (mod && mod.__esModule) return mod;
|
27 | var result = {};
|
28 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
29 | __setModuleDefault(result, mod);
|
30 | return result;
|
31 | };
|
32 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
33 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
34 | return new (P || (P = Promise))(function (resolve, reject) {
|
35 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
36 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
37 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
38 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
39 | });
|
40 | };
|
41 | var __generator = (this && this.__generator) || function (thisArg, body) {
|
42 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
43 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
44 | function verb(n) { return function (v) { return step([n, v]); }; }
|
45 | function step(op) {
|
46 | if (f) throw new TypeError("Generator is already executing.");
|
47 | while (_) try {
|
48 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
49 | if (y = 0, t) op = [op[0] & 2, t.value];
|
50 | switch (op[0]) {
|
51 | case 0: case 1: t = op; break;
|
52 | case 4: _.label++; return { value: op[1], done: false };
|
53 | case 5: _.label++; y = op[1]; op = [0]; continue;
|
54 | case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
55 | default:
|
56 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
57 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
58 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
59 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
60 | if (t[2]) _.ops.pop();
|
61 | _.trys.pop(); continue;
|
62 | }
|
63 | op = body.call(thisArg, _);
|
64 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
65 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
66 | }
|
67 | };
|
68 | var __spreadArray = (this && this.__spreadArray) || function (to, from) {
|
69 | for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
|
70 | to[j] = from[i];
|
71 | return to;
|
72 | };
|
73 | Object.defineProperty(exports, "__esModule", { value: true });
|
74 | exports.isUserToken = exports.checkMandatoryValue = exports.readPropertyWithWarn = exports.wrapJwtInHeader = exports.audiences = exports.issuerUrl = exports.verifyJwtWithKey = exports.verificationKeyCache = exports.verifyJwt = exports.retrieveJwt = exports.decodeJwtComplete = exports.decodeJwt = void 0;
|
75 | var url = __importStar(require("url"));
|
76 | var util_1 = require("@sap-cloud-sdk/util");
|
77 | var jsonwebtoken_1 = require("jsonwebtoken");
|
78 | var environment_accessor_1 = require("./environment-accessor");
|
79 | var cache_1 = require("./cache");
|
80 | var verification_keys_1 = require("./verification-keys");
|
81 | var logger = util_1.createLogger({
|
82 | package: 'core',
|
83 | messageContext: 'jwt'
|
84 | });
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function decodeJwt(token) {
|
91 | return decodeJwtComplete(token).payload;
|
92 | }
|
93 | exports.decodeJwt = decodeJwt;
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | function decodeJwtComplete(token) {
|
100 | var decodedToken = jsonwebtoken_1.decode(token, { complete: true });
|
101 | if (decodedToken === null || typeof decodedToken === 'string') {
|
102 | throw new Error('JwtError: The given jwt payload does not encode valid JSON.');
|
103 | }
|
104 | return decodedToken;
|
105 | }
|
106 | exports.decodeJwtComplete = decodeJwtComplete;
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | function retrieveJwt(req) {
|
113 | var header = authHeader(req);
|
114 | if (validateAuthHeader(header)) {
|
115 | return header.split(' ')[1];
|
116 | }
|
117 | }
|
118 | exports.retrieveJwt = retrieveJwt;
|
119 | function authHeader(req) {
|
120 | var entries = Object.entries(req.headers).find(function (_a) {
|
121 | var key = _a[0];
|
122 | return key.toLowerCase() === 'authorization';
|
123 | });
|
124 | if (entries) {
|
125 | var header = entries[1];
|
126 |
|
127 | return Array.isArray(header) ? header[0] : header;
|
128 | }
|
129 | return undefined;
|
130 | }
|
131 | function validateAuthHeader(header) {
|
132 | if (typeof header === 'undefined') {
|
133 | logger.warn('Authorization header not set.');
|
134 | return false;
|
135 | }
|
136 | var _a = header.split(' '), authType = _a[0], token = _a[1];
|
137 | if (typeof token === 'undefined') {
|
138 | logger.warn('Token in auth header missing.');
|
139 | return false;
|
140 | }
|
141 | if (authType.toLowerCase() !== 'bearer') {
|
142 | logger.warn('Authorization type is not Bearer.');
|
143 | return false;
|
144 | }
|
145 | return true;
|
146 | }
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | function validateJwtHeaderForVerification(header, uaaDomain) {
|
156 | if (!header.jku || !header.kid) {
|
157 | throw new Error('JWT does not contain verification key URL (`jku`) and/or key ID (`kid`).');
|
158 | }
|
159 | var jkuDomain = url.parse(header.jku).hostname;
|
160 | if (!uaaDomain || !jkuDomain || !jkuDomain.endsWith(uaaDomain)) {
|
161 | throw new Error("The domains of the XSUAA and verification URL do not match. The XSUAA domain is '" + uaaDomain + "' and the jku field provided in the JWT is '" + jkuDomain + "'.");
|
162 | }
|
163 | }
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | function verifyJwt(token, options) {
|
198 | return __awaiter(this, void 0, void 0, function () {
|
199 | var creds, header, cacheKey, key;
|
200 | return __generator(this, function (_a) {
|
201 | options = __assign(__assign({}, defaultVerifyJwtOptions), options);
|
202 | creds = environment_accessor_1.getXsuaaServiceCredentials(token);
|
203 | header = decodeJwtComplete(token).header;
|
204 | validateJwtHeaderForVerification(header, creds.uaadomain);
|
205 | cacheKey = buildCacheKey(header.jku, header.kid);
|
206 | if (options.cacheVerificationKeys) {
|
207 | key = exports.verificationKeyCache.get(cacheKey);
|
208 | if (key) {
|
209 | return [2 , verifyJwtWithKey(token, key.value).catch(function (error) {
|
210 | logger.warn('Unable to verify JWT with cached key, fetching new verification key.');
|
211 | logger.warn("Original error: " + error.message);
|
212 | return fetchAndCacheKeyAndVerify(creds, header, token, options);
|
213 | })];
|
214 | }
|
215 | }
|
216 | return [2 , fetchAndCacheKeyAndVerify(creds, header, token, options)];
|
217 | });
|
218 | });
|
219 | }
|
220 | exports.verifyJwt = verifyJwt;
|
221 | function fetchAndCacheKeyAndVerify(creds, header, token, options) {
|
222 | return __awaiter(this, void 0, void 0, function () {
|
223 | var key;
|
224 | return __generator(this, function (_a) {
|
225 | switch (_a.label) {
|
226 | case 0: return [4 , getVerificationKey(creds, header).catch(function (error) {
|
227 | throw new util_1.ErrorWithCause('Failed to verify JWT. Could not retrieve verification key.', error);
|
228 | })];
|
229 | case 1:
|
230 | key = _a.sent();
|
231 | if (options === null || options === void 0 ? void 0 : options.cacheVerificationKeys) {
|
232 | exports.verificationKeyCache.set(buildCacheKey(header.jku, header.kid), key);
|
233 | }
|
234 | return [2 , verifyJwtWithKey(token, key.value)];
|
235 | }
|
236 | });
|
237 | });
|
238 | }
|
239 | var defaultVerifyJwtOptions = {
|
240 | cacheVerificationKeys: true
|
241 | };
|
242 | function getVerificationKey(xsuaaCredentials, header) {
|
243 | return verification_keys_1.fetchVerificationKeys(xsuaaCredentials, header.jku).then(function (verificationKeys) {
|
244 | if (!verificationKeys.length) {
|
245 | throw Error('No verification keys have been returned by the XSUAA service.');
|
246 | }
|
247 | var verificationKey = verificationKeys.find(function (key) { return key.keyId === header.kid; });
|
248 | if (!verificationKey) {
|
249 | throw new Error('Could not find verification key for the given key ID.');
|
250 | }
|
251 | return verificationKey;
|
252 | });
|
253 | }
|
254 |
|
255 | exports.verificationKeyCache = new cache_1.Cache({ minutes: 15 });
|
256 | function buildCacheKey(jku, kid) {
|
257 | if (!jku || !kid) {
|
258 | throw new Error('Could not build cache key. `jku` and/or `kid` is not defined.');
|
259 | }
|
260 | return jku + kid;
|
261 | }
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 | function verifyJwtWithKey(token, key) {
|
269 | return new Promise(function (resolve, reject) {
|
270 | jsonwebtoken_1.verify(token, sanitizeVerificationKey(key), function (err, decodedToken) {
|
271 | if (err) {
|
272 | return reject(new util_1.ErrorWithCause('Invalid JWT.', err));
|
273 | }
|
274 | if (!decodedToken) {
|
275 | return reject('Invalid JWT. Token verification yielded `undefined`.');
|
276 | }
|
277 | return resolve(decodedToken);
|
278 | });
|
279 | });
|
280 | }
|
281 | exports.verifyJwtWithKey = verifyJwtWithKey;
|
282 | function sanitizeVerificationKey(key) {
|
283 |
|
284 | return key
|
285 | .replace(/\n/g, '')
|
286 | .replace(/(KEY\s*-+)([^\n-])/, '$1\n$2')
|
287 | .replace(/([^\n-])(-+\s*END)/, '$1\n$2');
|
288 | }
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 | function issuerUrl(decodedToken) {
|
295 | return readPropertyWithWarn(decodedToken, 'iss');
|
296 | }
|
297 | exports.issuerUrl = issuerUrl;
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 | function audiences(decodedToken) {
|
310 | if (audiencesFromAud(decodedToken).length) {
|
311 | return new Set(audiencesFromAud(decodedToken));
|
312 | }
|
313 | return new Set(audiencesFromScope(decodedToken));
|
314 | }
|
315 | exports.audiences = audiences;
|
316 | function audiencesFromAud(decodedToken) {
|
317 | if (!(decodedToken.aud instanceof Array && decodedToken.aud.length)) {
|
318 | return [];
|
319 | }
|
320 | return decodedToken.aud.map(function (aud) {
|
321 | return aud.includes('.') ? aud.substr(0, aud.indexOf('.')) : aud;
|
322 | });
|
323 | }
|
324 | function audiencesFromScope(decodedToken) {
|
325 | if (!decodedToken.scope) {
|
326 | return [];
|
327 | }
|
328 | var scopes = decodedToken.scope instanceof Array
|
329 | ? decodedToken.scope
|
330 | : [decodedToken.scope];
|
331 | return scopes.reduce(function (aud, scope) {
|
332 | if (scope.includes('.')) {
|
333 | return __spreadArray(__spreadArray([], aud), [scope.substr(0, scope.indexOf('.'))]);
|
334 | }
|
335 | return aud;
|
336 | }, []);
|
337 | }
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 | function wrapJwtInHeader(token) {
|
344 | return { headers: { Authorization: 'Bearer ' + token } };
|
345 | }
|
346 | exports.wrapJwtInHeader = wrapJwtInHeader;
|
347 | function readPropertyWithWarn(jwtPayload, property) {
|
348 | if (!jwtPayload[property]) {
|
349 | logger.warn("WarningJWT: The provided JWT payload does not include a '" + property + "' property.");
|
350 | }
|
351 | return jwtPayload[property];
|
352 | }
|
353 | exports.readPropertyWithWarn = readPropertyWithWarn;
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 | function checkMandatoryValue(key, mapping, jwtPayload) {
|
361 | var value = mapping[key].extractorFunction(jwtPayload);
|
362 | if (!value) {
|
363 | throw new Error("Property '" + mapping[key].keyInJwt + "' is missing in JWT payload.");
|
364 | }
|
365 | }
|
366 | exports.checkMandatoryValue = checkMandatoryValue;
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 | function isUserToken(token) {
|
374 | if (!token) {
|
375 | return false;
|
376 | }
|
377 |
|
378 | var keys = Object.keys(token.decoded);
|
379 | return !(keys.length === 1 && keys[0] === 'iss');
|
380 | }
|
381 | exports.isUserToken = isUserToken;
|
382 |
|
\ | No newline at end of file |