@boxyhq/saml-jackson 1.3.12 → 1.5.0
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/analytics.d.ts +12 -0
- package/dist/controller/analytics.js +66 -0
- package/dist/controller/analytics.js.map +1 -0
- package/dist/controller/api.d.ts +2 -1
- package/dist/controller/api.js +21 -0
- package/dist/controller/api.js.map +1 -1
- package/dist/controller/connection/oidc.js +1 -1
- package/dist/controller/connection/oidc.js.map +1 -1
- package/dist/controller/connection/saml.js +24 -3
- package/dist/controller/connection/saml.js.map +1 -1
- package/dist/controller/oauth.d.ts +3 -2
- package/dist/controller/oauth.js +133 -281
- package/dist/controller/oauth.js.map +1 -1
- package/dist/controller/saml-handler.d.ts +38 -0
- package/dist/controller/saml-handler.js +166 -0
- package/dist/controller/saml-handler.js.map +1 -0
- package/dist/controller/setup-link.d.ts +12 -0
- package/dist/controller/setup-link.js +134 -0
- package/dist/controller/setup-link.js.map +1 -0
- package/dist/controller/utils.d.ts +16 -1
- package/dist/controller/utils.js +48 -3
- package/dist/controller/utils.js.map +1 -1
- package/dist/db/mem.js +6 -2
- package/dist/db/mem.js.map +1 -1
- package/dist/db/utils.js +0 -1
- package/dist/db/utils.js.map +1 -1
- package/dist/directory-sync/Base.js +2 -2
- package/dist/directory-sync/Base.js.map +1 -1
- package/dist/directory-sync/WebhookEventsLogger.d.ts +4 -1
- package/dist/directory-sync/WebhookEventsLogger.js +3 -3
- package/dist/directory-sync/WebhookEventsLogger.js.map +1 -1
- package/dist/ee/common/checkLicense.d.ts +2 -0
- package/dist/ee/common/checkLicense.js +19 -0
- package/dist/ee/common/checkLicense.js.map +1 -0
- package/dist/ee/federated-saml/app.d.ts +19 -0
- package/dist/ee/federated-saml/app.js +126 -0
- package/dist/ee/federated-saml/app.js.map +1 -0
- package/dist/ee/federated-saml/index.d.ts +12 -0
- package/dist/ee/federated-saml/index.js +56 -0
- package/dist/ee/federated-saml/index.js.map +1 -0
- package/dist/ee/federated-saml/sso.d.ts +17 -0
- package/dist/ee/federated-saml/sso.js +76 -0
- package/dist/ee/federated-saml/sso.js.map +1 -0
- package/dist/ee/federated-saml/types.d.ts +18 -0
- package/dist/ee/federated-saml/types.js +3 -0
- package/dist/ee/federated-saml/types.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/saml/lib.d.ts +31 -0
- package/dist/saml/lib.js +217 -0
- package/dist/saml/lib.js.map +1 -0
- package/dist/typings.d.ts +37 -4
- package/dist/typings.js +15 -0
- package/dist/typings.js.map +1 -1
- package/package.json +15 -14
package/dist/controller/oauth.js
CHANGED
@@ -37,55 +37,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
38
38
|
exports.OAuthController = void 0;
|
39
39
|
const crypto_1 = __importDefault(require("crypto"));
|
40
|
+
const jose = __importStar(require("jose"));
|
40
41
|
const util_1 = require("util");
|
41
42
|
const zlib_1 = require("zlib");
|
43
|
+
const saml20_1 = __importDefault(require("@boxyhq/saml20"));
|
42
44
|
const openid_client_1 = require("openid-client");
|
43
|
-
const
|
44
|
-
const dbutils = __importStar(require("../db/utils"));
|
45
|
+
const utils_1 = require("./utils");
|
45
46
|
const metrics = __importStar(require("../opentelemetry/metrics"));
|
46
|
-
const saml20_1 = __importDefault(require("@boxyhq/saml20"));
|
47
|
-
const claims_1 = __importDefault(require("../saml/claims"));
|
48
47
|
const error_1 = require("./error");
|
49
48
|
const allowed = __importStar(require("./oauth/allowed"));
|
50
49
|
const codeVerifier = __importStar(require("./oauth/code-verifier"));
|
51
50
|
const redirect = __importStar(require("./oauth/redirect"));
|
52
|
-
const utils_1 = require("./utils");
|
53
51
|
const x509_1 = require("../saml/x509");
|
52
|
+
const saml_handler_1 = require("./saml-handler");
|
53
|
+
const lib_1 = require("../saml/lib");
|
54
54
|
const deflateRawAsync = (0, util_1.promisify)(zlib_1.deflateRaw);
|
55
|
-
const validateSAMLResponse = (rawResponse, validateOpts) => __awaiter(void 0, void 0, void 0, function* () {
|
56
|
-
const profile = yield saml20_1.default.validate(rawResponse, validateOpts);
|
57
|
-
if (profile && profile.claims) {
|
58
|
-
// we map claims to our attributes id, email, firstName, lastName where possible. We also map original claims to raw
|
59
|
-
profile.claims = claims_1.default.map(profile.claims);
|
60
|
-
// some providers don't return the id in the assertion, we set it to a sha256 hash of the email
|
61
|
-
if (!profile.claims.id && profile.claims.email) {
|
62
|
-
profile.claims.id = crypto_1.default.createHash('sha256').update(profile.claims.email).digest('hex');
|
63
|
-
}
|
64
|
-
// we'll send a ripemd160 hash of the id, this can be used in the case of email missing it can be used as the local part
|
65
|
-
profile.claims.idHash = dbutils.keyDigest(profile.claims.id);
|
66
|
-
}
|
67
|
-
return profile;
|
68
|
-
});
|
69
|
-
function getEncodedTenantProduct(param) {
|
70
|
-
try {
|
71
|
-
const sp = new URLSearchParams(param);
|
72
|
-
const tenant = sp.get('tenant');
|
73
|
-
const product = sp.get('product');
|
74
|
-
if (tenant && product) {
|
75
|
-
return {
|
76
|
-
tenant: sp.get('tenant'),
|
77
|
-
product: sp.get('product'),
|
78
|
-
};
|
79
|
-
}
|
80
|
-
return null;
|
81
|
-
}
|
82
|
-
catch (err) {
|
83
|
-
return null;
|
84
|
-
}
|
85
|
-
}
|
86
|
-
function getScopeValues(scope) {
|
87
|
-
return typeof scope === 'string' ? scope.split(' ').filter((s) => s.length > 0) : [];
|
88
|
-
}
|
89
55
|
class OAuthController {
|
90
56
|
constructor({ connectionStore, sessionStore, codeStore, tokenStore, opts }) {
|
91
57
|
this.connectionStore = connectionStore;
|
@@ -93,53 +59,11 @@ class OAuthController {
|
|
93
59
|
this.codeStore = codeStore;
|
94
60
|
this.tokenStore = tokenStore;
|
95
61
|
this.opts = opts;
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
}
|
102
|
-
else if (this.opts.idpDiscoveryPath) {
|
103
|
-
if (!isIdpFlow) {
|
104
|
-
// redirect to IdP selection page
|
105
|
-
const idpList = connections.map(({ idpMetadata, oidcProvider, clientID, name }) => {
|
106
|
-
var _a;
|
107
|
-
return JSON.stringify({
|
108
|
-
provider: (_a = idpMetadata === null || idpMetadata === void 0 ? void 0 : idpMetadata.provider) !== null && _a !== void 0 ? _a : oidcProvider === null || oidcProvider === void 0 ? void 0 : oidcProvider.provider,
|
109
|
-
clientID,
|
110
|
-
name,
|
111
|
-
connectionIsSAML: idpMetadata && typeof idpMetadata === 'object',
|
112
|
-
connectionIsOIDC: oidcProvider && typeof oidcProvider === 'object',
|
113
|
-
});
|
114
|
-
});
|
115
|
-
return {
|
116
|
-
redirect_url: redirect.success(this.opts.externalUrl + this.opts.idpDiscoveryPath, Object.assign(Object.assign({}, originalParams), { idp: idpList })),
|
117
|
-
};
|
118
|
-
}
|
119
|
-
else {
|
120
|
-
// Relevant to IdP initiated SAML flow
|
121
|
-
const appList = connections.map(({ product, name, description, clientID }) => ({
|
122
|
-
product,
|
123
|
-
name,
|
124
|
-
description,
|
125
|
-
clientID,
|
126
|
-
}));
|
127
|
-
return {
|
128
|
-
app_select_form: saml20_1.default.createPostForm(this.opts.idpDiscoveryPath, [
|
129
|
-
{
|
130
|
-
name: 'SAMLResponse',
|
131
|
-
value: originalParams.SAMLResponse,
|
132
|
-
},
|
133
|
-
{
|
134
|
-
name: 'app',
|
135
|
-
value: encodeURIComponent(JSON.stringify(appList)),
|
136
|
-
},
|
137
|
-
]),
|
138
|
-
};
|
139
|
-
}
|
140
|
-
}
|
141
|
-
}
|
142
|
-
return {};
|
62
|
+
this.samlHandler = new saml_handler_1.SAMLHandler({
|
63
|
+
connection: connectionStore,
|
64
|
+
session: sessionStore,
|
65
|
+
opts,
|
66
|
+
});
|
143
67
|
}
|
144
68
|
authorize(body) {
|
145
69
|
var _a;
|
@@ -156,85 +80,58 @@ class OAuthController {
|
|
156
80
|
throw new error_1.JacksonError('Please specify a redirect URL.', 400);
|
157
81
|
}
|
158
82
|
let connection;
|
159
|
-
const requestedScopes = getScopeValues(scope);
|
83
|
+
const requestedScopes = (0, utils_1.getScopeValues)(scope);
|
160
84
|
const requestedOIDCFlow = requestedScopes.includes('openid');
|
161
85
|
if (tenant && product) {
|
162
|
-
const
|
163
|
-
name: utils_1.IndexNames.TenantProduct,
|
164
|
-
value: dbutils.keyFromParts(tenant, product),
|
165
|
-
});
|
166
|
-
if (!connections || connections.length === 0) {
|
167
|
-
throw new error_1.JacksonError('IdP connection not found.', 403);
|
168
|
-
}
|
169
|
-
connection = connections[0];
|
170
|
-
// Support multiple matches
|
171
|
-
const { resolvedConnection, redirect_url } = this.resolveMultipleConnectionMatches(connections, idp_hint, {
|
172
|
-
response_type,
|
173
|
-
client_id,
|
174
|
-
redirect_uri,
|
175
|
-
state,
|
86
|
+
const response = yield this.samlHandler.resolveConnection({
|
176
87
|
tenant,
|
177
88
|
product,
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
nonce,
|
182
|
-
code_challenge,
|
183
|
-
code_challenge_method,
|
89
|
+
idp_hint,
|
90
|
+
authFlow: 'oauth',
|
91
|
+
originalParams: Object.assign({}, body),
|
184
92
|
});
|
185
|
-
if (
|
186
|
-
return {
|
93
|
+
if ('redirectUrl' in response) {
|
94
|
+
return {
|
95
|
+
redirect_url: response.redirectUrl,
|
96
|
+
};
|
187
97
|
}
|
188
|
-
if (
|
189
|
-
connection =
|
98
|
+
if ('connection' in response) {
|
99
|
+
connection = response.connection;
|
190
100
|
}
|
191
101
|
}
|
192
102
|
else if (client_id && client_id !== '' && client_id !== 'undefined' && client_id !== 'null') {
|
193
103
|
// if tenant and product are encoded in the client_id then we parse it and check for the relevant connection(s)
|
194
|
-
let sp = getEncodedTenantProduct(client_id);
|
104
|
+
let sp = (0, utils_1.getEncodedTenantProduct)(client_id);
|
195
105
|
if (!sp && access_type) {
|
196
|
-
sp = getEncodedTenantProduct(access_type);
|
106
|
+
sp = (0, utils_1.getEncodedTenantProduct)(access_type);
|
197
107
|
}
|
198
108
|
if (!sp && resource) {
|
199
|
-
sp = getEncodedTenantProduct(resource);
|
109
|
+
sp = (0, utils_1.getEncodedTenantProduct)(resource);
|
200
110
|
}
|
201
111
|
if (!sp && requestedScopes) {
|
202
112
|
const encodedParams = requestedScopes.find((scope) => scope.includes('=') && scope.includes('&')); // for now assume only one encoded param i.e. for tenant/product
|
203
113
|
if (encodedParams) {
|
204
|
-
sp = getEncodedTenantProduct(encodedParams);
|
114
|
+
sp = (0, utils_1.getEncodedTenantProduct)(encodedParams);
|
205
115
|
}
|
206
116
|
}
|
207
117
|
if (sp && sp.tenant && sp.product) {
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
value: dbutils.keyFromParts(sp.tenant, sp.product),
|
213
|
-
});
|
214
|
-
if (!connections || connections.length === 0) {
|
215
|
-
throw new error_1.JacksonError('IdP connection not found.', 403);
|
216
|
-
}
|
217
|
-
connection = connections[0];
|
218
|
-
// Support multiple matches
|
219
|
-
const { resolvedConnection, redirect_url } = this.resolveMultipleConnectionMatches(connections, idp_hint, {
|
220
|
-
response_type,
|
221
|
-
client_id,
|
222
|
-
redirect_uri,
|
223
|
-
state,
|
118
|
+
const { tenant, product } = sp;
|
119
|
+
requestedTenant = tenant;
|
120
|
+
requestedProduct = product;
|
121
|
+
const response = yield this.samlHandler.resolveConnection({
|
224
122
|
tenant,
|
225
123
|
product,
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
nonce,
|
230
|
-
code_challenge,
|
231
|
-
code_challenge_method,
|
124
|
+
idp_hint,
|
125
|
+
authFlow: 'oauth',
|
126
|
+
originalParams: Object.assign({}, body),
|
232
127
|
});
|
233
|
-
if (
|
234
|
-
return {
|
128
|
+
if ('redirectUrl' in response) {
|
129
|
+
return {
|
130
|
+
redirect_url: response.redirectUrl,
|
131
|
+
};
|
235
132
|
}
|
236
|
-
if (
|
237
|
-
connection =
|
133
|
+
if ('connection' in response) {
|
134
|
+
connection = response.connection;
|
238
135
|
}
|
239
136
|
}
|
240
137
|
else {
|
@@ -286,14 +183,14 @@ class OAuthController {
|
|
286
183
|
// Connection retrieved: Handover to IdP starts here
|
287
184
|
let ssoUrl;
|
288
185
|
let post = false;
|
289
|
-
const connectionIsSAML =
|
290
|
-
const connectionIsOIDC =
|
186
|
+
const connectionIsSAML = 'idpMetadata' in connection && connection.idpMetadata !== undefined;
|
187
|
+
const connectionIsOIDC = 'oidcProvider' in connection && connection.oidcProvider !== undefined;
|
291
188
|
// Init sessionId
|
292
189
|
const sessionId = crypto_1.default.randomBytes(16).toString('hex');
|
293
190
|
const relayState = utils_1.relayStatePrefix + sessionId;
|
294
191
|
// SAML connection: SAML request will be constructed here
|
295
192
|
let samlReq;
|
296
|
-
if (
|
193
|
+
if ('idpMetadata' in connection) {
|
297
194
|
const { sso } = connection.idpMetadata;
|
298
195
|
if ('redirectUrl' in sso) {
|
299
196
|
// HTTP Redirect binding
|
@@ -342,7 +239,7 @@ class OAuthController {
|
|
342
239
|
}
|
343
240
|
// OIDC Connection: Issuer discovery, openid-client init and extraction of authorization endpoint happens here
|
344
241
|
let oidcCodeVerifier;
|
345
|
-
if (connectionIsOIDC) {
|
242
|
+
if (connectionIsOIDC && 'oidcProvider' in connection) {
|
346
243
|
if (!this.opts.oidcPath) {
|
347
244
|
return {
|
348
245
|
redirect_url: (0, utils_1.OAuthErrorResponse)({
|
@@ -473,148 +370,114 @@ class OAuthController {
|
|
473
370
|
});
|
474
371
|
}
|
475
372
|
samlResponse(body) {
|
476
|
-
var _a
|
373
|
+
var _a;
|
477
374
|
return __awaiter(this, void 0, void 0, function* () {
|
478
|
-
const { SAMLResponse, idp_hint } = body;
|
479
|
-
let RelayState = body.RelayState || ''; // RelayState will contain the sessionId from earlier quasi-oauth flow
|
375
|
+
const { SAMLResponse, idp_hint, RelayState = '' } = body;
|
480
376
|
const isIdPFlow = !RelayState.startsWith(utils_1.relayStatePrefix);
|
377
|
+
// IdP is disabled so block the request
|
481
378
|
if (!this.opts.idpEnabled && isIdPFlow) {
|
482
|
-
//
|
379
|
+
// IdP login is disabled so block the request
|
483
380
|
throw new error_1.JacksonError('IdP (Identity Provider) flow has been disabled. Please head to your Service Provider to login.', 403);
|
484
381
|
}
|
485
|
-
|
382
|
+
const sessionId = RelayState.replace(utils_1.relayStatePrefix, '');
|
486
383
|
const rawResponse = Buffer.from(SAMLResponse, 'base64').toString();
|
487
384
|
const issuer = saml20_1.default.parseIssuer(rawResponse);
|
488
385
|
if (!issuer) {
|
489
386
|
throw new error_1.JacksonError('Issuer not found.', 403);
|
490
387
|
}
|
491
|
-
const
|
388
|
+
const connections = yield this.connectionStore.getByIndex({
|
492
389
|
name: utils_1.IndexNames.EntityID,
|
493
390
|
value: issuer,
|
494
391
|
});
|
495
|
-
if (!
|
392
|
+
if (!connections || connections.length === 0) {
|
496
393
|
throw new error_1.JacksonError('SAML connection not found.', 403);
|
497
394
|
}
|
498
|
-
|
395
|
+
const session = sessionId ? yield this.sessionStore.get(sessionId) : null;
|
396
|
+
if (!isIdPFlow && !session) {
|
397
|
+
throw new error_1.JacksonError('Unable to validate state from the origin request.', 403);
|
398
|
+
}
|
399
|
+
const isSAMLFederated = session && 'samlFederated' in session;
|
400
|
+
const isSPFflow = !isIdPFlow && !isSAMLFederated;
|
401
|
+
let connection;
|
402
|
+
// IdP initiated SSO flow
|
499
403
|
if (isIdPFlow) {
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
}
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
}
|
527
|
-
if (!
|
404
|
+
const response = yield this.samlHandler.resolveConnection({
|
405
|
+
idp_hint,
|
406
|
+
authFlow: 'idp-initiated',
|
407
|
+
entityId: issuer,
|
408
|
+
originalParams: {
|
409
|
+
SAMLResponse,
|
410
|
+
},
|
411
|
+
});
|
412
|
+
// Redirect to the product selection page
|
413
|
+
if ('postForm' in response) {
|
414
|
+
return {
|
415
|
+
app_select_form: response.postForm,
|
416
|
+
};
|
417
|
+
}
|
418
|
+
// Found a connection
|
419
|
+
if ('connection' in response) {
|
420
|
+
connection = response.connection;
|
421
|
+
}
|
422
|
+
}
|
423
|
+
// SP initiated SSO flow
|
424
|
+
// Resolve if there are multiple matches for SP login
|
425
|
+
if (isSPFflow) {
|
426
|
+
connection = connections.filter((c) => {
|
427
|
+
return (c.clientID === session.requested.client_id ||
|
428
|
+
(c.tenant === session.requested.tenant && c.product === session.requested.product));
|
429
|
+
})[0];
|
430
|
+
}
|
431
|
+
if (!connection) {
|
432
|
+
connection = connections[0];
|
433
|
+
}
|
434
|
+
if (!connection) {
|
528
435
|
throw new error_1.JacksonError('SAML connection not found.', 403);
|
529
436
|
}
|
530
|
-
const { privateKey } = yield (0, x509_1.getDefaultCertificate)();
|
531
|
-
const validateOpts = {
|
532
|
-
thumbprint: samlConnection.idpMetadata.thumbprint,
|
533
|
-
audience: this.opts.samlAudience,
|
534
|
-
privateKey,
|
535
|
-
};
|
536
437
|
if (session &&
|
537
438
|
session.redirect_uri &&
|
538
|
-
!allowed.redirect(session.redirect_uri,
|
439
|
+
!allowed.redirect(session.redirect_uri, connection.redirectUrl)) {
|
539
440
|
throw new error_1.JacksonError('Redirect URL is not allowed.', 403);
|
540
441
|
}
|
442
|
+
const { privateKey } = yield (0, x509_1.getDefaultCertificate)();
|
443
|
+
const validateOpts = {
|
444
|
+
thumbprint: `${connection.idpMetadata.thumbprint}`,
|
445
|
+
audience: `${this.opts.samlAudience}`,
|
446
|
+
privateKey,
|
447
|
+
};
|
541
448
|
if (session && session.id) {
|
542
|
-
validateOpts
|
449
|
+
validateOpts['inResponseTo'] = session.id;
|
543
450
|
}
|
544
|
-
|
545
|
-
|
451
|
+
const redirect_uri = (session && session.redirect_uri) || connection.defaultRedirectUrl;
|
452
|
+
let profile = null;
|
546
453
|
try {
|
547
|
-
profile = yield
|
454
|
+
profile = yield (0, lib_1.extractSAMLResponseAttributes)(rawResponse, validateOpts);
|
548
455
|
}
|
549
456
|
catch (err) {
|
550
|
-
// return error to redirect_uri
|
551
457
|
return {
|
552
458
|
redirect_url: (0, utils_1.OAuthErrorResponse)({
|
553
459
|
error: 'access_denied',
|
554
460
|
error_description: (0, utils_1.getErrorMessage)(err),
|
555
461
|
redirect_uri,
|
556
|
-
state: (_a = session
|
462
|
+
state: (_a = session.requested) === null || _a === void 0 ? void 0 : _a.state,
|
557
463
|
}),
|
558
464
|
};
|
559
465
|
}
|
560
|
-
//
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
clientSecret: samlConnection.clientSecret,
|
566
|
-
requested: session === null || session === void 0 ? void 0 : session.requested,
|
567
|
-
isIdPFlow,
|
568
|
-
};
|
569
|
-
if (session) {
|
570
|
-
codeVal.session = session;
|
571
|
-
}
|
572
|
-
try {
|
573
|
-
yield this.codeStore.put(code, codeVal);
|
574
|
-
}
|
575
|
-
catch (err) {
|
576
|
-
// return error to redirect_uri
|
577
|
-
return {
|
578
|
-
redirect_url: (0, utils_1.OAuthErrorResponse)({
|
579
|
-
error: 'server_error',
|
580
|
-
error_description: (0, utils_1.getErrorMessage)(err),
|
581
|
-
redirect_uri,
|
582
|
-
state: (_b = session === null || session === void 0 ? void 0 : session.requested) === null || _b === void 0 ? void 0 : _b.state,
|
583
|
-
}),
|
584
|
-
};
|
466
|
+
// This is a federated SAML flow, let's create a new SAMLResponse and POST it to the SP
|
467
|
+
if (isSAMLFederated) {
|
468
|
+
const { responseForm } = yield this.samlHandler.createSAMLResponse({ profile, session });
|
469
|
+
yield this.sessionStore.delete(sessionId);
|
470
|
+
return { responseForm };
|
585
471
|
}
|
472
|
+
const code = yield this._buildAuthorizationCode(connection, profile, session, isIdPFlow);
|
586
473
|
const params = {
|
587
474
|
code,
|
588
475
|
};
|
589
476
|
if (session && session.state) {
|
590
|
-
params
|
591
|
-
}
|
592
|
-
const redirectUrl = redirect.success(redirect_uri, params);
|
593
|
-
// delete the session
|
594
|
-
try {
|
595
|
-
yield this.sessionStore.delete(RelayState);
|
477
|
+
params['state'] = session.state;
|
596
478
|
}
|
597
|
-
|
598
|
-
|
599
|
-
}
|
600
|
-
return { redirect_url: redirectUrl };
|
601
|
-
});
|
602
|
-
}
|
603
|
-
extractOIDCUserProfile(tokenSet, oidcClient) {
|
604
|
-
var _a, _b, _c, _d, _e;
|
605
|
-
return __awaiter(this, void 0, void 0, function* () {
|
606
|
-
const profile = { claims: {} };
|
607
|
-
const idTokenClaims = tokenSet.claims();
|
608
|
-
const userinfo = yield oidcClient.userinfo(tokenSet);
|
609
|
-
profile.claims.id = idTokenClaims.sub;
|
610
|
-
profile.claims.idHash = dbutils.keyDigest(idTokenClaims.sub);
|
611
|
-
profile.claims.email = (_a = idTokenClaims.email) !== null && _a !== void 0 ? _a : userinfo.email;
|
612
|
-
profile.claims.firstName = (_b = idTokenClaims.given_name) !== null && _b !== void 0 ? _b : userinfo.given_name;
|
613
|
-
profile.claims.lastName = (_c = idTokenClaims.family_name) !== null && _c !== void 0 ? _c : userinfo.family_name;
|
614
|
-
profile.claims.roles = (_d = idTokenClaims.roles) !== null && _d !== void 0 ? _d : userinfo.roles;
|
615
|
-
profile.claims.groups = (_e = idTokenClaims.groups) !== null && _e !== void 0 ? _e : userinfo.groups;
|
616
|
-
profile.claims.raw = userinfo;
|
617
|
-
return profile;
|
479
|
+
yield this.sessionStore.delete(sessionId);
|
480
|
+
return { redirect_url: redirect.success(redirect_uri, params) };
|
618
481
|
});
|
619
482
|
}
|
620
483
|
oidcAuthzResponse(body) {
|
@@ -668,7 +531,7 @@ class OAuthController {
|
|
668
531
|
const tokenSet = yield oidcClient.callback(this.opts.externalUrl + this.opts.oidcPath, {
|
669
532
|
code: opCode,
|
670
533
|
}, { code_verifier: session.oidcCodeVerifier });
|
671
|
-
profile = yield
|
534
|
+
profile = yield (0, utils_1.extractOIDCUserProfile)(tokenSet, oidcClient);
|
672
535
|
}
|
673
536
|
catch (err) {
|
674
537
|
if (err) {
|
@@ -682,48 +545,37 @@ class OAuthController {
|
|
682
545
|
};
|
683
546
|
}
|
684
547
|
}
|
685
|
-
|
686
|
-
const code = crypto_1.default.randomBytes(20).toString('hex');
|
687
|
-
const codeVal = {
|
688
|
-
profile,
|
689
|
-
clientID: oidcConnection.clientID,
|
690
|
-
clientSecret: oidcConnection.clientSecret,
|
691
|
-
requested: session === null || session === void 0 ? void 0 : session.requested,
|
692
|
-
};
|
693
|
-
if (session) {
|
694
|
-
codeVal.session = session;
|
695
|
-
}
|
696
|
-
try {
|
697
|
-
yield this.codeStore.put(code, codeVal);
|
698
|
-
}
|
699
|
-
catch (err) {
|
700
|
-
// return error to redirect_uri
|
701
|
-
return {
|
702
|
-
redirect_url: (0, utils_1.OAuthErrorResponse)({
|
703
|
-
error: 'server_error',
|
704
|
-
error_description: (0, utils_1.getErrorMessage)(err),
|
705
|
-
redirect_uri,
|
706
|
-
state: session.state,
|
707
|
-
}),
|
708
|
-
};
|
709
|
-
}
|
548
|
+
const code = yield this._buildAuthorizationCode(oidcConnection, profile, session, false);
|
710
549
|
const params = {
|
711
550
|
code,
|
712
551
|
};
|
713
552
|
if (session && session.state) {
|
714
|
-
params
|
553
|
+
params['state'] = session.state;
|
715
554
|
}
|
716
555
|
const redirectUrl = redirect.success(redirect_uri, params);
|
717
|
-
|
718
|
-
try {
|
719
|
-
yield this.sessionStore.delete(RelayState);
|
720
|
-
}
|
721
|
-
catch (_err) {
|
722
|
-
// ignore error
|
723
|
-
}
|
556
|
+
yield this.sessionStore.delete(RelayState);
|
724
557
|
return { redirect_url: redirectUrl };
|
725
558
|
});
|
726
559
|
}
|
560
|
+
// Build the authorization code for the session
|
561
|
+
_buildAuthorizationCode(connection, profile, session, isIdPFlow) {
|
562
|
+
return __awaiter(this, void 0, void 0, function* () {
|
563
|
+
// Store details against a code
|
564
|
+
const code = crypto_1.default.randomBytes(20).toString('hex');
|
565
|
+
const codeVal = {
|
566
|
+
profile,
|
567
|
+
clientID: connection.clientID,
|
568
|
+
clientSecret: connection.clientSecret,
|
569
|
+
requested: session ? session.requested : null,
|
570
|
+
isIdPFlow,
|
571
|
+
};
|
572
|
+
if (session) {
|
573
|
+
codeVal['session'] = session;
|
574
|
+
}
|
575
|
+
yield this.codeStore.put(code, codeVal);
|
576
|
+
return code;
|
577
|
+
});
|
578
|
+
}
|
727
579
|
/**
|
728
580
|
* @swagger
|
729
581
|
*
|
@@ -819,7 +671,7 @@ class OAuthController {
|
|
819
671
|
else if (client_id && client_secret) {
|
820
672
|
// check if we have an encoded client_id
|
821
673
|
if (client_id !== 'dummy') {
|
822
|
-
const sp = getEncodedTenantProduct(client_id);
|
674
|
+
const sp = (0, utils_1.getEncodedTenantProduct)(client_id);
|
823
675
|
if (!sp) {
|
824
676
|
// OAuth flow
|
825
677
|
if (client_id !== codeVal.clientID || client_secret !== codeVal.clientSecret) {
|