@boxyhq/saml-jackson 1.20.1 → 1.20.2-beta.10579
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/controller/oauth/allowed.js +13 -1
- package/dist/controller/oauth/allowed.js.map +1 -1
- package/dist/controller/oauth/code-verifier.d.ts +0 -1
- package/dist/controller/oauth/code-verifier.js +2 -3
- package/dist/controller/oauth/code-verifier.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 +1 -0
- 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/directory-sync/scim/transform.d.ts +0 -5
- package/dist/directory-sync/scim/transform.js +5 -8
- package/dist/directory-sync/scim/transform.js.map +1 -1
- package/dist/ee/federated-saml/app.d.ts +28 -14
- 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 +5 -0
- package/dist/ee/product/index.js +3 -3
- package/dist/ee/product/index.js.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/saml/x509.d.ts +0 -4
- package/dist/saml/x509.js +2 -3
- package/dist/saml/x509.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"}
|
@@ -3,14 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.encode =
|
6
|
+
exports.encode = void 0;
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
8
8
|
const transformBase64 = (input) => {
|
9
9
|
return input.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
10
10
|
};
|
11
|
-
exports.transformBase64 = transformBase64;
|
12
11
|
const encode = (code_challenge) => {
|
13
|
-
return
|
12
|
+
return transformBase64(crypto_1.default.createHash('sha256').update(code_challenge).digest('base64'));
|
14
13
|
};
|
15
14
|
exports.encode = encode;
|
16
15
|
//# sourceMappingURL=code-verifier.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"code-verifier.js","sourceRoot":"","sources":["../../../src/controller/oauth/code-verifier.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;
|
1
|
+
{"version":3,"file":"code-verifier.js","sourceRoot":"","sources":["../../../src/controller/oauth/code-verifier.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B,MAAM,eAAe,GAAG,CAAC,KAAa,EAAU,EAAE;IAChD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACzE,CAAC,CAAC;AAEK,MAAM,MAAM,GAAG,CAAC,cAAsB,EAAU,EAAE;IACvD,OAAO,eAAe,CAAC,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC9F,CAAC,CAAC;AAFW,QAAA,MAAM,UAEjB"}
|
@@ -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()
|