@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.
@@ -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 && rUrl.hostname === url.hostname && rUrl.port === url.port) {
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,oDAAoD;QAEpD,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;YAC/F,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAnBW,QAAA,QAAQ,YAmBnB"}
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
- constructor({ connectionStore, sessionStore, codeStore, tokenStore, ssoTracer, opts }: {
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;
@@ -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, utils_1.getScopeValues)(scope);
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, utils_1.getEncodedTenantProduct)(client_id);
114
+ let sp = (0, utils_2.getEncodedTenantProduct)(client_id);
112
115
  if (!sp && access_type) {
113
- sp = (0, utils_1.getEncodedTenantProduct)(access_type);
116
+ sp = (0, utils_2.getEncodedTenantProduct)(access_type);
114
117
  }
115
118
  if (!sp && resource) {
116
- sp = (0, utils_1.getEncodedTenantProduct)(resource);
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, utils_1.getEncodedTenantProduct)(encodedParams);
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 = yield this.connectionStore.get(client_id);
146
- if (connection) {
147
- requestedTenant = connection.tenant;
148
- requestedProduct = connection.product;
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
- throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
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, utils_1.getErrorMessage)(err);
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, utils_1.isConnectionActive)(connection)) {
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, utils_1.isJWSKeyPairLoaded)(this.opts.openid.jwtSigningKeys));
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, utils_1.OAuthErrorResponse)({
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 = utils_1.relayStatePrefix + sessionId;
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, utils_1.OAuthErrorResponse)({
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, utils_1.getErrorMessage)(err);
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, utils_1.OAuthErrorResponse)({
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, utils_1.OAuthErrorResponse)({
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, utils_1.OAuthErrorResponse)({
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, utils_1.getErrorMessage)(err),
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, utils_1.getErrorMessage)(err);
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, utils_1.OAuthErrorResponse)({
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(utils_1.relayStatePrefix);
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(utils_1.relayStatePrefix, '');
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: utils_1.IndexNames.EntityID,
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
- throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
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, utils_1.getErrorMessage)(err),
591
+ error: (0, utils_2.getErrorMessage)(err),
538
592
  context: {
539
593
  samlResponse: rawResponse,
540
- tenant: ((_a = session === null || session === void 0 ? void 0 : session.requested) === null || _a === void 0 ? void 0 : _a.tenant) || (connection === null || connection === void 0 ? void 0 : connection.tenant),
541
- product: ((_b = session === null || session === void 0 ? void 0 : session.requested) === null || _b === void 0 ? void 0 : _b.product) || (connection === null || connection === void 0 ? void 0 : connection.product),
542
- clientID: ((_c = session === null || session === void 0 ? void 0 : session.requested) === null || _c === void 0 ? void 0 : _c.client_id) || (connection === null || connection === void 0 ? void 0 : connection.clientID),
543
- providerName: (_d = connection === null || connection === void 0 ? void 0 : connection.idpMetadata) === null || _d === void 0 ? void 0 : _d.provider,
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: !!((_e = session === null || session === void 0 ? void 0 : session.requested) === null || _e === void 0 ? void 0 : _e.oidc),
549
- acsUrl: (_f = session === null || session === void 0 ? void 0 : session.requested) === null || _f === void 0 ? void 0 : _f.acsUrl,
550
- entityId: (_g = session === null || session === void 0 ? void 0 : session.requested) === null || _g === void 0 ? void 0 : _g.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, utils_1.getErrorMessage)(err);
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: (_h = connection === null || connection === void 0 ? void 0 : connection.idpMetadata) === null || _h === void 0 ? void 0 : _h.provider,
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, utils_1.OAuthErrorResponse)({
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: (_j = session.requested) === null || _j === void 0 ? void 0 : _j.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(utils_1.relayStatePrefix, '');
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
- throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
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, utils_1.getErrorMessage)(err),
710
+ error: (0, utils_2.getErrorMessage)(err),
648
711
  context: {
649
- tenant: ((_a = session === null || session === void 0 ? void 0 : session.requested) === null || _a === void 0 ? void 0 : _a.tenant) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.tenant),
650
- product: ((_b = session === null || session === void 0 ? void 0 : session.requested) === null || _b === void 0 ? void 0 : _b.product) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.product),
651
- clientID: ((_c = session === null || session === void 0 ? void 0 : session.requested) === null || _c === void 0 ? void 0 : _c.client_id) || (oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.clientID),
652
- providerName: (_d = oidcConnection === null || oidcConnection === void 0 ? void 0 : oidcConnection.oidcProvider) === null || _d === void 0 ? void 0 : _d.provider,
653
- acsUrl: (_e = session === null || session === void 0 ? void 0 : session.requested) === null || _e === void 0 ? void 0 : _e.acsUrl,
654
- entityId: (_f = session === null || session === void 0 ? void 0 : session.requested) === null || _f === void 0 ? void 0 : _f.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
- requestedOIDCFlow: !!((_g = session === null || session === void 0 ? void 0 : session.requested) === null || _g === void 0 ? void 0 : _g.oidc),
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, utils_1.extractOIDCUserProfile)(tokenSet, oidcClient);
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, utils_1.getErrorMessage)(err);
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, utils_1.OAuthErrorResponse)({
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, utils_1.getEncodedTenantProduct)(client_id);
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, utils_1.isJWSKeyPairLoaded)(jwtSigningKeys)) {
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, utils_1.loadJWSPrivateKey)(jwtSigningKeys.private, jwsAlg);
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()