@boxyhq/saml-jackson 1.20.2 → 1.20.4
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/controller/oauth/allowed.js +13 -1
- package/dist/controller/oauth/allowed.js.map +1 -1
- package/dist/controller/oauth.d.ts +3 -1
- package/dist/controller/oauth.js +124 -59
- package/dist/controller/oauth.js.map +1 -1
- package/dist/controller/sso-handler.d.ts +4 -3
- package/dist/controller/sso-handler.js +18 -13
- package/dist/controller/sso-handler.js.map +1 -1
- package/dist/controller/utils.d.ts +3 -0
- package/dist/controller/utils.js +7 -1
- package/dist/controller/utils.js.map +1 -1
- package/dist/ee/federated-saml/app.d.ts +29 -15
- package/dist/ee/federated-saml/app.js +67 -31
- package/dist/ee/federated-saml/app.js.map +1 -1
- package/dist/ee/federated-saml/index.js +1 -1
- package/dist/ee/federated-saml/index.js.map +1 -1
- package/dist/ee/federated-saml/types.d.ts +6 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
@@ -10,8 +10,20 @@ const redirect = (redirectUrl, redirectUrls) => {
|
|
10
10
|
const url = new URL(redirectUrl);
|
11
11
|
for (const idx in redirectUrls) {
|
12
12
|
const rUrl = new URL(redirectUrls[idx]);
|
13
|
+
let hostname = url.hostname;
|
14
|
+
let hostNameAllowed = rUrl.hostname;
|
15
|
+
// allow subdomain globbing *.example.com only
|
16
|
+
try {
|
17
|
+
if (rUrl.hostname.startsWith('*.')) {
|
18
|
+
hostNameAllowed = rUrl.hostname.slice(2);
|
19
|
+
hostname = hostname.slice(hostname.indexOf('.') + 1);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
catch (e) {
|
23
|
+
// no-op
|
24
|
+
}
|
13
25
|
// TODO: Check pathname, for now pathname is ignored
|
14
|
-
if (rUrl.protocol === url.protocol &&
|
26
|
+
if (rUrl.protocol === url.protocol && hostNameAllowed === hostname && rUrl.port === url.port) {
|
15
27
|
return true;
|
16
28
|
}
|
17
29
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"allowed.js","sourceRoot":"","sources":["../../../src/controller/oauth/allowed.ts"],"names":[],"mappings":";;;AAAA,MAAM,sBAAsB,GAAG,oCAAoC,CAAC;AAE7D,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAE,YAAsB,EAAW,EAAE;IAC/E,0CAA0C;IAC1C,IAAI,WAAW,KAAK,sBAAsB,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,GAAG,GAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAEtC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAE7C,
|
1
|
+
{"version":3,"file":"allowed.js","sourceRoot":"","sources":["../../../src/controller/oauth/allowed.ts"],"names":[],"mappings":";;;AAAA,MAAM,sBAAsB,GAAG,oCAAoC,CAAC;AAE7D,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAE,YAAsB,EAAW,EAAE;IAC/E,0CAA0C;IAC1C,IAAI,WAAW,KAAK,sBAAsB,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,GAAG,GAAQ,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAEtC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAQ,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;QAE7C,IAAI,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC5B,IAAI,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC;QAEpC,8CAA8C;QAC9C,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACzC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ;QACV,CAAC;QAED,oDAAoD;QAEpD,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,eAAe,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;YAC7F,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAhCW,QAAA,QAAQ,YAgCnB"}
|
@@ -7,13 +7,15 @@ export declare class OAuthController implements IOAuthController {
|
|
7
7
|
private ssoTracer;
|
8
8
|
private opts;
|
9
9
|
private ssoHandler;
|
10
|
-
|
10
|
+
private samlFedApp;
|
11
|
+
constructor({ connectionStore, sessionStore, codeStore, tokenStore, ssoTracer, opts, samlFedApp }: {
|
11
12
|
connectionStore: any;
|
12
13
|
sessionStore: any;
|
13
14
|
codeStore: any;
|
14
15
|
tokenStore: any;
|
15
16
|
ssoTracer: any;
|
16
17
|
opts: any;
|
18
|
+
samlFedApp: any;
|
17
19
|
});
|
18
20
|
authorize(body: OAuthReq): Promise<{
|
19
21
|
redirect_url?: string;
|
package/dist/controller/oauth.js
CHANGED
@@ -43,6 +43,7 @@ const zlib_1 = require("zlib");
|
|
43
43
|
const saml20_1 = __importDefault(require("@boxyhq/saml20"));
|
44
44
|
const openid_client_1 = require("openid-client");
|
45
45
|
const utils_1 = require("./utils");
|
46
|
+
const utils_2 = require("./utils");
|
46
47
|
const metrics = __importStar(require("../opentelemetry/metrics"));
|
47
48
|
const error_1 = require("./error");
|
48
49
|
const allowed = __importStar(require("./oauth/allowed"));
|
@@ -54,13 +55,14 @@ const lib_1 = require("../saml/lib");
|
|
54
55
|
const oidc_issuer_1 = require("./oauth/oidc-issuer");
|
55
56
|
const deflateRawAsync = (0, util_1.promisify)(zlib_1.deflateRaw);
|
56
57
|
class OAuthController {
|
57
|
-
constructor({ connectionStore, sessionStore, codeStore, tokenStore, ssoTracer, opts }) {
|
58
|
+
constructor({ connectionStore, sessionStore, codeStore, tokenStore, ssoTracer, opts, samlFedApp }) {
|
58
59
|
this.connectionStore = connectionStore;
|
59
60
|
this.sessionStore = sessionStore;
|
60
61
|
this.codeStore = codeStore;
|
61
62
|
this.tokenStore = tokenStore;
|
62
63
|
this.ssoTracer = ssoTracer;
|
63
64
|
this.opts = opts;
|
65
|
+
this.samlFedApp = samlFedApp;
|
64
66
|
this.ssoHandler = new sso_handler_1.SSOHandler({
|
65
67
|
connection: connectionStore,
|
66
68
|
session: sessionStore,
|
@@ -76,6 +78,7 @@ class OAuthController {
|
|
76
78
|
let requestedScopes;
|
77
79
|
let requestedOIDCFlow;
|
78
80
|
let connection;
|
81
|
+
let fedApp;
|
79
82
|
try {
|
80
83
|
const tenant = 'tenant' in body ? body.tenant : undefined;
|
81
84
|
const product = 'product' in body ? body.product : undefined;
|
@@ -87,7 +90,7 @@ class OAuthController {
|
|
87
90
|
if (!redirect_uri) {
|
88
91
|
throw new error_1.JacksonError('Please specify a redirect URL.', 400);
|
89
92
|
}
|
90
|
-
requestedScopes = (0,
|
93
|
+
requestedScopes = (0, utils_2.getScopeValues)(scope);
|
91
94
|
requestedOIDCFlow = requestedScopes.includes('openid');
|
92
95
|
if (tenant && product) {
|
93
96
|
const response = yield this.ssoHandler.resolveConnection({
|
@@ -108,17 +111,17 @@ class OAuthController {
|
|
108
111
|
}
|
109
112
|
else if (client_id && client_id !== '' && client_id !== 'undefined' && client_id !== 'null') {
|
110
113
|
// if tenant and product are encoded in the client_id then we parse it and check for the relevant connection(s)
|
111
|
-
let sp = (0,
|
114
|
+
let sp = (0, utils_2.getEncodedTenantProduct)(client_id);
|
112
115
|
if (!sp && access_type) {
|
113
|
-
sp = (0,
|
116
|
+
sp = (0, utils_2.getEncodedTenantProduct)(access_type);
|
114
117
|
}
|
115
118
|
if (!sp && resource) {
|
116
|
-
sp = (0,
|
119
|
+
sp = (0, utils_2.getEncodedTenantProduct)(resource);
|
117
120
|
}
|
118
121
|
if (!sp && requestedScopes) {
|
119
122
|
const encodedParams = requestedScopes.find((scope) => scope.includes('=') && scope.includes('&')); // for now assume only one encoded param i.e. for tenant/product
|
120
123
|
if (encodedParams) {
|
121
|
-
sp = (0,
|
124
|
+
sp = (0, utils_2.getEncodedTenantProduct)(encodedParams);
|
122
125
|
}
|
123
126
|
}
|
124
127
|
if (sp && sp.tenant && sp.product) {
|
@@ -142,10 +145,38 @@ class OAuthController {
|
|
142
145
|
}
|
143
146
|
}
|
144
147
|
else {
|
145
|
-
connection
|
146
|
-
if
|
147
|
-
|
148
|
-
|
148
|
+
// client_id is not encoded, so we look for the connection using the client_id
|
149
|
+
// First we check if it's a federated connection
|
150
|
+
if (client_id.startsWith(`${utils_1.clientIDFederatedPrefix}${utils_1.clientIDOIDCPrefix}`)) {
|
151
|
+
fedApp = yield this.samlFedApp.get({
|
152
|
+
id: client_id.replace(utils_1.clientIDFederatedPrefix, ''),
|
153
|
+
});
|
154
|
+
const response = yield this.ssoHandler.resolveConnection({
|
155
|
+
tenant: fedApp.tenant,
|
156
|
+
product: fedApp.product,
|
157
|
+
idp_hint,
|
158
|
+
authFlow: 'oauth',
|
159
|
+
originalParams: Object.assign({}, body),
|
160
|
+
tenants: fedApp.tenants,
|
161
|
+
samlFedAppId: fedApp.id,
|
162
|
+
fedType: fedApp.type,
|
163
|
+
});
|
164
|
+
if ('redirectUrl' in response) {
|
165
|
+
return {
|
166
|
+
redirect_url: response.redirectUrl,
|
167
|
+
};
|
168
|
+
}
|
169
|
+
if ('connection' in response) {
|
170
|
+
connection = response.connection;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
else {
|
174
|
+
// If it's not a federated connection, we look for the connection using the client_id
|
175
|
+
connection = yield this.connectionStore.get(client_id);
|
176
|
+
if (connection) {
|
177
|
+
requestedTenant = connection.tenant;
|
178
|
+
requestedProduct = connection.product;
|
179
|
+
}
|
149
180
|
}
|
150
181
|
}
|
151
182
|
}
|
@@ -156,11 +187,18 @@ class OAuthController {
|
|
156
187
|
throw new error_1.JacksonError('IdP connection not found.', 403);
|
157
188
|
}
|
158
189
|
if (!allowed.redirect(redirect_uri, connection.redirectUrl)) {
|
159
|
-
|
190
|
+
if (fedApp) {
|
191
|
+
if (!allowed.redirect(redirect_uri, fedApp.redirectUrl)) {
|
192
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
else {
|
196
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
197
|
+
}
|
160
198
|
}
|
161
199
|
}
|
162
200
|
catch (err) {
|
163
|
-
const error_description = (0,
|
201
|
+
const error_description = (0, utils_2.getErrorMessage)(err);
|
164
202
|
// Save the error trace
|
165
203
|
yield this.ssoTracer.saveTrace({
|
166
204
|
error: error_description,
|
@@ -174,11 +212,11 @@ class OAuthController {
|
|
174
212
|
});
|
175
213
|
throw err;
|
176
214
|
}
|
177
|
-
if (!(0,
|
215
|
+
if (!(0, utils_2.isConnectionActive)(connection)) {
|
178
216
|
throw new error_1.JacksonError('SSO connection is deactivated. Please contact your administrator.', 403);
|
179
217
|
}
|
180
218
|
const isMissingJWTKeysForOIDCFlow = requestedOIDCFlow &&
|
181
|
-
(!((_a = this.opts.openid) === null || _a === void 0 ? void 0 : _a.jwtSigningKeys) || !(0,
|
219
|
+
(!((_a = this.opts.openid) === null || _a === void 0 ? void 0 : _a.jwtSigningKeys) || !(0, utils_2.isJWSKeyPairLoaded)(this.opts.openid.jwtSigningKeys));
|
182
220
|
const oAuthClientReqError = !state || response_type !== 'code';
|
183
221
|
const connectionIsSAML = 'idpMetadata' in connection && connection.idpMetadata !== undefined;
|
184
222
|
const connectionIsOIDC = 'oidcProvider' in connection && connection.oidcProvider !== undefined;
|
@@ -213,7 +251,7 @@ class OAuthController {
|
|
213
251
|
},
|
214
252
|
});
|
215
253
|
return {
|
216
|
-
redirect_url: (0,
|
254
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
217
255
|
error,
|
218
256
|
error_description: traceId ? `${traceId}: ${error_description}` : error_description,
|
219
257
|
redirect_uri,
|
@@ -226,7 +264,7 @@ class OAuthController {
|
|
226
264
|
let post = false;
|
227
265
|
// Init sessionId
|
228
266
|
const sessionId = crypto_1.default.randomBytes(16).toString('hex');
|
229
|
-
const relayState =
|
267
|
+
const relayState = utils_2.relayStatePrefix + sessionId;
|
230
268
|
// SAML connection: SAML request will be constructed here
|
231
269
|
let samlReq;
|
232
270
|
if (connectionIsSAML) {
|
@@ -256,7 +294,7 @@ class OAuthController {
|
|
256
294
|
},
|
257
295
|
});
|
258
296
|
return {
|
259
|
-
redirect_url: (0,
|
297
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
260
298
|
error: 'invalid_request',
|
261
299
|
error_description: traceId ? `${traceId}: ${error_description}` : error_description,
|
262
300
|
redirect_uri,
|
@@ -278,7 +316,7 @@ class OAuthController {
|
|
278
316
|
});
|
279
317
|
}
|
280
318
|
catch (err) {
|
281
|
-
const error_description = (0,
|
319
|
+
const error_description = (0, utils_2.getErrorMessage)(err);
|
282
320
|
// Save the error trace
|
283
321
|
const traceId = yield this.ssoTracer.saveTrace({
|
284
322
|
error: error_description,
|
@@ -291,7 +329,7 @@ class OAuthController {
|
|
291
329
|
},
|
292
330
|
});
|
293
331
|
return {
|
294
|
-
redirect_url: (0,
|
332
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
295
333
|
error: 'server_error',
|
296
334
|
error_description: traceId ? `${traceId}: ${error_description}` : error_description,
|
297
335
|
redirect_uri,
|
@@ -306,7 +344,7 @@ class OAuthController {
|
|
306
344
|
if (connectionIsOIDC) {
|
307
345
|
if (!this.opts.oidcPath) {
|
308
346
|
return {
|
309
|
-
redirect_url: (0,
|
347
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
310
348
|
error: 'server_error',
|
311
349
|
error_description: 'OpenID response handler path (oidcPath) is not set',
|
312
350
|
redirect_uri,
|
@@ -340,9 +378,9 @@ class OAuthController {
|
|
340
378
|
catch (err) {
|
341
379
|
if (err) {
|
342
380
|
return {
|
343
|
-
redirect_url: (0,
|
381
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
344
382
|
error: 'server_error',
|
345
|
-
error_description: (err === null || err === void 0 ? void 0 : err.error) || (0,
|
383
|
+
error_description: (err === null || err === void 0 ? void 0 : err.error) || (0, utils_2.getErrorMessage)(err),
|
346
384
|
redirect_uri,
|
347
385
|
state,
|
348
386
|
}),
|
@@ -362,6 +400,11 @@ class OAuthController {
|
|
362
400
|
if (idp_hint) {
|
363
401
|
requested.idp_hint = idp_hint;
|
364
402
|
}
|
403
|
+
else {
|
404
|
+
if (fedApp) {
|
405
|
+
requested.idp_hint = connection.clientID;
|
406
|
+
}
|
407
|
+
}
|
365
408
|
if (requestedOIDCFlow) {
|
366
409
|
requested.oidc = true;
|
367
410
|
if (nonce) {
|
@@ -378,6 +421,7 @@ class OAuthController {
|
|
378
421
|
code_challenge,
|
379
422
|
code_challenge_method,
|
380
423
|
requested,
|
424
|
+
oidcFederated: fedApp ? { redirectUrl: fedApp.redirectUrl, id: fedApp.id } : undefined,
|
381
425
|
};
|
382
426
|
yield this.sessionStore.put(sessionId, connectionIsSAML
|
383
427
|
? Object.assign(Object.assign({}, sessionObj), { id: samlReq === null || samlReq === void 0 ? void 0 : samlReq.id }) : Object.assign(Object.assign({}, sessionObj), { id: connection.clientID, oidcCodeVerifier, oidcNonce }));
|
@@ -416,7 +460,7 @@ class OAuthController {
|
|
416
460
|
throw 'Connection appears to be misconfigured';
|
417
461
|
}
|
418
462
|
catch (err) {
|
419
|
-
const error_description = (0,
|
463
|
+
const error_description = (0, utils_2.getErrorMessage)(err);
|
420
464
|
// Save the error trace
|
421
465
|
const traceId = yield this.ssoTracer.saveTrace({
|
422
466
|
error: error_description,
|
@@ -430,7 +474,7 @@ class OAuthController {
|
|
430
474
|
},
|
431
475
|
});
|
432
476
|
return {
|
433
|
-
redirect_url: (0,
|
477
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
434
478
|
error: 'server_error',
|
435
479
|
error_description: traceId ? `${traceId}: ${error_description}` : error_description,
|
436
480
|
redirect_uri,
|
@@ -441,7 +485,7 @@ class OAuthController {
|
|
441
485
|
});
|
442
486
|
}
|
443
487
|
samlResponse(body) {
|
444
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
488
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
445
489
|
return __awaiter(this, void 0, void 0, function* () {
|
446
490
|
let connection;
|
447
491
|
let rawResponse;
|
@@ -450,23 +494,24 @@ class OAuthController {
|
|
450
494
|
let issuer;
|
451
495
|
let isIdPFlow;
|
452
496
|
let isSAMLFederated;
|
497
|
+
let isOIDCFederated;
|
453
498
|
let validateOpts;
|
454
499
|
let redirect_uri;
|
455
500
|
const { SAMLResponse, idp_hint, RelayState = '' } = body;
|
456
501
|
try {
|
457
|
-
isIdPFlow = !RelayState.startsWith(
|
502
|
+
isIdPFlow = !RelayState.startsWith(utils_2.relayStatePrefix);
|
458
503
|
rawResponse = Buffer.from(SAMLResponse, 'base64').toString();
|
459
504
|
issuer = saml20_1.default.parseIssuer(rawResponse);
|
460
505
|
if (!this.opts.idpEnabled && isIdPFlow) {
|
461
506
|
// IdP login is disabled so block the request
|
462
507
|
throw new error_1.JacksonError('IdP (Identity Provider) flow has been disabled. Please head to your Service Provider to login.', 403);
|
463
508
|
}
|
464
|
-
sessionId = RelayState.replace(
|
509
|
+
sessionId = RelayState.replace(utils_2.relayStatePrefix, '');
|
465
510
|
if (!issuer) {
|
466
511
|
throw new error_1.JacksonError('Issuer not found.', 403);
|
467
512
|
}
|
468
513
|
const connections = (yield this.connectionStore.getByIndex({
|
469
|
-
name:
|
514
|
+
name: utils_2.IndexNames.EntityID,
|
470
515
|
value: issuer,
|
471
516
|
})).data;
|
472
517
|
if (!connections || connections.length === 0) {
|
@@ -477,6 +522,7 @@ class OAuthController {
|
|
477
522
|
throw new error_1.JacksonError('Unable to validate state from the origin request.', 403);
|
478
523
|
}
|
479
524
|
isSAMLFederated = session && 'samlFederated' in session;
|
525
|
+
isOIDCFederated = session && 'oidcFederated' in session;
|
480
526
|
const isSPFlow = !isIdPFlow && !isSAMLFederated;
|
481
527
|
// IdP initiated SSO flow
|
482
528
|
if (isIdPFlow) {
|
@@ -501,9 +547,10 @@ class OAuthController {
|
|
501
547
|
}
|
502
548
|
// SP initiated SSO flow
|
503
549
|
// Resolve if there are multiple matches for SP login
|
504
|
-
if (isSPFlow || isSAMLFederated) {
|
550
|
+
if (isSPFlow || isSAMLFederated || isOIDCFederated) {
|
505
551
|
connection = connections.filter((c) => {
|
506
552
|
return (c.clientID === session.requested.client_id ||
|
553
|
+
c.clientID === session.requested.idp_hint ||
|
507
554
|
(c.tenant === session.requested.tenant && c.product === session.requested.product));
|
508
555
|
})[0];
|
509
556
|
}
|
@@ -513,7 +560,14 @@ class OAuthController {
|
|
513
560
|
if (session &&
|
514
561
|
session.redirect_uri &&
|
515
562
|
!allowed.redirect(session.redirect_uri, connection.redirectUrl)) {
|
516
|
-
|
563
|
+
if (isOIDCFederated) {
|
564
|
+
if (!allowed.redirect(session.redirect_uri, (_a = session.oidcFederated) === null || _a === void 0 ? void 0 : _a.redirectUrl)) {
|
565
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
else {
|
569
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
570
|
+
}
|
517
571
|
}
|
518
572
|
const { privateKey } = yield (0, x509_1.getDefaultCertificate)();
|
519
573
|
validateOpts = {
|
@@ -534,20 +588,20 @@ class OAuthController {
|
|
534
588
|
catch (err) {
|
535
589
|
// Save the error trace
|
536
590
|
yield this.ssoTracer.saveTrace({
|
537
|
-
error: (0,
|
591
|
+
error: (0, utils_2.getErrorMessage)(err),
|
538
592
|
context: {
|
539
593
|
samlResponse: rawResponse,
|
540
|
-
tenant: ((
|
541
|
-
product: ((
|
542
|
-
clientID: ((
|
543
|
-
providerName: (
|
594
|
+
tenant: ((_b = session === null || session === void 0 ? void 0 : session.requested) === null || _b === void 0 ? void 0 : _b.tenant) || (connection === null || connection === void 0 ? void 0 : connection.tenant),
|
595
|
+
product: ((_c = session === null || session === void 0 ? void 0 : session.requested) === null || _c === void 0 ? void 0 : _c.product) || (connection === null || connection === void 0 ? void 0 : connection.product),
|
596
|
+
clientID: ((_d = session === null || session === void 0 ? void 0 : session.requested) === null || _d === void 0 ? void 0 : _d.client_id) || (connection === null || connection === void 0 ? void 0 : connection.clientID),
|
597
|
+
providerName: (_e = connection === null || connection === void 0 ? void 0 : connection.idpMetadata) === null || _e === void 0 ? void 0 : _e.provider,
|
544
598
|
redirectUri: isIdPFlow ? connection === null || connection === void 0 ? void 0 : connection.defaultRedirectUrl : session === null || session === void 0 ? void 0 : session.redirect_uri,
|
545
599
|
issuer,
|
546
600
|
isSAMLFederated: !!isSAMLFederated,
|
547
601
|
isIdPFlow: !!isIdPFlow,
|
548
|
-
requestedOIDCFlow: !!((
|
549
|
-
acsUrl: (
|
550
|
-
entityId: (
|
602
|
+
requestedOIDCFlow: !!((_f = session === null || session === void 0 ? void 0 : session.requested) === null || _f === void 0 ? void 0 : _f.oidc),
|
603
|
+
acsUrl: (_g = session === null || session === void 0 ? void 0 : session.requested) === null || _g === void 0 ? void 0 : _g.acsUrl,
|
604
|
+
entityId: (_h = session === null || session === void 0 ? void 0 : session.requested) === null || _h === void 0 ? void 0 : _h.entityId,
|
551
605
|
relayState: RelayState,
|
552
606
|
},
|
553
607
|
});
|
@@ -573,7 +627,7 @@ class OAuthController {
|
|
573
627
|
return { redirect_url: redirect.success(redirect_uri, params) };
|
574
628
|
}
|
575
629
|
catch (err) {
|
576
|
-
const error_description = (0,
|
630
|
+
const error_description = (0, utils_2.getErrorMessage)(err);
|
577
631
|
// Trace the error
|
578
632
|
const traceId = yield this.ssoTracer.saveTrace({
|
579
633
|
error: error_description,
|
@@ -582,7 +636,7 @@ class OAuthController {
|
|
582
636
|
tenant: connection.tenant,
|
583
637
|
product: connection.product,
|
584
638
|
clientID: connection.clientID,
|
585
|
-
providerName: (
|
639
|
+
providerName: (_j = connection === null || connection === void 0 ? void 0 : connection.idpMetadata) === null || _j === void 0 ? void 0 : _j.provider,
|
586
640
|
redirectUri: isIdPFlow ? connection === null || connection === void 0 ? void 0 : connection.defaultRedirectUrl : session === null || session === void 0 ? void 0 : session.redirect_uri,
|
587
641
|
isSAMLFederated,
|
588
642
|
isIdPFlow,
|
@@ -598,22 +652,23 @@ class OAuthController {
|
|
598
652
|
throw err;
|
599
653
|
}
|
600
654
|
return {
|
601
|
-
redirect_url: (0,
|
655
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
602
656
|
error: 'access_denied',
|
603
657
|
error_description: traceId ? `${traceId}: ${error_description}` : error_description,
|
604
658
|
redirect_uri,
|
605
|
-
state: (
|
659
|
+
state: (_k = session.requested) === null || _k === void 0 ? void 0 : _k.state,
|
606
660
|
}),
|
607
661
|
};
|
608
662
|
}
|
609
663
|
});
|
610
664
|
}
|
611
665
|
oidcAuthzResponse(body) {
|
612
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
666
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
613
667
|
return __awaiter(this, void 0, void 0, function* () {
|
614
668
|
let oidcConnection;
|
615
669
|
let session;
|
616
670
|
let isSAMLFederated;
|
671
|
+
let isOIDCFederated;
|
617
672
|
let redirect_uri;
|
618
673
|
let profile;
|
619
674
|
const callbackParams = body;
|
@@ -622,12 +677,13 @@ class OAuthController {
|
|
622
677
|
if (!RelayState) {
|
623
678
|
throw new error_1.JacksonError('State from original request is missing.', 403);
|
624
679
|
}
|
625
|
-
RelayState = RelayState.replace(
|
680
|
+
RelayState = RelayState.replace(utils_2.relayStatePrefix, '');
|
626
681
|
session = yield this.sessionStore.get(RelayState);
|
627
682
|
if (!session) {
|
628
683
|
throw new error_1.JacksonError('Unable to validate state from the original request.', 403);
|
629
684
|
}
|
630
685
|
isSAMLFederated = session && 'samlFederated' in session;
|
686
|
+
isOIDCFederated = session && 'oidcFederated' in session;
|
631
687
|
oidcConnection = yield this.connectionStore.get(session.id);
|
632
688
|
if (!oidcConnection) {
|
633
689
|
throw new error_1.JacksonError('OIDC connection not found.', 403);
|
@@ -638,24 +694,32 @@ class OAuthController {
|
|
638
694
|
throw new error_1.JacksonError('Redirect URL from the authorization request could not be retrieved', 403);
|
639
695
|
}
|
640
696
|
if (redirect_uri && !allowed.redirect(redirect_uri, oidcConnection.redirectUrl)) {
|
641
|
-
|
697
|
+
if (isOIDCFederated) {
|
698
|
+
if (!allowed.redirect(redirect_uri, (_a = session.oidcFederated) === null || _a === void 0 ? void 0 : _a.redirectUrl)) {
|
699
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
700
|
+
}
|
701
|
+
}
|
702
|
+
else {
|
703
|
+
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
704
|
+
}
|
642
705
|
}
|
643
706
|
}
|
644
707
|
}
|
645
708
|
catch (err) {
|
646
709
|
yield this.ssoTracer.saveTrace({
|
647
|
-
error: (0,
|
710
|
+
error: (0, utils_2.getErrorMessage)(err),
|
648
711
|
context: {
|
649
|
-
tenant: ((
|
650
|
-
product: ((
|
651
|
-
clientID: ((
|
652
|
-
providerName: (
|
653
|
-
acsUrl: (
|
654
|
-
entityId: (
|
712
|
+
tenant: ((_b = session === null || session === void 0 ? void 0 : session.requested) === null || _b === void 0 ? void 0 : _b.tenant) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.tenant),
|
713
|
+
product: ((_c = session === null || session === void 0 ? void 0 : session.requested) === null || _c === void 0 ? void 0 : _c.product) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.product),
|
714
|
+
clientID: ((_d = session === null || session === void 0 ? void 0 : session.requested) === null || _d === void 0 ? void 0 : _d.client_id) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.clientID),
|
715
|
+
providerName: (_e = oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.oidcProvider) === null || _e === void 0 ? void 0 : _e.provider,
|
716
|
+
acsUrl: (_f = session === null || session === void 0 ? void 0 : session.requested) === null || _f === void 0 ? void 0 : _f.acsUrl,
|
717
|
+
entityId: (_g = session === null || session === void 0 ? void 0 : session.requested) === null || _g === void 0 ? void 0 : _g.entityId,
|
655
718
|
redirectUri: redirect_uri,
|
656
719
|
relayState: RelayState,
|
657
720
|
isSAMLFederated: !!isSAMLFederated,
|
658
|
-
|
721
|
+
isOIDCFederated: !!isOIDCFederated,
|
722
|
+
requestedOIDCFlow: !!((_h = session === null || session === void 0 ? void 0 : session.requested) === null || _h === void 0 ? void 0 : _h.oidc),
|
659
723
|
},
|
660
724
|
});
|
661
725
|
// Rethrow err and redirect to Jackson error page
|
@@ -677,7 +741,7 @@ class OAuthController {
|
|
677
741
|
nonce: session.oidcNonce,
|
678
742
|
state: callbackParams.state,
|
679
743
|
});
|
680
|
-
profile = yield (0,
|
744
|
+
profile = yield (0, utils_2.extractOIDCUserProfile)(tokenSet, oidcClient);
|
681
745
|
if (isSAMLFederated) {
|
682
746
|
const { responseForm } = yield this.ssoHandler.createSAMLResponse({ profile, session });
|
683
747
|
yield this.sessionStore.delete(RelayState);
|
@@ -695,7 +759,7 @@ class OAuthController {
|
|
695
759
|
}
|
696
760
|
catch (err) {
|
697
761
|
const { error, error_description, error_uri, session_state, scope, stack } = err;
|
698
|
-
const error_message = (0,
|
762
|
+
const error_message = (0, utils_2.getErrorMessage)(err);
|
699
763
|
const traceId = yield this.ssoTracer.saveTrace({
|
700
764
|
error: error_message,
|
701
765
|
context: {
|
@@ -706,6 +770,7 @@ class OAuthController {
|
|
706
770
|
redirectUri: redirect_uri,
|
707
771
|
relayState: RelayState,
|
708
772
|
isSAMLFederated: !!isSAMLFederated,
|
773
|
+
isOIDCFederated: !!isOIDCFederated,
|
709
774
|
acsUrl: session.requested.acsUrl,
|
710
775
|
entityId: session.requested.entityId,
|
711
776
|
requestedOIDCFlow: !!session.requested.oidc,
|
@@ -723,7 +788,7 @@ class OAuthController {
|
|
723
788
|
throw err;
|
724
789
|
}
|
725
790
|
return {
|
726
|
-
redirect_url: (0,
|
791
|
+
redirect_url: (0, utils_2.OAuthErrorResponse)({
|
727
792
|
error: error || 'server_error',
|
728
793
|
error_description: traceId ? `${traceId}: ${error_message}` : error_message,
|
729
794
|
redirect_uri: redirect_uri,
|
@@ -852,7 +917,7 @@ class OAuthController {
|
|
852
917
|
else if (client_id && client_secret) {
|
853
918
|
// check if we have an encoded client_id
|
854
919
|
if (client_id !== 'dummy') {
|
855
|
-
const sp = (0,
|
920
|
+
const sp = (0, utils_2.getEncodedTenantProduct)(client_id);
|
856
921
|
if (!sp) {
|
857
922
|
// OAuth flow
|
858
923
|
if (client_id !== codeVal.clientID || client_secret !== codeVal.clientSecret) {
|
@@ -886,12 +951,12 @@ class OAuthController {
|
|
886
951
|
const requestHasNonce = !!((_f = codeVal.requested) === null || _f === void 0 ? void 0 : _f.nonce);
|
887
952
|
if (requestedOIDCFlow) {
|
888
953
|
const { jwtSigningKeys, jwsAlg } = (_g = this.opts.openid) !== null && _g !== void 0 ? _g : {};
|
889
|
-
if (!jwtSigningKeys || !(0,
|
954
|
+
if (!jwtSigningKeys || !(0, utils_2.isJWSKeyPairLoaded)(jwtSigningKeys)) {
|
890
955
|
throw new error_1.JacksonError('JWT signing keys are not loaded', 500);
|
891
956
|
}
|
892
957
|
let claims = requestHasNonce ? { nonce: codeVal.requested.nonce } : {};
|
893
958
|
claims = Object.assign(Object.assign({}, claims), { id: codeVal.profile.claims.id, email: codeVal.profile.claims.email, firstName: codeVal.profile.claims.firstName, lastName: codeVal.profile.claims.lastName, roles: codeVal.profile.claims.roles, groups: codeVal.profile.claims.groups });
|
894
|
-
const signingKey = yield (0,
|
959
|
+
const signingKey = yield (0, utils_2.loadJWSPrivateKey)(jwtSigningKeys.private, jwsAlg);
|
895
960
|
const id_token = yield new jose.SignJWT(claims)
|
896
961
|
.setProtectedHeader({ alg: jwsAlg })
|
897
962
|
.setIssuedAt()
|