@backstage/plugin-auth-backend 0.0.0-nightly-202111282332 → 0.0.0-nightly-2021112922954
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/CHANGELOG.md +33 -3
- package/dist/index.cjs.js +1138 -1068
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +138 -96
- package/package.json +4 -4
package/dist/index.cjs.js
CHANGED
|
@@ -5,25 +5,25 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var express = require('express');
|
|
6
6
|
var Router = require('express-promise-router');
|
|
7
7
|
var cookieParser = require('cookie-parser');
|
|
8
|
-
var
|
|
9
|
-
var jwtDecoder = require('jwt-decode');
|
|
8
|
+
var OAuth2Strategy = require('passport-oauth2');
|
|
10
9
|
var errors = require('@backstage/errors');
|
|
11
10
|
var pickBy = require('lodash/pickBy');
|
|
12
11
|
var crypto = require('crypto');
|
|
13
12
|
var url = require('url');
|
|
14
13
|
var catalogModel = require('@backstage/catalog-model');
|
|
14
|
+
var jwtDecoder = require('jwt-decode');
|
|
15
|
+
var fetch = require('node-fetch');
|
|
16
|
+
var NodeCache = require('node-cache');
|
|
17
|
+
var jose = require('jose');
|
|
18
|
+
var passportBitbucketOauth2 = require('passport-bitbucket-oauth2');
|
|
19
|
+
var passportGithub2 = require('passport-github2');
|
|
15
20
|
var passportGitlab2 = require('passport-gitlab2');
|
|
16
21
|
var passportGoogleOauth20 = require('passport-google-oauth20');
|
|
17
22
|
var passportMicrosoft = require('passport-microsoft');
|
|
18
|
-
var fetch = require('node-fetch');
|
|
19
|
-
var OAuth2Strategy = require('passport-oauth2');
|
|
20
23
|
var openidClient = require('openid-client');
|
|
21
24
|
var passportOktaOauth = require('passport-okta-oauth');
|
|
22
|
-
var passportBitbucketOauth2 = require('passport-bitbucket-oauth2');
|
|
23
|
-
var NodeCache = require('node-cache');
|
|
24
|
-
var jose = require('jose');
|
|
25
|
-
var passportSaml = require('passport-saml');
|
|
26
25
|
var passportOneloginOauth = require('passport-onelogin-oauth');
|
|
26
|
+
var passportSaml = require('passport-saml');
|
|
27
27
|
var catalogClient = require('@backstage/catalog-client');
|
|
28
28
|
var uuid = require('uuid');
|
|
29
29
|
var luxon = require('luxon');
|
|
@@ -57,128 +57,69 @@ function _interopNamespace(e) {
|
|
|
57
57
|
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
58
58
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
59
59
|
var cookieParser__default = /*#__PURE__*/_interopDefaultLegacy(cookieParser);
|
|
60
|
-
var
|
|
60
|
+
var OAuth2Strategy__default = /*#__PURE__*/_interopDefaultLegacy(OAuth2Strategy);
|
|
61
61
|
var pickBy__default = /*#__PURE__*/_interopDefaultLegacy(pickBy);
|
|
62
62
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
63
63
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
64
|
+
var jwtDecoder__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecoder);
|
|
64
65
|
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
65
|
-
var OAuth2Strategy__default = /*#__PURE__*/_interopDefaultLegacy(OAuth2Strategy);
|
|
66
66
|
var NodeCache__default = /*#__PURE__*/_interopDefaultLegacy(NodeCache);
|
|
67
67
|
var session__default = /*#__PURE__*/_interopDefaultLegacy(session);
|
|
68
68
|
var passport__default = /*#__PURE__*/_interopDefaultLegacy(passport);
|
|
69
69
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
email = firstEmail.value;
|
|
76
|
-
}
|
|
77
|
-
let picture = void 0;
|
|
78
|
-
if (profile.avatarUrl) {
|
|
79
|
-
picture = profile.avatarUrl;
|
|
80
|
-
} else if (profile.photos && profile.photos.length > 0) {
|
|
81
|
-
const [firstPhoto] = profile.photos;
|
|
82
|
-
picture = firstPhoto.value;
|
|
83
|
-
}
|
|
84
|
-
let displayName = (_b = (_a = profile.displayName) != null ? _a : profile.username) != null ? _b : profile.id;
|
|
85
|
-
if ((!email || !picture || !displayName) && idToken) {
|
|
86
|
-
try {
|
|
87
|
-
const decoded = jwtDecoder__default["default"](idToken);
|
|
88
|
-
if (!email && decoded.email) {
|
|
89
|
-
email = decoded.email;
|
|
90
|
-
}
|
|
91
|
-
if (!picture && decoded.picture) {
|
|
92
|
-
picture = decoded.picture;
|
|
93
|
-
}
|
|
94
|
-
if (!displayName && decoded.name) {
|
|
95
|
-
displayName = decoded.name;
|
|
96
|
-
}
|
|
97
|
-
} catch (e) {
|
|
98
|
-
throw new Error(`Failed to parse id token and get profile info, ${e}`);
|
|
70
|
+
const defaultScopes = ["offline_access", "read:me"];
|
|
71
|
+
class AtlassianStrategy extends OAuth2Strategy__default["default"] {
|
|
72
|
+
constructor(options, verify) {
|
|
73
|
+
if (!options.scope) {
|
|
74
|
+
throw new TypeError("Atlassian requires a scope option");
|
|
99
75
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
};
|
|
107
|
-
const executeRedirectStrategy = async (req, providerStrategy, options) => {
|
|
108
|
-
return new Promise((resolve) => {
|
|
109
|
-
const strategy = Object.create(providerStrategy);
|
|
110
|
-
strategy.redirect = (url, status) => {
|
|
111
|
-
resolve({ url, status: status != null ? status : void 0 });
|
|
112
|
-
};
|
|
113
|
-
strategy.authenticate(req, { ...options });
|
|
114
|
-
});
|
|
115
|
-
};
|
|
116
|
-
const executeFrameHandlerStrategy = async (req, providerStrategy) => {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
const strategy = Object.create(providerStrategy);
|
|
119
|
-
strategy.success = (result, privateInfo) => {
|
|
120
|
-
resolve({ result, privateInfo });
|
|
121
|
-
};
|
|
122
|
-
strategy.fail = (info) => {
|
|
123
|
-
var _a;
|
|
124
|
-
reject(new Error(`Authentication rejected, ${(_a = info.message) != null ? _a : ""}`));
|
|
125
|
-
};
|
|
126
|
-
strategy.error = (error) => {
|
|
127
|
-
var _a;
|
|
128
|
-
let message = `Authentication failed, ${error.message}`;
|
|
129
|
-
if ((_a = error.oauthError) == null ? void 0 : _a.data) {
|
|
130
|
-
try {
|
|
131
|
-
const errorData = JSON.parse(error.oauthError.data);
|
|
132
|
-
if (errorData.message) {
|
|
133
|
-
message += ` - ${errorData.message}`;
|
|
134
|
-
}
|
|
135
|
-
} catch (parseError) {
|
|
136
|
-
message += ` - ${error.oauthError}`;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
reject(new Error(message));
|
|
76
|
+
const scopes = options.scope.split(" ");
|
|
77
|
+
const optionsWithURLs = {
|
|
78
|
+
...options,
|
|
79
|
+
authorizationURL: `https://auth.atlassian.com/authorize`,
|
|
80
|
+
tokenURL: `https://auth.atlassian.com/oauth/token`,
|
|
81
|
+
scope: Array.from(/* @__PURE__ */ new Set([...defaultScopes, ...scopes]))
|
|
140
82
|
};
|
|
141
|
-
|
|
142
|
-
|
|
83
|
+
super(optionsWithURLs, verify);
|
|
84
|
+
this.profileURL = "https://api.atlassian.com/me";
|
|
85
|
+
this.name = "atlassian";
|
|
86
|
+
this._oauth2.useAuthorizationHeaderforGET(true);
|
|
87
|
+
}
|
|
88
|
+
authorizationParams() {
|
|
89
|
+
return {
|
|
90
|
+
audience: "api.atlassian.com",
|
|
91
|
+
prompt: "consent"
|
|
143
92
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const executeRefreshTokenStrategy = async (providerStrategy, refreshToken, scope) => {
|
|
148
|
-
return new Promise((resolve, reject) => {
|
|
149
|
-
const anyStrategy = providerStrategy;
|
|
150
|
-
const OAuth2 = anyStrategy._oauth2.constructor;
|
|
151
|
-
const oauth2 = new OAuth2(anyStrategy._oauth2._clientId, anyStrategy._oauth2._clientSecret, anyStrategy._oauth2._baseSite, anyStrategy._oauth2._authorizeUrl, anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl, anyStrategy._oauth2._customHeaders);
|
|
152
|
-
oauth2.getOAuthAccessToken(refreshToken, {
|
|
153
|
-
scope,
|
|
154
|
-
grant_type: "refresh_token"
|
|
155
|
-
}, (err, accessToken, newRefreshToken, params) => {
|
|
93
|
+
}
|
|
94
|
+
userProfile(accessToken, done) {
|
|
95
|
+
this._oauth2.get(this.profileURL, accessToken, (err, body) => {
|
|
156
96
|
if (err) {
|
|
157
|
-
|
|
97
|
+
return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
|
|
158
98
|
}
|
|
159
|
-
if (!
|
|
160
|
-
|
|
99
|
+
if (!body) {
|
|
100
|
+
return done(new Error("Failed to fetch user profile, body cannot be empty"));
|
|
161
101
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
});
|
|
169
|
-
};
|
|
170
|
-
const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) => {
|
|
171
|
-
return new Promise((resolve, reject) => {
|
|
172
|
-
const anyStrategy = providerStrategy;
|
|
173
|
-
anyStrategy.userProfile(accessToken, (error, rawProfile) => {
|
|
174
|
-
if (error) {
|
|
175
|
-
reject(error);
|
|
176
|
-
} else {
|
|
177
|
-
resolve(rawProfile);
|
|
102
|
+
try {
|
|
103
|
+
const json = typeof body !== "string" ? body.toString() : body;
|
|
104
|
+
const profile = AtlassianStrategy.parse(json);
|
|
105
|
+
return done(null, profile);
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return done(new Error("Failed to parse user profile"));
|
|
178
108
|
}
|
|
179
109
|
});
|
|
180
|
-
}
|
|
181
|
-
|
|
110
|
+
}
|
|
111
|
+
static parse(json) {
|
|
112
|
+
const resp = JSON.parse(json);
|
|
113
|
+
return {
|
|
114
|
+
id: resp.account_id,
|
|
115
|
+
provider: "atlassian",
|
|
116
|
+
username: resp.nickname,
|
|
117
|
+
displayName: resp.name,
|
|
118
|
+
emails: [{ value: resp.email }],
|
|
119
|
+
photos: [{ value: resp.picture }]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
182
123
|
|
|
183
124
|
const readState = (stateString) => {
|
|
184
125
|
var _a, _b;
|
|
@@ -460,10 +401,10 @@ class OAuthAdapter {
|
|
|
460
401
|
}
|
|
461
402
|
const scope = (_b = (_a = req.query.scope) == null ? void 0 : _a.toString()) != null ? _b : "";
|
|
462
403
|
const forwardReq = Object.assign(req, { scope, refreshToken });
|
|
463
|
-
const response = await this.handlers.refresh(forwardReq);
|
|
404
|
+
const { response, refreshToken: newRefreshToken } = await this.handlers.refresh(forwardReq);
|
|
464
405
|
const backstageIdentity = await this.populateIdentity(response.backstageIdentity);
|
|
465
|
-
if (
|
|
466
|
-
this.setRefreshTokenCookie(res,
|
|
406
|
+
if (newRefreshToken && newRefreshToken !== refreshToken) {
|
|
407
|
+
this.setRefreshTokenCookie(res, newRefreshToken);
|
|
467
408
|
}
|
|
468
409
|
res.status(200).json({ ...response, backstageIdentity });
|
|
469
410
|
} catch (error) {
|
|
@@ -488,101 +429,347 @@ class OAuthAdapter {
|
|
|
488
429
|
}
|
|
489
430
|
}
|
|
490
431
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
432
|
+
const makeProfileInfo = (profile, idToken) => {
|
|
433
|
+
var _a, _b;
|
|
434
|
+
let email = void 0;
|
|
435
|
+
if (profile.emails && profile.emails.length > 0) {
|
|
436
|
+
const [firstEmail] = profile.emails;
|
|
437
|
+
email = firstEmail.value;
|
|
495
438
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
const token = await this.tokenIssuer.issueToken({
|
|
504
|
-
claims: { sub: "backstage.io/auth-backend" }
|
|
505
|
-
});
|
|
506
|
-
const { items } = await this.catalogApi.getEntities({ filter }, { token });
|
|
507
|
-
if (items.length !== 1) {
|
|
508
|
-
if (items.length > 1) {
|
|
509
|
-
throw new errors.ConflictError("User lookup resulted in multiple matches");
|
|
510
|
-
} else {
|
|
511
|
-
throw new errors.NotFoundError("User not found");
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return items[0];
|
|
439
|
+
let picture = void 0;
|
|
440
|
+
if (profile.avatarUrl) {
|
|
441
|
+
picture = profile.avatarUrl;
|
|
442
|
+
} else if (profile.photos && profile.photos.length > 0) {
|
|
443
|
+
const [firstPhoto] = profile.photos;
|
|
444
|
+
picture = firstPhoto.value;
|
|
515
445
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
defaultNamespace: "default"
|
|
523
|
-
});
|
|
524
|
-
return parsedRef;
|
|
525
|
-
} catch {
|
|
526
|
-
logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
|
|
527
|
-
return null;
|
|
446
|
+
let displayName = (_b = (_a = profile.displayName) != null ? _a : profile.username) != null ? _b : profile.id;
|
|
447
|
+
if ((!email || !picture || !displayName) && idToken) {
|
|
448
|
+
try {
|
|
449
|
+
const decoded = jwtDecoder__default["default"](idToken);
|
|
450
|
+
if (!email && decoded.email) {
|
|
451
|
+
email = decoded.email;
|
|
528
452
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
|
|
538
|
-
const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
|
|
539
|
-
logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
|
|
453
|
+
if (!picture && decoded.picture) {
|
|
454
|
+
picture = decoded.picture;
|
|
455
|
+
}
|
|
456
|
+
if (!displayName && decoded.name) {
|
|
457
|
+
displayName = decoded.name;
|
|
458
|
+
}
|
|
459
|
+
} catch (e) {
|
|
460
|
+
throw new Error(`Failed to parse id token and get profile info, ${e}`);
|
|
540
461
|
}
|
|
541
|
-
const memberOf = entities.flatMap((e) => {
|
|
542
|
-
var _a, _b;
|
|
543
|
-
return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.target)) != null ? _b : [];
|
|
544
|
-
});
|
|
545
|
-
const newEntityRefs = [
|
|
546
|
-
...new Set(resolvedEntityRefs.concat(memberOf).map(catalogModel.stringifyEntityRef))
|
|
547
|
-
];
|
|
548
|
-
logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
|
|
549
|
-
return newEntityRefs;
|
|
550
462
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
463
|
+
return {
|
|
464
|
+
email,
|
|
465
|
+
picture,
|
|
466
|
+
displayName
|
|
467
|
+
};
|
|
468
|
+
};
|
|
469
|
+
const executeRedirectStrategy = async (req, providerStrategy, options) => {
|
|
470
|
+
return new Promise((resolve) => {
|
|
471
|
+
const strategy = Object.create(providerStrategy);
|
|
472
|
+
strategy.redirect = (url, status) => {
|
|
473
|
+
resolve({ url, status: status != null ? status : void 0 });
|
|
474
|
+
};
|
|
475
|
+
strategy.authenticate(req, { ...options });
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
const executeFrameHandlerStrategy = async (req, providerStrategy) => {
|
|
479
|
+
return new Promise((resolve, reject) => {
|
|
480
|
+
const strategy = Object.create(providerStrategy);
|
|
481
|
+
strategy.success = (result, privateInfo) => {
|
|
482
|
+
resolve({ result, privateInfo });
|
|
483
|
+
};
|
|
484
|
+
strategy.fail = (info) => {
|
|
485
|
+
var _a;
|
|
486
|
+
reject(new Error(`Authentication rejected, ${(_a = info.message) != null ? _a : ""}`));
|
|
487
|
+
};
|
|
488
|
+
strategy.error = (error) => {
|
|
489
|
+
var _a;
|
|
490
|
+
let message = `Authentication failed, ${error.message}`;
|
|
491
|
+
if ((_a = error.oauthError) == null ? void 0 : _a.data) {
|
|
492
|
+
try {
|
|
493
|
+
const errorData = JSON.parse(error.oauthError.data);
|
|
494
|
+
if (errorData.message) {
|
|
495
|
+
message += ` - ${errorData.message}`;
|
|
496
|
+
}
|
|
497
|
+
} catch (parseError) {
|
|
498
|
+
message += ` - ${error.oauthError}`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
reject(new Error(message));
|
|
502
|
+
};
|
|
503
|
+
strategy.redirect = () => {
|
|
504
|
+
reject(new Error("Unexpected redirect"));
|
|
505
|
+
};
|
|
506
|
+
strategy.authenticate(req, {});
|
|
507
|
+
});
|
|
508
|
+
};
|
|
509
|
+
const executeRefreshTokenStrategy = async (providerStrategy, refreshToken, scope) => {
|
|
510
|
+
return new Promise((resolve, reject) => {
|
|
511
|
+
const anyStrategy = providerStrategy;
|
|
512
|
+
const OAuth2 = anyStrategy._oauth2.constructor;
|
|
513
|
+
const oauth2 = new OAuth2(anyStrategy._oauth2._clientId, anyStrategy._oauth2._clientSecret, anyStrategy._oauth2._baseSite, anyStrategy._oauth2._authorizeUrl, anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl, anyStrategy._oauth2._customHeaders);
|
|
514
|
+
oauth2.getOAuthAccessToken(refreshToken, {
|
|
515
|
+
scope,
|
|
516
|
+
grant_type: "refresh_token"
|
|
517
|
+
}, (err, accessToken, newRefreshToken, params) => {
|
|
518
|
+
if (err) {
|
|
519
|
+
reject(new Error(`Failed to refresh access token ${err.toString()}`));
|
|
520
|
+
}
|
|
521
|
+
if (!accessToken) {
|
|
522
|
+
reject(new Error(`Failed to refresh access token, no access token received`));
|
|
523
|
+
}
|
|
524
|
+
resolve({
|
|
525
|
+
accessToken,
|
|
526
|
+
refreshToken: newRefreshToken,
|
|
527
|
+
params
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) => {
|
|
533
|
+
return new Promise((resolve, reject) => {
|
|
534
|
+
const anyStrategy = providerStrategy;
|
|
535
|
+
anyStrategy.userProfile(accessToken, (error, rawProfile) => {
|
|
536
|
+
if (error) {
|
|
537
|
+
reject(error);
|
|
538
|
+
} else {
|
|
539
|
+
resolve(rawProfile);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
class CatalogIdentityClient {
|
|
546
|
+
constructor(options) {
|
|
547
|
+
this.catalogApi = options.catalogApi;
|
|
548
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
549
|
+
}
|
|
550
|
+
async findUser(query) {
|
|
551
|
+
const filter = {
|
|
552
|
+
kind: "user"
|
|
553
|
+
};
|
|
554
|
+
for (const [key, value] of Object.entries(query.annotations)) {
|
|
555
|
+
filter[`metadata.annotations.${key}`] = value;
|
|
556
|
+
}
|
|
557
|
+
const token = await this.tokenIssuer.issueToken({
|
|
558
|
+
claims: { sub: "backstage.io/auth-backend" }
|
|
559
|
+
});
|
|
560
|
+
const { items } = await this.catalogApi.getEntities({ filter }, { token });
|
|
561
|
+
if (items.length !== 1) {
|
|
562
|
+
if (items.length > 1) {
|
|
563
|
+
throw new errors.ConflictError("User lookup resulted in multiple matches");
|
|
564
|
+
} else {
|
|
565
|
+
throw new errors.NotFoundError("User not found");
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return items[0];
|
|
569
|
+
}
|
|
570
|
+
async resolveCatalogMembership(query) {
|
|
571
|
+
const { entityRefs, logger } = query;
|
|
572
|
+
const resolvedEntityRefs = entityRefs.map((ref) => {
|
|
573
|
+
try {
|
|
574
|
+
const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
|
|
575
|
+
defaultKind: "user",
|
|
576
|
+
defaultNamespace: "default"
|
|
577
|
+
});
|
|
578
|
+
return parsedRef;
|
|
579
|
+
} catch {
|
|
580
|
+
logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}).filter((ref) => ref !== null);
|
|
584
|
+
const filter = resolvedEntityRefs.map((ref) => ({
|
|
585
|
+
kind: ref.kind,
|
|
586
|
+
"metadata.namespace": ref.namespace,
|
|
587
|
+
"metadata.name": ref.name
|
|
588
|
+
}));
|
|
589
|
+
const entities = await this.catalogApi.getEntities({ filter }).then((r) => r.items);
|
|
590
|
+
if (entityRefs.length !== entities.length) {
|
|
591
|
+
const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
|
|
592
|
+
const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
|
|
593
|
+
logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
|
|
594
|
+
}
|
|
595
|
+
const memberOf = entities.flatMap((e) => {
|
|
596
|
+
var _a, _b;
|
|
597
|
+
return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.target)) != null ? _b : [];
|
|
598
|
+
});
|
|
599
|
+
const newEntityRefs = [
|
|
600
|
+
...new Set(resolvedEntityRefs.concat(memberOf).map(catalogModel.stringifyEntityRef))
|
|
601
|
+
];
|
|
602
|
+
logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
|
|
603
|
+
return newEntityRefs;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function getEntityClaims(entity) {
|
|
608
|
+
var _a, _b;
|
|
609
|
+
const userRef = catalogModel.stringifyEntityRef(entity);
|
|
610
|
+
const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF && r.target.kind.toLocaleLowerCase("en-US") === "group").map((r) => catalogModel.stringifyEntityRef(r.target))) != null ? _b : [];
|
|
557
611
|
return {
|
|
558
612
|
sub: userRef,
|
|
559
613
|
ent: [userRef, ...membershipRefs]
|
|
560
614
|
};
|
|
561
615
|
}
|
|
562
616
|
|
|
563
|
-
|
|
617
|
+
const atlassianDefaultAuthHandler = async ({
|
|
618
|
+
fullProfile,
|
|
619
|
+
params
|
|
620
|
+
}) => ({
|
|
621
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
622
|
+
});
|
|
623
|
+
class AtlassianAuthProvider {
|
|
624
|
+
constructor(options) {
|
|
625
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
626
|
+
this.logger = options.logger;
|
|
627
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
628
|
+
this.authHandler = options.authHandler;
|
|
629
|
+
this.signInResolver = options.signInResolver;
|
|
630
|
+
this._strategy = new AtlassianStrategy({
|
|
631
|
+
clientID: options.clientId,
|
|
632
|
+
clientSecret: options.clientSecret,
|
|
633
|
+
callbackURL: options.callbackUrl,
|
|
634
|
+
scope: options.scopes
|
|
635
|
+
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
636
|
+
done(void 0, {
|
|
637
|
+
fullProfile,
|
|
638
|
+
accessToken,
|
|
639
|
+
refreshToken,
|
|
640
|
+
params
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
async start(req) {
|
|
645
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
646
|
+
state: encodeState(req.state)
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
async handler(req) {
|
|
650
|
+
const { result } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
651
|
+
return {
|
|
652
|
+
response: await this.handleResult(result),
|
|
653
|
+
refreshToken: result.refreshToken
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
async handleResult(result) {
|
|
657
|
+
const { profile } = await this.authHandler(result);
|
|
658
|
+
const response = {
|
|
659
|
+
providerInfo: {
|
|
660
|
+
idToken: result.params.id_token,
|
|
661
|
+
accessToken: result.accessToken,
|
|
662
|
+
scope: result.params.scope,
|
|
663
|
+
expiresInSeconds: result.params.expires_in
|
|
664
|
+
},
|
|
665
|
+
profile
|
|
666
|
+
};
|
|
667
|
+
if (this.signInResolver) {
|
|
668
|
+
response.backstageIdentity = await this.signInResolver({
|
|
669
|
+
result,
|
|
670
|
+
profile
|
|
671
|
+
}, {
|
|
672
|
+
tokenIssuer: this.tokenIssuer,
|
|
673
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
674
|
+
logger: this.logger
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
return response;
|
|
678
|
+
}
|
|
679
|
+
async refresh(req) {
|
|
680
|
+
const { accessToken, params, refreshToken } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
681
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
682
|
+
return {
|
|
683
|
+
response: await this.handleResult({
|
|
684
|
+
fullProfile,
|
|
685
|
+
params,
|
|
686
|
+
accessToken
|
|
687
|
+
}),
|
|
688
|
+
refreshToken
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const createAtlassianProvider = (options) => {
|
|
693
|
+
return ({
|
|
694
|
+
providerId,
|
|
695
|
+
globalConfig,
|
|
696
|
+
config,
|
|
697
|
+
tokenIssuer,
|
|
698
|
+
catalogApi,
|
|
699
|
+
logger
|
|
700
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
701
|
+
var _a, _b;
|
|
702
|
+
const clientId = envConfig.getString("clientId");
|
|
703
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
704
|
+
const scopes = envConfig.getString("scopes");
|
|
705
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
706
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
707
|
+
catalogApi,
|
|
708
|
+
tokenIssuer
|
|
709
|
+
});
|
|
710
|
+
const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
|
|
711
|
+
const provider = new AtlassianAuthProvider({
|
|
712
|
+
clientId,
|
|
713
|
+
clientSecret,
|
|
714
|
+
scopes,
|
|
715
|
+
callbackUrl,
|
|
716
|
+
authHandler,
|
|
717
|
+
signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
|
|
718
|
+
catalogIdentityClient,
|
|
719
|
+
logger,
|
|
720
|
+
tokenIssuer
|
|
721
|
+
});
|
|
722
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
723
|
+
disableRefresh: true,
|
|
724
|
+
providerId,
|
|
725
|
+
tokenIssuer
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
class Auth0Strategy extends OAuth2Strategy__default["default"] {
|
|
731
|
+
constructor(options, verify) {
|
|
732
|
+
const optionsWithURLs = {
|
|
733
|
+
...options,
|
|
734
|
+
authorizationURL: `https://${options.domain}/authorize`,
|
|
735
|
+
tokenURL: `https://${options.domain}/oauth/token`,
|
|
736
|
+
userInfoURL: `https://${options.domain}/userinfo`,
|
|
737
|
+
apiUrl: `https://${options.domain}/api`
|
|
738
|
+
};
|
|
739
|
+
super(optionsWithURLs, verify);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
class Auth0AuthProvider {
|
|
564
744
|
constructor(options) {
|
|
565
745
|
this.signInResolver = options.signInResolver;
|
|
566
746
|
this.authHandler = options.authHandler;
|
|
567
|
-
this.stateEncoder = options.stateEncoder;
|
|
568
747
|
this.tokenIssuer = options.tokenIssuer;
|
|
569
748
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
570
749
|
this.logger = options.logger;
|
|
571
|
-
this._strategy = new
|
|
750
|
+
this._strategy = new Auth0Strategy({
|
|
572
751
|
clientID: options.clientId,
|
|
573
752
|
clientSecret: options.clientSecret,
|
|
574
753
|
callbackURL: options.callbackUrl,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
authorizationURL: options.authorizationUrl
|
|
754
|
+
domain: options.domain,
|
|
755
|
+
passReqToCallback: false
|
|
578
756
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
579
|
-
done(void 0, {
|
|
757
|
+
done(void 0, {
|
|
758
|
+
fullProfile,
|
|
759
|
+
accessToken,
|
|
760
|
+
refreshToken,
|
|
761
|
+
params
|
|
762
|
+
}, {
|
|
763
|
+
refreshToken
|
|
764
|
+
});
|
|
580
765
|
});
|
|
581
766
|
}
|
|
582
767
|
async start(req) {
|
|
583
768
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
769
|
+
accessType: "offline",
|
|
770
|
+
prompt: "consent",
|
|
584
771
|
scope: req.scope,
|
|
585
|
-
state: (
|
|
772
|
+
state: encodeState(req.state)
|
|
586
773
|
});
|
|
587
774
|
}
|
|
588
775
|
async handler(req) {
|
|
@@ -593,28 +780,25 @@ class GithubAuthProvider {
|
|
|
593
780
|
};
|
|
594
781
|
}
|
|
595
782
|
async refresh(req) {
|
|
596
|
-
const {
|
|
597
|
-
accessToken,
|
|
598
|
-
refreshToken: newRefreshToken,
|
|
599
|
-
params
|
|
600
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
783
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
601
784
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
602
|
-
return
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
785
|
+
return {
|
|
786
|
+
response: await this.handleResult({
|
|
787
|
+
fullProfile,
|
|
788
|
+
params,
|
|
789
|
+
accessToken
|
|
790
|
+
}),
|
|
791
|
+
refreshToken
|
|
792
|
+
};
|
|
608
793
|
}
|
|
609
794
|
async handleResult(result) {
|
|
610
795
|
const { profile } = await this.authHandler(result);
|
|
611
|
-
const expiresInStr = result.params.expires_in;
|
|
612
796
|
const response = {
|
|
613
797
|
providerInfo: {
|
|
798
|
+
idToken: result.params.id_token,
|
|
614
799
|
accessToken: result.accessToken,
|
|
615
|
-
refreshToken: result.refreshToken,
|
|
616
800
|
scope: result.params.scope,
|
|
617
|
-
expiresInSeconds:
|
|
801
|
+
expiresInSeconds: result.params.expires_in
|
|
618
802
|
},
|
|
619
803
|
profile
|
|
620
804
|
};
|
|
@@ -631,15 +815,15 @@ class GithubAuthProvider {
|
|
|
631
815
|
return response;
|
|
632
816
|
}
|
|
633
817
|
}
|
|
634
|
-
const
|
|
635
|
-
const {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return { id
|
|
818
|
+
const defaultSignInResolver$1 = async (info) => {
|
|
819
|
+
const { profile } = info;
|
|
820
|
+
if (!profile.email) {
|
|
821
|
+
throw new Error("Profile does not contain an email");
|
|
822
|
+
}
|
|
823
|
+
const id = profile.email.split("@")[0];
|
|
824
|
+
return { id, token: "" };
|
|
641
825
|
};
|
|
642
|
-
const
|
|
826
|
+
const createAuth0Provider = (options) => {
|
|
643
827
|
return ({
|
|
644
828
|
providerId,
|
|
645
829
|
globalConfig,
|
|
@@ -648,90 +832,193 @@ const createGithubProvider = (options) => {
|
|
|
648
832
|
catalogApi,
|
|
649
833
|
logger
|
|
650
834
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
651
|
-
var _a, _b
|
|
835
|
+
var _a, _b;
|
|
652
836
|
const clientId = envConfig.getString("clientId");
|
|
653
837
|
const clientSecret = envConfig.getString("clientSecret");
|
|
654
|
-
const
|
|
655
|
-
const
|
|
656
|
-
const authorizationUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/authorize` : void 0;
|
|
657
|
-
const tokenUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/access_token` : void 0;
|
|
658
|
-
const userProfileUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/api/v3/user` : void 0;
|
|
659
|
-
const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
838
|
+
const domain = envConfig.getString("domain");
|
|
839
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
660
840
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
661
841
|
catalogApi,
|
|
662
842
|
tokenIssuer
|
|
663
843
|
});
|
|
664
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
665
|
-
profile: makeProfileInfo(fullProfile)
|
|
666
|
-
});
|
|
667
|
-
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : githubDefaultSignInResolver;
|
|
668
|
-
const signInResolver = (info) => signInResolverFn(info, {
|
|
669
|
-
catalogIdentityClient,
|
|
670
|
-
tokenIssuer,
|
|
671
|
-
logger
|
|
844
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
845
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
672
846
|
});
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
};
|
|
676
|
-
const provider = new GithubAuthProvider({
|
|
847
|
+
const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver$1;
|
|
848
|
+
const provider = new Auth0AuthProvider({
|
|
677
849
|
clientId,
|
|
678
850
|
clientSecret,
|
|
679
851
|
callbackUrl,
|
|
680
|
-
|
|
681
|
-
userProfileUrl,
|
|
682
|
-
authorizationUrl,
|
|
683
|
-
signInResolver,
|
|
852
|
+
domain,
|
|
684
853
|
authHandler,
|
|
854
|
+
signInResolver,
|
|
685
855
|
tokenIssuer,
|
|
686
856
|
catalogIdentityClient,
|
|
687
|
-
stateEncoder,
|
|
688
857
|
logger
|
|
689
858
|
});
|
|
690
859
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
691
|
-
|
|
860
|
+
disableRefresh: true,
|
|
692
861
|
providerId,
|
|
693
862
|
tokenIssuer
|
|
694
863
|
});
|
|
695
864
|
});
|
|
696
865
|
};
|
|
697
866
|
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
const token = await ctx.tokenIssuer.issueToken({
|
|
705
|
-
claims: { sub: id, ent: [`user:default/${id}`] }
|
|
706
|
-
});
|
|
707
|
-
return { id, token };
|
|
867
|
+
const ALB_JWT_HEADER = "x-amzn-oidc-data";
|
|
868
|
+
const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
|
|
869
|
+
const getJWTHeaders = (input) => {
|
|
870
|
+
const encoded = input.split(".")[0];
|
|
871
|
+
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
|
708
872
|
};
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
873
|
+
class AwsAlbAuthProvider {
|
|
874
|
+
constructor(options) {
|
|
875
|
+
this.region = options.region;
|
|
876
|
+
this.issuer = options.issuer;
|
|
877
|
+
this.authHandler = options.authHandler;
|
|
878
|
+
this.signInResolver = options.signInResolver;
|
|
879
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
880
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
881
|
+
this.logger = options.logger;
|
|
882
|
+
this.keyCache = new NodeCache__default["default"]({ stdTTL: 3600 });
|
|
883
|
+
}
|
|
884
|
+
frameHandler() {
|
|
885
|
+
return Promise.resolve(void 0);
|
|
886
|
+
}
|
|
887
|
+
async refresh(req, res) {
|
|
888
|
+
try {
|
|
889
|
+
const result = await this.getResult(req);
|
|
890
|
+
const response = await this.handleResult(result);
|
|
891
|
+
res.json(response);
|
|
892
|
+
} catch (e) {
|
|
893
|
+
this.logger.error("Exception occurred during AWS ALB token refresh", e);
|
|
894
|
+
res.status(401);
|
|
895
|
+
res.end();
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
start() {
|
|
899
|
+
return Promise.resolve(void 0);
|
|
900
|
+
}
|
|
901
|
+
async getResult(req) {
|
|
902
|
+
const jwt = req.header(ALB_JWT_HEADER);
|
|
903
|
+
const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);
|
|
904
|
+
if (jwt === void 0) {
|
|
905
|
+
throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_JWT_HEADER}`);
|
|
906
|
+
}
|
|
907
|
+
if (accessToken === void 0) {
|
|
908
|
+
throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`);
|
|
909
|
+
}
|
|
910
|
+
try {
|
|
911
|
+
const headers = getJWTHeaders(jwt);
|
|
912
|
+
const key = await this.getKey(headers.kid);
|
|
913
|
+
const claims = jose.JWT.verify(jwt, key);
|
|
914
|
+
if (this.issuer && claims.iss !== this.issuer) {
|
|
915
|
+
throw new errors.AuthenticationError("Issuer mismatch on JWT token");
|
|
916
|
+
}
|
|
917
|
+
const fullProfile = {
|
|
918
|
+
provider: "unknown",
|
|
919
|
+
id: claims.sub,
|
|
920
|
+
displayName: claims.name,
|
|
921
|
+
username: claims.email.split("@")[0].toLowerCase(),
|
|
922
|
+
name: {
|
|
923
|
+
familyName: claims.family_name,
|
|
924
|
+
givenName: claims.given_name
|
|
925
|
+
},
|
|
926
|
+
emails: [{ value: claims.email.toLowerCase() }],
|
|
927
|
+
photos: [{ value: claims.picture }]
|
|
928
|
+
};
|
|
929
|
+
return {
|
|
930
|
+
fullProfile,
|
|
931
|
+
expiresInSeconds: claims.exp,
|
|
932
|
+
accessToken
|
|
933
|
+
};
|
|
934
|
+
} catch (e) {
|
|
935
|
+
throw new Error(`Exception occurred during JWT processing: ${e}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
async handleResult(result) {
|
|
939
|
+
const { profile } = await this.authHandler(result);
|
|
940
|
+
const backstageIdentity = await this.signInResolver({
|
|
941
|
+
result,
|
|
942
|
+
profile
|
|
943
|
+
}, {
|
|
944
|
+
tokenIssuer: this.tokenIssuer,
|
|
945
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
946
|
+
logger: this.logger
|
|
947
|
+
});
|
|
948
|
+
return {
|
|
949
|
+
providerInfo: {
|
|
950
|
+
accessToken: result.accessToken,
|
|
951
|
+
expiresInSeconds: result.expiresInSeconds
|
|
952
|
+
},
|
|
953
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
|
|
954
|
+
profile
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
async getKey(keyId) {
|
|
958
|
+
const optionalCacheKey = this.keyCache.get(keyId);
|
|
959
|
+
if (optionalCacheKey) {
|
|
960
|
+
return crypto__namespace.createPublicKey(optionalCacheKey);
|
|
961
|
+
}
|
|
962
|
+
const keyText = await fetch__default["default"](`https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`).then((response) => response.text());
|
|
963
|
+
const keyValue = crypto__namespace.createPublicKey(keyText);
|
|
964
|
+
this.keyCache.set(keyId, keyValue.export({ format: "pem", type: "spki" }));
|
|
965
|
+
return keyValue;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const createAwsAlbProvider = (options) => {
|
|
969
|
+
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
|
970
|
+
const region = config.getString("region");
|
|
971
|
+
const issuer = config.getOptionalString("iss");
|
|
972
|
+
if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
|
|
973
|
+
throw new Error("SignInResolver is required to use this authentication provider");
|
|
974
|
+
}
|
|
975
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
976
|
+
catalogApi,
|
|
977
|
+
tokenIssuer
|
|
978
|
+
});
|
|
979
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
980
|
+
profile: makeProfileInfo(fullProfile)
|
|
981
|
+
});
|
|
982
|
+
const signInResolver = options == null ? void 0 : options.signIn.resolver;
|
|
983
|
+
return new AwsAlbAuthProvider({
|
|
984
|
+
region,
|
|
985
|
+
issuer,
|
|
986
|
+
signInResolver,
|
|
987
|
+
authHandler,
|
|
988
|
+
tokenIssuer,
|
|
989
|
+
catalogIdentityClient,
|
|
990
|
+
logger
|
|
991
|
+
});
|
|
992
|
+
};
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
class BitbucketAuthProvider {
|
|
716
996
|
constructor(options) {
|
|
997
|
+
this.signInResolver = options.signInResolver;
|
|
998
|
+
this.authHandler = options.authHandler;
|
|
999
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
717
1000
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
718
1001
|
this.logger = options.logger;
|
|
719
|
-
this.
|
|
720
|
-
this.authHandler = options.authHandler;
|
|
721
|
-
this.signInResolver = options.signInResolver;
|
|
722
|
-
this._strategy = new passportGitlab2.Strategy({
|
|
1002
|
+
this._strategy = new passportBitbucketOauth2.Strategy({
|
|
723
1003
|
clientID: options.clientId,
|
|
724
1004
|
clientSecret: options.clientSecret,
|
|
725
1005
|
callbackURL: options.callbackUrl,
|
|
726
|
-
|
|
1006
|
+
passReqToCallback: false
|
|
727
1007
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
728
|
-
done(void 0, {
|
|
1008
|
+
done(void 0, {
|
|
1009
|
+
fullProfile,
|
|
1010
|
+
params,
|
|
1011
|
+
accessToken,
|
|
1012
|
+
refreshToken
|
|
1013
|
+
}, {
|
|
729
1014
|
refreshToken
|
|
730
1015
|
});
|
|
731
1016
|
});
|
|
732
1017
|
}
|
|
733
1018
|
async start(req) {
|
|
734
1019
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
1020
|
+
accessType: "offline",
|
|
1021
|
+
prompt: "consent",
|
|
735
1022
|
scope: req.scope,
|
|
736
1023
|
state: encodeState(req.state)
|
|
737
1024
|
});
|
|
@@ -744,26 +1031,24 @@ class GitlabAuthProvider {
|
|
|
744
1031
|
};
|
|
745
1032
|
}
|
|
746
1033
|
async refresh(req) {
|
|
747
|
-
const {
|
|
748
|
-
accessToken,
|
|
749
|
-
refreshToken: newRefreshToken,
|
|
750
|
-
params
|
|
751
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1034
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
752
1035
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
753
|
-
return
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
1036
|
+
return {
|
|
1037
|
+
response: await this.handleResult({
|
|
1038
|
+
fullProfile,
|
|
1039
|
+
params,
|
|
1040
|
+
accessToken
|
|
1041
|
+
}),
|
|
1042
|
+
refreshToken
|
|
1043
|
+
};
|
|
759
1044
|
}
|
|
760
1045
|
async handleResult(result) {
|
|
1046
|
+
result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
|
|
761
1047
|
const { profile } = await this.authHandler(result);
|
|
762
1048
|
const response = {
|
|
763
1049
|
providerInfo: {
|
|
764
1050
|
idToken: result.params.id_token,
|
|
765
1051
|
accessToken: result.accessToken,
|
|
766
|
-
refreshToken: result.refreshToken,
|
|
767
1052
|
scope: result.params.scope,
|
|
768
1053
|
expiresInSeconds: result.params.expires_in
|
|
769
1054
|
},
|
|
@@ -782,7 +1067,35 @@ class GitlabAuthProvider {
|
|
|
782
1067
|
return response;
|
|
783
1068
|
}
|
|
784
1069
|
}
|
|
785
|
-
const
|
|
1070
|
+
const bitbucketUsernameSignInResolver = async (info, ctx) => {
|
|
1071
|
+
const { result } = info;
|
|
1072
|
+
if (!result.fullProfile.username) {
|
|
1073
|
+
throw new Error("Bitbucket profile contained no Username");
|
|
1074
|
+
}
|
|
1075
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1076
|
+
annotations: {
|
|
1077
|
+
"bitbucket.org/username": result.fullProfile.username
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
const claims = getEntityClaims(entity);
|
|
1081
|
+
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1082
|
+
return { id: entity.metadata.name, entity, token };
|
|
1083
|
+
};
|
|
1084
|
+
const bitbucketUserIdSignInResolver = async (info, ctx) => {
|
|
1085
|
+
const { result } = info;
|
|
1086
|
+
if (!result.fullProfile.id) {
|
|
1087
|
+
throw new Error("Bitbucket profile contained no User ID");
|
|
1088
|
+
}
|
|
1089
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1090
|
+
annotations: {
|
|
1091
|
+
"bitbucket.org/user-id": result.fullProfile.id
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
const claims = getEntityClaims(entity);
|
|
1095
|
+
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1096
|
+
return { id: entity.metadata.name, entity, token };
|
|
1097
|
+
};
|
|
1098
|
+
const createBitbucketProvider = (options) => {
|
|
786
1099
|
return ({
|
|
787
1100
|
providerId,
|
|
788
1101
|
globalConfig,
|
|
@@ -791,33 +1104,26 @@ const createGitlabProvider = (options) => {
|
|
|
791
1104
|
catalogApi,
|
|
792
1105
|
logger
|
|
793
1106
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
794
|
-
var _a
|
|
1107
|
+
var _a;
|
|
795
1108
|
const clientId = envConfig.getString("clientId");
|
|
796
1109
|
const clientSecret = envConfig.getString("clientSecret");
|
|
797
|
-
const audience = envConfig.getOptionalString("audience");
|
|
798
|
-
const baseUrl = audience || "https://gitlab.com";
|
|
799
1110
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
800
1111
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
801
1112
|
catalogApi,
|
|
802
1113
|
tokenIssuer
|
|
803
1114
|
});
|
|
804
|
-
const authHandler = (
|
|
805
|
-
|
|
806
|
-
const signInResolver = (info) => signInResolverFn(info, {
|
|
807
|
-
catalogIdentityClient,
|
|
808
|
-
tokenIssuer,
|
|
809
|
-
logger
|
|
1115
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1116
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
810
1117
|
});
|
|
811
|
-
const provider = new
|
|
1118
|
+
const provider = new BitbucketAuthProvider({
|
|
812
1119
|
clientId,
|
|
813
1120
|
clientSecret,
|
|
814
1121
|
callbackUrl,
|
|
815
|
-
|
|
1122
|
+
signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
|
|
816
1123
|
authHandler,
|
|
817
|
-
|
|
1124
|
+
tokenIssuer,
|
|
818
1125
|
catalogIdentityClient,
|
|
819
|
-
logger
|
|
820
|
-
tokenIssuer
|
|
1126
|
+
logger
|
|
821
1127
|
});
|
|
822
1128
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
823
1129
|
disableRefresh: false,
|
|
@@ -827,35 +1133,29 @@ const createGitlabProvider = (options) => {
|
|
|
827
1133
|
});
|
|
828
1134
|
};
|
|
829
1135
|
|
|
830
|
-
class
|
|
1136
|
+
class GithubAuthProvider {
|
|
831
1137
|
constructor(options) {
|
|
832
1138
|
this.signInResolver = options.signInResolver;
|
|
833
1139
|
this.authHandler = options.authHandler;
|
|
1140
|
+
this.stateEncoder = options.stateEncoder;
|
|
834
1141
|
this.tokenIssuer = options.tokenIssuer;
|
|
835
1142
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
836
1143
|
this.logger = options.logger;
|
|
837
|
-
this._strategy = new
|
|
1144
|
+
this._strategy = new passportGithub2.Strategy({
|
|
838
1145
|
clientID: options.clientId,
|
|
839
1146
|
clientSecret: options.clientSecret,
|
|
840
1147
|
callbackURL: options.callbackUrl,
|
|
841
|
-
|
|
1148
|
+
tokenURL: options.tokenUrl,
|
|
1149
|
+
userProfileURL: options.userProfileUrl,
|
|
1150
|
+
authorizationURL: options.authorizationUrl
|
|
842
1151
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
843
|
-
done(void 0, {
|
|
844
|
-
fullProfile,
|
|
845
|
-
params,
|
|
846
|
-
accessToken,
|
|
847
|
-
refreshToken
|
|
848
|
-
}, {
|
|
849
|
-
refreshToken
|
|
850
|
-
});
|
|
1152
|
+
done(void 0, { fullProfile, params, accessToken }, { refreshToken });
|
|
851
1153
|
});
|
|
852
1154
|
}
|
|
853
1155
|
async start(req) {
|
|
854
1156
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
855
|
-
accessType: "offline",
|
|
856
|
-
prompt: "consent",
|
|
857
1157
|
scope: req.scope,
|
|
858
|
-
state:
|
|
1158
|
+
state: (await this.stateEncoder(req)).encodedState
|
|
859
1159
|
});
|
|
860
1160
|
}
|
|
861
1161
|
async handler(req) {
|
|
@@ -866,23 +1166,25 @@ class GoogleAuthProvider {
|
|
|
866
1166
|
};
|
|
867
1167
|
}
|
|
868
1168
|
async refresh(req) {
|
|
869
|
-
const { accessToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1169
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
870
1170
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
871
|
-
return
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1171
|
+
return {
|
|
1172
|
+
response: await this.handleResult({
|
|
1173
|
+
fullProfile,
|
|
1174
|
+
params,
|
|
1175
|
+
accessToken
|
|
1176
|
+
}),
|
|
1177
|
+
refreshToken
|
|
1178
|
+
};
|
|
877
1179
|
}
|
|
878
1180
|
async handleResult(result) {
|
|
879
1181
|
const { profile } = await this.authHandler(result);
|
|
1182
|
+
const expiresInStr = result.params.expires_in;
|
|
880
1183
|
const response = {
|
|
881
1184
|
providerInfo: {
|
|
882
|
-
idToken: result.params.id_token,
|
|
883
1185
|
accessToken: result.accessToken,
|
|
884
1186
|
scope: result.params.scope,
|
|
885
|
-
expiresInSeconds:
|
|
1187
|
+
expiresInSeconds: expiresInStr === void 0 ? void 0 : Number(expiresInStr)
|
|
886
1188
|
},
|
|
887
1189
|
profile
|
|
888
1190
|
};
|
|
@@ -899,43 +1201,15 @@ class GoogleAuthProvider {
|
|
|
899
1201
|
return response;
|
|
900
1202
|
}
|
|
901
1203
|
}
|
|
902
|
-
const
|
|
903
|
-
const {
|
|
904
|
-
|
|
905
|
-
throw new Error("Google profile contained no email");
|
|
906
|
-
}
|
|
907
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
908
|
-
annotations: {
|
|
909
|
-
"google.com/email": profile.email
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
const claims = getEntityClaims(entity);
|
|
913
|
-
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
914
|
-
return { id: entity.metadata.name, entity, token };
|
|
915
|
-
};
|
|
916
|
-
const googleDefaultSignInResolver = async (info, ctx) => {
|
|
917
|
-
const { profile } = info;
|
|
918
|
-
if (!profile.email) {
|
|
919
|
-
throw new Error("Google profile contained no email");
|
|
920
|
-
}
|
|
921
|
-
let userId;
|
|
922
|
-
try {
|
|
923
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
924
|
-
annotations: {
|
|
925
|
-
"google.com/email": profile.email
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
userId = entity.metadata.name;
|
|
929
|
-
} catch (error) {
|
|
930
|
-
ctx.logger.warn(`Failed to look up user, ${error}, falling back to allowing login based on email pattern, this will probably break in the future`);
|
|
931
|
-
userId = profile.email.split("@")[0];
|
|
932
|
-
}
|
|
1204
|
+
const githubDefaultSignInResolver = async (info, ctx) => {
|
|
1205
|
+
const { fullProfile } = info.result;
|
|
1206
|
+
const userId = fullProfile.username || fullProfile.id;
|
|
933
1207
|
const token = await ctx.tokenIssuer.issueToken({
|
|
934
1208
|
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
935
1209
|
});
|
|
936
1210
|
return { id: userId, token };
|
|
937
1211
|
};
|
|
938
|
-
const
|
|
1212
|
+
const createGithubProvider = (options) => {
|
|
939
1213
|
return ({
|
|
940
1214
|
providerId,
|
|
941
1215
|
globalConfig,
|
|
@@ -944,57 +1218,86 @@ const createGoogleProvider = (options) => {
|
|
|
944
1218
|
catalogApi,
|
|
945
1219
|
logger
|
|
946
1220
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
947
|
-
var _a, _b;
|
|
1221
|
+
var _a, _b, _c;
|
|
948
1222
|
const clientId = envConfig.getString("clientId");
|
|
949
1223
|
const clientSecret = envConfig.getString("clientSecret");
|
|
950
|
-
const
|
|
1224
|
+
const enterpriseInstanceUrl = envConfig.getOptionalString("enterpriseInstanceUrl");
|
|
1225
|
+
const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
|
|
1226
|
+
const authorizationUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/authorize` : void 0;
|
|
1227
|
+
const tokenUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/access_token` : void 0;
|
|
1228
|
+
const userProfileUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/api/v3/user` : void 0;
|
|
1229
|
+
const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
951
1230
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
952
1231
|
catalogApi,
|
|
953
1232
|
tokenIssuer
|
|
954
1233
|
});
|
|
955
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile
|
|
956
|
-
profile: makeProfileInfo(fullProfile
|
|
1234
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
1235
|
+
profile: makeProfileInfo(fullProfile)
|
|
957
1236
|
});
|
|
958
|
-
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b :
|
|
1237
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : githubDefaultSignInResolver;
|
|
959
1238
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
960
1239
|
catalogIdentityClient,
|
|
961
1240
|
tokenIssuer,
|
|
962
1241
|
logger
|
|
963
1242
|
});
|
|
964
|
-
const
|
|
1243
|
+
const stateEncoder = (_c = options == null ? void 0 : options.stateEncoder) != null ? _c : async (req) => {
|
|
1244
|
+
return { encodedState: encodeState(req.state) };
|
|
1245
|
+
};
|
|
1246
|
+
const provider = new GithubAuthProvider({
|
|
965
1247
|
clientId,
|
|
966
1248
|
clientSecret,
|
|
967
1249
|
callbackUrl,
|
|
1250
|
+
tokenUrl,
|
|
1251
|
+
userProfileUrl,
|
|
1252
|
+
authorizationUrl,
|
|
968
1253
|
signInResolver,
|
|
969
1254
|
authHandler,
|
|
970
1255
|
tokenIssuer,
|
|
971
1256
|
catalogIdentityClient,
|
|
1257
|
+
stateEncoder,
|
|
972
1258
|
logger
|
|
973
1259
|
});
|
|
974
1260
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
975
|
-
|
|
1261
|
+
persistScopes: true,
|
|
976
1262
|
providerId,
|
|
977
1263
|
tokenIssuer
|
|
978
1264
|
});
|
|
979
1265
|
});
|
|
980
1266
|
};
|
|
981
1267
|
|
|
982
|
-
|
|
1268
|
+
const gitlabDefaultSignInResolver = async (info, ctx) => {
|
|
1269
|
+
const { profile, result } = info;
|
|
1270
|
+
let id = result.fullProfile.id;
|
|
1271
|
+
if (profile.email) {
|
|
1272
|
+
id = profile.email.split("@")[0];
|
|
1273
|
+
}
|
|
1274
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
1275
|
+
claims: { sub: id, ent: [`user:default/${id}`] }
|
|
1276
|
+
});
|
|
1277
|
+
return { id, token };
|
|
1278
|
+
};
|
|
1279
|
+
const gitlabDefaultAuthHandler = async ({
|
|
1280
|
+
fullProfile,
|
|
1281
|
+
params
|
|
1282
|
+
}) => ({
|
|
1283
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1284
|
+
});
|
|
1285
|
+
class GitlabAuthProvider {
|
|
983
1286
|
constructor(options) {
|
|
984
|
-
this.signInResolver = options.signInResolver;
|
|
985
|
-
this.authHandler = options.authHandler;
|
|
986
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
987
|
-
this.logger = options.logger;
|
|
988
1287
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
989
|
-
this.
|
|
1288
|
+
this.logger = options.logger;
|
|
1289
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
1290
|
+
this.authHandler = options.authHandler;
|
|
1291
|
+
this.signInResolver = options.signInResolver;
|
|
1292
|
+
this._strategy = new passportGitlab2.Strategy({
|
|
990
1293
|
clientID: options.clientId,
|
|
991
1294
|
clientSecret: options.clientSecret,
|
|
992
1295
|
callbackURL: options.callbackUrl,
|
|
993
|
-
|
|
994
|
-
tokenURL: options.tokenUrl,
|
|
995
|
-
passReqToCallback: false
|
|
1296
|
+
baseURL: options.baseUrl
|
|
996
1297
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
997
|
-
done(void 0, { fullProfile,
|
|
1298
|
+
done(void 0, { fullProfile, params, accessToken }, {
|
|
1299
|
+
refreshToken
|
|
1300
|
+
});
|
|
998
1301
|
});
|
|
999
1302
|
}
|
|
1000
1303
|
async start(req) {
|
|
@@ -1011,18 +1314,18 @@ class MicrosoftAuthProvider {
|
|
|
1011
1314
|
};
|
|
1012
1315
|
}
|
|
1013
1316
|
async refresh(req) {
|
|
1014
|
-
const { accessToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1317
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1015
1318
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1016
|
-
return
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1319
|
+
return {
|
|
1320
|
+
response: await this.handleResult({
|
|
1321
|
+
fullProfile,
|
|
1322
|
+
params,
|
|
1323
|
+
accessToken
|
|
1324
|
+
}),
|
|
1325
|
+
refreshToken
|
|
1326
|
+
};
|
|
1022
1327
|
}
|
|
1023
1328
|
async handleResult(result) {
|
|
1024
|
-
const photo = await this.getUserPhoto(result.accessToken);
|
|
1025
|
-
result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
|
|
1026
1329
|
const { profile } = await this.authHandler(result);
|
|
1027
1330
|
const response = {
|
|
1028
1331
|
providerInfo: {
|
|
@@ -1035,58 +1338,18 @@ class MicrosoftAuthProvider {
|
|
|
1035
1338
|
};
|
|
1036
1339
|
if (this.signInResolver) {
|
|
1037
1340
|
response.backstageIdentity = await this.signInResolver({
|
|
1038
|
-
result,
|
|
1039
|
-
profile
|
|
1040
|
-
}, {
|
|
1041
|
-
tokenIssuer: this.tokenIssuer,
|
|
1042
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1043
|
-
logger: this.logger
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
return response;
|
|
1047
|
-
}
|
|
1048
|
-
getUserPhoto(accessToken) {
|
|
1049
|
-
return new Promise((resolve) => {
|
|
1050
|
-
fetch__default["default"]("https://graph.microsoft.com/v1.0/me/photos/48x48/$value", {
|
|
1051
|
-
headers: {
|
|
1052
|
-
Authorization: `Bearer ${accessToken}`
|
|
1053
|
-
}
|
|
1054
|
-
}).then((response) => response.arrayBuffer()).then((arrayBuffer) => {
|
|
1055
|
-
const imageUrl = `data:image/jpeg;base64,${Buffer.from(arrayBuffer).toString("base64")}`;
|
|
1056
|
-
resolve(imageUrl);
|
|
1057
|
-
}).catch((error) => {
|
|
1058
|
-
this.logger.warn(`Could not retrieve user profile photo from Microsoft Graph API: ${error}`);
|
|
1059
|
-
resolve(void 0);
|
|
1060
|
-
});
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
const microsoftEmailSignInResolver = async (info, ctx) => {
|
|
1065
|
-
const { profile } = info;
|
|
1066
|
-
if (!profile.email) {
|
|
1067
|
-
throw new Error("Microsoft profile contained no email");
|
|
1068
|
-
}
|
|
1069
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1070
|
-
annotations: {
|
|
1071
|
-
"microsoft.com/email": profile.email
|
|
1341
|
+
result,
|
|
1342
|
+
profile
|
|
1343
|
+
}, {
|
|
1344
|
+
tokenIssuer: this.tokenIssuer,
|
|
1345
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1346
|
+
logger: this.logger
|
|
1347
|
+
});
|
|
1072
1348
|
}
|
|
1073
|
-
|
|
1074
|
-
const claims = getEntityClaims(entity);
|
|
1075
|
-
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1076
|
-
return { id: entity.metadata.name, entity, token };
|
|
1077
|
-
};
|
|
1078
|
-
const microsoftDefaultSignInResolver = async (info, ctx) => {
|
|
1079
|
-
const { profile } = info;
|
|
1080
|
-
if (!profile.email) {
|
|
1081
|
-
throw new Error("Profile contained no email");
|
|
1349
|
+
return response;
|
|
1082
1350
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1086
|
-
});
|
|
1087
|
-
return { id: userId, token };
|
|
1088
|
-
};
|
|
1089
|
-
const createMicrosoftProvider = (options) => {
|
|
1351
|
+
}
|
|
1352
|
+
const createGitlabProvider = (options) => {
|
|
1090
1353
|
return ({
|
|
1091
1354
|
providerId,
|
|
1092
1355
|
globalConfig,
|
|
@@ -1095,32 +1358,28 @@ const createMicrosoftProvider = (options) => {
|
|
|
1095
1358
|
catalogApi,
|
|
1096
1359
|
logger
|
|
1097
1360
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1098
|
-
var _a, _b;
|
|
1361
|
+
var _a, _b, _c;
|
|
1099
1362
|
const clientId = envConfig.getString("clientId");
|
|
1100
1363
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1101
|
-
const
|
|
1364
|
+
const audience = envConfig.getOptionalString("audience");
|
|
1365
|
+
const baseUrl = audience || "https://gitlab.com";
|
|
1102
1366
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1103
|
-
const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
|
|
1104
|
-
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
1105
1367
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1106
1368
|
catalogApi,
|
|
1107
1369
|
tokenIssuer
|
|
1108
1370
|
});
|
|
1109
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ?
|
|
1110
|
-
|
|
1111
|
-
});
|
|
1112
|
-
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : microsoftDefaultSignInResolver;
|
|
1371
|
+
const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
|
|
1372
|
+
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : gitlabDefaultSignInResolver;
|
|
1113
1373
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1114
1374
|
catalogIdentityClient,
|
|
1115
1375
|
tokenIssuer,
|
|
1116
1376
|
logger
|
|
1117
1377
|
});
|
|
1118
|
-
const provider = new
|
|
1378
|
+
const provider = new GitlabAuthProvider({
|
|
1119
1379
|
clientId,
|
|
1120
1380
|
clientSecret,
|
|
1121
1381
|
callbackUrl,
|
|
1122
|
-
|
|
1123
|
-
tokenUrl,
|
|
1382
|
+
baseUrl,
|
|
1124
1383
|
authHandler,
|
|
1125
1384
|
signInResolver,
|
|
1126
1385
|
catalogIdentityClient,
|
|
@@ -1135,30 +1394,24 @@ const createMicrosoftProvider = (options) => {
|
|
|
1135
1394
|
});
|
|
1136
1395
|
};
|
|
1137
1396
|
|
|
1138
|
-
class
|
|
1397
|
+
class GoogleAuthProvider {
|
|
1139
1398
|
constructor(options) {
|
|
1140
1399
|
this.signInResolver = options.signInResolver;
|
|
1141
1400
|
this.authHandler = options.authHandler;
|
|
1142
1401
|
this.tokenIssuer = options.tokenIssuer;
|
|
1143
1402
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1144
1403
|
this.logger = options.logger;
|
|
1145
|
-
this._strategy = new
|
|
1404
|
+
this._strategy = new passportGoogleOauth20.Strategy({
|
|
1146
1405
|
clientID: options.clientId,
|
|
1147
1406
|
clientSecret: options.clientSecret,
|
|
1148
1407
|
callbackURL: options.callbackUrl,
|
|
1149
|
-
|
|
1150
|
-
tokenURL: options.tokenUrl,
|
|
1151
|
-
passReqToCallback: false,
|
|
1152
|
-
scope: options.scope,
|
|
1153
|
-
customHeaders: options.includeBasicAuth ? {
|
|
1154
|
-
Authorization: `Basic ${this.encodeClientCredentials(options.clientId, options.clientSecret)}`
|
|
1155
|
-
} : void 0
|
|
1408
|
+
passReqToCallback: false
|
|
1156
1409
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1157
1410
|
done(void 0, {
|
|
1158
1411
|
fullProfile,
|
|
1412
|
+
params,
|
|
1159
1413
|
accessToken,
|
|
1160
|
-
refreshToken
|
|
1161
|
-
params
|
|
1414
|
+
refreshToken
|
|
1162
1415
|
}, {
|
|
1163
1416
|
refreshToken
|
|
1164
1417
|
});
|
|
@@ -1180,19 +1433,16 @@ class OAuth2AuthProvider {
|
|
|
1180
1433
|
};
|
|
1181
1434
|
}
|
|
1182
1435
|
async refresh(req) {
|
|
1183
|
-
const
|
|
1184
|
-
const {
|
|
1185
|
-
accessToken,
|
|
1186
|
-
params,
|
|
1187
|
-
refreshToken: updatedRefreshToken
|
|
1188
|
-
} = refreshTokenResponse;
|
|
1436
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1189
1437
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1190
|
-
return
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1438
|
+
return {
|
|
1439
|
+
response: await this.handleResult({
|
|
1440
|
+
fullProfile,
|
|
1441
|
+
params,
|
|
1442
|
+
accessToken
|
|
1443
|
+
}),
|
|
1444
|
+
refreshToken
|
|
1445
|
+
};
|
|
1196
1446
|
}
|
|
1197
1447
|
async handleResult(result) {
|
|
1198
1448
|
const { profile } = await this.authHandler(result);
|
|
@@ -1201,8 +1451,7 @@ class OAuth2AuthProvider {
|
|
|
1201
1451
|
idToken: result.params.id_token,
|
|
1202
1452
|
accessToken: result.accessToken,
|
|
1203
1453
|
scope: result.params.scope,
|
|
1204
|
-
expiresInSeconds: result.params.expires_in
|
|
1205
|
-
refreshToken: result.refreshToken
|
|
1454
|
+
expiresInSeconds: result.params.expires_in
|
|
1206
1455
|
},
|
|
1207
1456
|
profile
|
|
1208
1457
|
};
|
|
@@ -1218,22 +1467,44 @@ class OAuth2AuthProvider {
|
|
|
1218
1467
|
}
|
|
1219
1468
|
return response;
|
|
1220
1469
|
}
|
|
1221
|
-
encodeClientCredentials(clientID, clientSecret) {
|
|
1222
|
-
return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
|
|
1223
|
-
}
|
|
1224
1470
|
}
|
|
1225
|
-
const
|
|
1471
|
+
const googleEmailSignInResolver = async (info, ctx) => {
|
|
1226
1472
|
const { profile } = info;
|
|
1227
1473
|
if (!profile.email) {
|
|
1228
|
-
throw new Error("
|
|
1474
|
+
throw new Error("Google profile contained no email");
|
|
1475
|
+
}
|
|
1476
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1477
|
+
annotations: {
|
|
1478
|
+
"google.com/email": profile.email
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
const claims = getEntityClaims(entity);
|
|
1482
|
+
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1483
|
+
return { id: entity.metadata.name, entity, token };
|
|
1484
|
+
};
|
|
1485
|
+
const googleDefaultSignInResolver = async (info, ctx) => {
|
|
1486
|
+
const { profile } = info;
|
|
1487
|
+
if (!profile.email) {
|
|
1488
|
+
throw new Error("Google profile contained no email");
|
|
1489
|
+
}
|
|
1490
|
+
let userId;
|
|
1491
|
+
try {
|
|
1492
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1493
|
+
annotations: {
|
|
1494
|
+
"google.com/email": profile.email
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
userId = entity.metadata.name;
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
ctx.logger.warn(`Failed to look up user, ${error}, falling back to allowing login based on email pattern, this will probably break in the future`);
|
|
1500
|
+
userId = profile.email.split("@")[0];
|
|
1229
1501
|
}
|
|
1230
|
-
const userId = profile.email.split("@")[0];
|
|
1231
1502
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1232
1503
|
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1233
1504
|
});
|
|
1234
1505
|
return { id: userId, token };
|
|
1235
1506
|
};
|
|
1236
|
-
const
|
|
1507
|
+
const createGoogleProvider = (options) => {
|
|
1237
1508
|
return ({
|
|
1238
1509
|
providerId,
|
|
1239
1510
|
globalConfig,
|
|
@@ -1242,15 +1513,10 @@ const createOAuth2Provider = (options) => {
|
|
|
1242
1513
|
catalogApi,
|
|
1243
1514
|
logger
|
|
1244
1515
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1245
|
-
var _a, _b
|
|
1516
|
+
var _a, _b;
|
|
1246
1517
|
const clientId = envConfig.getString("clientId");
|
|
1247
1518
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1248
1519
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1249
|
-
const authorizationUrl = envConfig.getString("authorizationUrl");
|
|
1250
|
-
const tokenUrl = envConfig.getString("tokenUrl");
|
|
1251
|
-
const scope = envConfig.getOptionalString("scope");
|
|
1252
|
-
const includeBasicAuth = envConfig.getOptionalBoolean("includeBasicAuth");
|
|
1253
|
-
const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
|
|
1254
1520
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1255
1521
|
catalogApi,
|
|
1256
1522
|
tokenIssuer
|
|
@@ -1258,113 +1524,83 @@ const createOAuth2Provider = (options) => {
|
|
|
1258
1524
|
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1259
1525
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1260
1526
|
});
|
|
1261
|
-
const signInResolverFn = (
|
|
1527
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : googleDefaultSignInResolver;
|
|
1262
1528
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1263
1529
|
catalogIdentityClient,
|
|
1264
1530
|
tokenIssuer,
|
|
1265
1531
|
logger
|
|
1266
1532
|
});
|
|
1267
|
-
const provider = new
|
|
1533
|
+
const provider = new GoogleAuthProvider({
|
|
1268
1534
|
clientId,
|
|
1269
1535
|
clientSecret,
|
|
1270
|
-
tokenIssuer,
|
|
1271
|
-
catalogIdentityClient,
|
|
1272
1536
|
callbackUrl,
|
|
1273
1537
|
signInResolver,
|
|
1274
1538
|
authHandler,
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
logger,
|
|
1279
|
-
includeBasicAuth
|
|
1539
|
+
tokenIssuer,
|
|
1540
|
+
catalogIdentityClient,
|
|
1541
|
+
logger
|
|
1280
1542
|
});
|
|
1281
1543
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1282
|
-
disableRefresh,
|
|
1544
|
+
disableRefresh: false,
|
|
1283
1545
|
providerId,
|
|
1284
1546
|
tokenIssuer
|
|
1285
1547
|
});
|
|
1286
1548
|
});
|
|
1287
1549
|
};
|
|
1288
1550
|
|
|
1289
|
-
class
|
|
1551
|
+
class MicrosoftAuthProvider {
|
|
1290
1552
|
constructor(options) {
|
|
1291
|
-
this.implementation = this.setupStrategy(options);
|
|
1292
|
-
this.scope = options.scope;
|
|
1293
|
-
this.prompt = options.prompt;
|
|
1294
1553
|
this.signInResolver = options.signInResolver;
|
|
1295
1554
|
this.authHandler = options.authHandler;
|
|
1296
1555
|
this.tokenIssuer = options.tokenIssuer;
|
|
1297
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1298
1556
|
this.logger = options.logger;
|
|
1557
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1558
|
+
this._strategy = new passportMicrosoft.Strategy({
|
|
1559
|
+
clientID: options.clientId,
|
|
1560
|
+
clientSecret: options.clientSecret,
|
|
1561
|
+
callbackURL: options.callbackUrl,
|
|
1562
|
+
authorizationURL: options.authorizationUrl,
|
|
1563
|
+
tokenURL: options.tokenUrl,
|
|
1564
|
+
passReqToCallback: false
|
|
1565
|
+
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1566
|
+
done(void 0, { fullProfile, accessToken, params }, { refreshToken });
|
|
1567
|
+
});
|
|
1299
1568
|
}
|
|
1300
1569
|
async start(req) {
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
scope: req.scope || this.scope || "openid profile email",
|
|
1570
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
1571
|
+
scope: req.scope,
|
|
1304
1572
|
state: encodeState(req.state)
|
|
1305
|
-
};
|
|
1306
|
-
const prompt = this.prompt || "none";
|
|
1307
|
-
if (prompt !== "auto") {
|
|
1308
|
-
options.prompt = prompt;
|
|
1309
|
-
}
|
|
1310
|
-
return await executeRedirectStrategy(req, strategy, options);
|
|
1573
|
+
});
|
|
1311
1574
|
}
|
|
1312
1575
|
async handler(req) {
|
|
1313
|
-
const {
|
|
1314
|
-
const strategyResponse = await executeFrameHandlerStrategy(req, strategy);
|
|
1315
|
-
const {
|
|
1316
|
-
result: { userinfo, tokenset },
|
|
1317
|
-
privateInfo
|
|
1318
|
-
} = strategyResponse;
|
|
1319
|
-
const identityResponse = await this.handleResult({ tokenset, userinfo });
|
|
1576
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1320
1577
|
return {
|
|
1321
|
-
response:
|
|
1578
|
+
response: await this.handleResult(result),
|
|
1322
1579
|
refreshToken: privateInfo.refreshToken
|
|
1323
1580
|
};
|
|
1324
1581
|
}
|
|
1325
1582
|
async refresh(req) {
|
|
1326
|
-
const {
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
const client = new issuer.Client({
|
|
1337
|
-
access_type: "offline",
|
|
1338
|
-
client_id: options.clientId,
|
|
1339
|
-
client_secret: options.clientSecret,
|
|
1340
|
-
redirect_uris: [options.callbackUrl],
|
|
1341
|
-
response_types: ["code"],
|
|
1342
|
-
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1343
|
-
scope: options.scope || ""
|
|
1344
|
-
});
|
|
1345
|
-
const strategy = new openidClient.Strategy({
|
|
1346
|
-
client,
|
|
1347
|
-
passReqToCallback: false
|
|
1348
|
-
}, (tokenset, userinfo, done) => {
|
|
1349
|
-
if (typeof done !== "function") {
|
|
1350
|
-
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1351
|
-
}
|
|
1352
|
-
done(void 0, { tokenset, userinfo }, {
|
|
1353
|
-
refreshToken: tokenset.refresh_token
|
|
1354
|
-
});
|
|
1355
|
-
});
|
|
1356
|
-
strategy.error = console.error;
|
|
1357
|
-
return { strategy, client };
|
|
1583
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1584
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1585
|
+
return {
|
|
1586
|
+
response: await this.handleResult({
|
|
1587
|
+
fullProfile,
|
|
1588
|
+
params,
|
|
1589
|
+
accessToken
|
|
1590
|
+
}),
|
|
1591
|
+
refreshToken
|
|
1592
|
+
};
|
|
1358
1593
|
}
|
|
1359
1594
|
async handleResult(result) {
|
|
1595
|
+
const photo = await this.getUserPhoto(result.accessToken);
|
|
1596
|
+
result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
|
|
1360
1597
|
const { profile } = await this.authHandler(result);
|
|
1361
1598
|
const response = {
|
|
1362
1599
|
providerInfo: {
|
|
1363
|
-
idToken: result.
|
|
1364
|
-
accessToken: result.
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
expiresInSeconds: result.tokenset.expires_in
|
|
1600
|
+
idToken: result.params.id_token,
|
|
1601
|
+
accessToken: result.accessToken,
|
|
1602
|
+
scope: result.params.scope,
|
|
1603
|
+
expiresInSeconds: result.params.expires_in
|
|
1368
1604
|
},
|
|
1369
1605
|
profile
|
|
1370
1606
|
};
|
|
@@ -1380,8 +1616,37 @@ class OidcAuthProvider {
|
|
|
1380
1616
|
}
|
|
1381
1617
|
return response;
|
|
1382
1618
|
}
|
|
1619
|
+
getUserPhoto(accessToken) {
|
|
1620
|
+
return new Promise((resolve) => {
|
|
1621
|
+
fetch__default["default"]("https://graph.microsoft.com/v1.0/me/photos/48x48/$value", {
|
|
1622
|
+
headers: {
|
|
1623
|
+
Authorization: `Bearer ${accessToken}`
|
|
1624
|
+
}
|
|
1625
|
+
}).then((response) => response.arrayBuffer()).then((arrayBuffer) => {
|
|
1626
|
+
const imageUrl = `data:image/jpeg;base64,${Buffer.from(arrayBuffer).toString("base64")}`;
|
|
1627
|
+
resolve(imageUrl);
|
|
1628
|
+
}).catch((error) => {
|
|
1629
|
+
this.logger.warn(`Could not retrieve user profile photo from Microsoft Graph API: ${error}`);
|
|
1630
|
+
resolve(void 0);
|
|
1631
|
+
});
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1383
1634
|
}
|
|
1384
|
-
const
|
|
1635
|
+
const microsoftEmailSignInResolver = async (info, ctx) => {
|
|
1636
|
+
const { profile } = info;
|
|
1637
|
+
if (!profile.email) {
|
|
1638
|
+
throw new Error("Microsoft profile contained no email");
|
|
1639
|
+
}
|
|
1640
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1641
|
+
annotations: {
|
|
1642
|
+
"microsoft.com/email": profile.email
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
const claims = getEntityClaims(entity);
|
|
1646
|
+
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1647
|
+
return { id: entity.metadata.name, entity, token };
|
|
1648
|
+
};
|
|
1649
|
+
const microsoftDefaultSignInResolver = async (info, ctx) => {
|
|
1385
1650
|
const { profile } = info;
|
|
1386
1651
|
if (!profile.email) {
|
|
1387
1652
|
throw new Error("Profile contained no email");
|
|
@@ -1392,7 +1657,7 @@ const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
|
1392
1657
|
});
|
|
1393
1658
|
return { id: userId, token };
|
|
1394
1659
|
};
|
|
1395
|
-
const
|
|
1660
|
+
const createMicrosoftProvider = (options) => {
|
|
1396
1661
|
return ({
|
|
1397
1662
|
providerId,
|
|
1398
1663
|
globalConfig,
|
|
@@ -1404,41 +1669,34 @@ const createOidcProvider = (options) => {
|
|
|
1404
1669
|
var _a, _b;
|
|
1405
1670
|
const clientId = envConfig.getString("clientId");
|
|
1406
1671
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1672
|
+
const tenantId = envConfig.getString("tenantId");
|
|
1407
1673
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1408
|
-
const
|
|
1409
|
-
const
|
|
1410
|
-
const scope = envConfig.getOptionalString("scope");
|
|
1411
|
-
const prompt = envConfig.getOptionalString("prompt");
|
|
1674
|
+
const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
|
|
1675
|
+
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
1412
1676
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1413
1677
|
catalogApi,
|
|
1414
1678
|
tokenIssuer
|
|
1415
1679
|
});
|
|
1416
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({
|
|
1417
|
-
profile:
|
|
1418
|
-
displayName: userinfo.name,
|
|
1419
|
-
email: userinfo.email,
|
|
1420
|
-
picture: userinfo.picture
|
|
1421
|
-
}
|
|
1680
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1681
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1422
1682
|
});
|
|
1423
|
-
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b :
|
|
1683
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : microsoftDefaultSignInResolver;
|
|
1424
1684
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1425
1685
|
catalogIdentityClient,
|
|
1426
1686
|
tokenIssuer,
|
|
1427
1687
|
logger
|
|
1428
1688
|
});
|
|
1429
|
-
const provider = new
|
|
1689
|
+
const provider = new MicrosoftAuthProvider({
|
|
1430
1690
|
clientId,
|
|
1431
1691
|
clientSecret,
|
|
1432
1692
|
callbackUrl,
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
scope,
|
|
1436
|
-
prompt,
|
|
1437
|
-
signInResolver,
|
|
1693
|
+
authorizationUrl,
|
|
1694
|
+
tokenUrl,
|
|
1438
1695
|
authHandler,
|
|
1696
|
+
signInResolver,
|
|
1697
|
+
catalogIdentityClient,
|
|
1439
1698
|
logger,
|
|
1440
|
-
tokenIssuer
|
|
1441
|
-
catalogIdentityClient
|
|
1699
|
+
tokenIssuer
|
|
1442
1700
|
});
|
|
1443
1701
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1444
1702
|
disableRefresh: false,
|
|
@@ -1448,35 +1706,30 @@ const createOidcProvider = (options) => {
|
|
|
1448
1706
|
});
|
|
1449
1707
|
};
|
|
1450
1708
|
|
|
1451
|
-
class
|
|
1709
|
+
class OAuth2AuthProvider {
|
|
1452
1710
|
constructor(options) {
|
|
1453
|
-
this.
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
}
|
|
1460
|
-
};
|
|
1461
|
-
this._signInResolver = options.signInResolver;
|
|
1462
|
-
this._authHandler = options.authHandler;
|
|
1463
|
-
this._tokenIssuer = options.tokenIssuer;
|
|
1464
|
-
this._catalogIdentityClient = options.catalogIdentityClient;
|
|
1465
|
-
this._logger = options.logger;
|
|
1466
|
-
this._strategy = new passportOktaOauth.Strategy({
|
|
1711
|
+
this.signInResolver = options.signInResolver;
|
|
1712
|
+
this.authHandler = options.authHandler;
|
|
1713
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
1714
|
+
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1715
|
+
this.logger = options.logger;
|
|
1716
|
+
this._strategy = new OAuth2Strategy.Strategy({
|
|
1467
1717
|
clientID: options.clientId,
|
|
1468
1718
|
clientSecret: options.clientSecret,
|
|
1469
1719
|
callbackURL: options.callbackUrl,
|
|
1470
|
-
|
|
1720
|
+
authorizationURL: options.authorizationUrl,
|
|
1721
|
+
tokenURL: options.tokenUrl,
|
|
1471
1722
|
passReqToCallback: false,
|
|
1472
|
-
|
|
1473
|
-
|
|
1723
|
+
scope: options.scope,
|
|
1724
|
+
customHeaders: options.includeBasicAuth ? {
|
|
1725
|
+
Authorization: `Basic ${this.encodeClientCredentials(options.clientId, options.clientSecret)}`
|
|
1726
|
+
} : void 0
|
|
1474
1727
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1475
1728
|
done(void 0, {
|
|
1729
|
+
fullProfile,
|
|
1476
1730
|
accessToken,
|
|
1477
1731
|
refreshToken,
|
|
1478
|
-
params
|
|
1479
|
-
fullProfile
|
|
1732
|
+
params
|
|
1480
1733
|
}, {
|
|
1481
1734
|
refreshToken
|
|
1482
1735
|
});
|
|
@@ -1498,17 +1751,20 @@ class OktaAuthProvider {
|
|
|
1498
1751
|
};
|
|
1499
1752
|
}
|
|
1500
1753
|
async refresh(req) {
|
|
1501
|
-
const
|
|
1754
|
+
const refreshTokenResponse = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1755
|
+
const { accessToken, params, refreshToken } = refreshTokenResponse;
|
|
1502
1756
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1503
|
-
return
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1757
|
+
return {
|
|
1758
|
+
response: await this.handleResult({
|
|
1759
|
+
fullProfile,
|
|
1760
|
+
params,
|
|
1761
|
+
accessToken
|
|
1762
|
+
}),
|
|
1763
|
+
refreshToken
|
|
1764
|
+
};
|
|
1509
1765
|
}
|
|
1510
1766
|
async handleResult(result) {
|
|
1511
|
-
const { profile } = await this.
|
|
1767
|
+
const { profile } = await this.authHandler(result);
|
|
1512
1768
|
const response = {
|
|
1513
1769
|
providerInfo: {
|
|
1514
1770
|
idToken: result.params.id_token,
|
|
@@ -1518,37 +1774,26 @@ class OktaAuthProvider {
|
|
|
1518
1774
|
},
|
|
1519
1775
|
profile
|
|
1520
1776
|
};
|
|
1521
|
-
if (this.
|
|
1522
|
-
response.backstageIdentity = await this.
|
|
1777
|
+
if (this.signInResolver) {
|
|
1778
|
+
response.backstageIdentity = await this.signInResolver({
|
|
1523
1779
|
result,
|
|
1524
1780
|
profile
|
|
1525
1781
|
}, {
|
|
1526
|
-
tokenIssuer: this.
|
|
1527
|
-
catalogIdentityClient: this.
|
|
1528
|
-
logger: this.
|
|
1782
|
+
tokenIssuer: this.tokenIssuer,
|
|
1783
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1784
|
+
logger: this.logger
|
|
1529
1785
|
});
|
|
1530
1786
|
}
|
|
1531
1787
|
return response;
|
|
1532
1788
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
const { profile } = info;
|
|
1536
|
-
if (!profile.email) {
|
|
1537
|
-
throw new Error("Okta profile contained no email");
|
|
1789
|
+
encodeClientCredentials(clientID, clientSecret) {
|
|
1790
|
+
return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
|
|
1538
1791
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
"okta.com/email": profile.email
|
|
1542
|
-
}
|
|
1543
|
-
});
|
|
1544
|
-
const claims = getEntityClaims(entity);
|
|
1545
|
-
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1546
|
-
return { id: entity.metadata.name, entity, token };
|
|
1547
|
-
};
|
|
1548
|
-
const oktaDefaultSignInResolver = async (info, ctx) => {
|
|
1792
|
+
}
|
|
1793
|
+
const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
|
|
1549
1794
|
const { profile } = info;
|
|
1550
1795
|
if (!profile.email) {
|
|
1551
|
-
throw new Error("
|
|
1796
|
+
throw new Error("Profile contained no email");
|
|
1552
1797
|
}
|
|
1553
1798
|
const userId = profile.email.split("@")[0];
|
|
1554
1799
|
const token = await ctx.tokenIssuer.issueToken({
|
|
@@ -1556,7 +1801,7 @@ const oktaDefaultSignInResolver = async (info, ctx) => {
|
|
|
1556
1801
|
});
|
|
1557
1802
|
return { id: userId, token };
|
|
1558
1803
|
};
|
|
1559
|
-
const
|
|
1804
|
+
const createOAuth2Provider = (options) => {
|
|
1560
1805
|
return ({
|
|
1561
1806
|
providerId,
|
|
1562
1807
|
globalConfig,
|
|
@@ -1565,103 +1810,126 @@ const createOktaProvider = (_options) => {
|
|
|
1565
1810
|
catalogApi,
|
|
1566
1811
|
logger
|
|
1567
1812
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1568
|
-
var _a, _b;
|
|
1813
|
+
var _a, _b, _c;
|
|
1569
1814
|
const clientId = envConfig.getString("clientId");
|
|
1570
1815
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1571
|
-
const audience = envConfig.getString("audience");
|
|
1572
1816
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1817
|
+
const authorizationUrl = envConfig.getString("authorizationUrl");
|
|
1818
|
+
const tokenUrl = envConfig.getString("tokenUrl");
|
|
1819
|
+
const scope = envConfig.getOptionalString("scope");
|
|
1820
|
+
const includeBasicAuth = envConfig.getOptionalBoolean("includeBasicAuth");
|
|
1821
|
+
const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
|
|
1576
1822
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1577
1823
|
catalogApi,
|
|
1578
1824
|
tokenIssuer
|
|
1579
1825
|
});
|
|
1580
|
-
const authHandler = (
|
|
1826
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1581
1827
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1582
1828
|
});
|
|
1583
|
-
const signInResolverFn = (
|
|
1829
|
+
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver$1;
|
|
1584
1830
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1585
1831
|
catalogIdentityClient,
|
|
1586
1832
|
tokenIssuer,
|
|
1587
1833
|
logger
|
|
1588
1834
|
});
|
|
1589
|
-
const provider = new
|
|
1590
|
-
audience,
|
|
1835
|
+
const provider = new OAuth2AuthProvider({
|
|
1591
1836
|
clientId,
|
|
1592
1837
|
clientSecret,
|
|
1593
|
-
callbackUrl,
|
|
1594
|
-
authHandler,
|
|
1595
|
-
signInResolver,
|
|
1596
1838
|
tokenIssuer,
|
|
1597
1839
|
catalogIdentityClient,
|
|
1598
|
-
|
|
1840
|
+
callbackUrl,
|
|
1841
|
+
signInResolver,
|
|
1842
|
+
authHandler,
|
|
1843
|
+
authorizationUrl,
|
|
1844
|
+
tokenUrl,
|
|
1845
|
+
scope,
|
|
1846
|
+
logger,
|
|
1847
|
+
includeBasicAuth
|
|
1599
1848
|
});
|
|
1600
1849
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1601
|
-
disableRefresh
|
|
1850
|
+
disableRefresh,
|
|
1602
1851
|
providerId,
|
|
1603
1852
|
tokenIssuer
|
|
1604
1853
|
});
|
|
1605
1854
|
});
|
|
1606
1855
|
};
|
|
1607
1856
|
|
|
1608
|
-
class
|
|
1857
|
+
class OidcAuthProvider {
|
|
1609
1858
|
constructor(options) {
|
|
1859
|
+
this.implementation = this.setupStrategy(options);
|
|
1860
|
+
this.scope = options.scope;
|
|
1861
|
+
this.prompt = options.prompt;
|
|
1610
1862
|
this.signInResolver = options.signInResolver;
|
|
1611
1863
|
this.authHandler = options.authHandler;
|
|
1612
1864
|
this.tokenIssuer = options.tokenIssuer;
|
|
1613
1865
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1614
1866
|
this.logger = options.logger;
|
|
1615
|
-
this._strategy = new passportBitbucketOauth2.Strategy({
|
|
1616
|
-
clientID: options.clientId,
|
|
1617
|
-
clientSecret: options.clientSecret,
|
|
1618
|
-
callbackURL: options.callbackUrl,
|
|
1619
|
-
passReqToCallback: false
|
|
1620
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1621
|
-
done(void 0, {
|
|
1622
|
-
fullProfile,
|
|
1623
|
-
params,
|
|
1624
|
-
accessToken,
|
|
1625
|
-
refreshToken
|
|
1626
|
-
}, {
|
|
1627
|
-
refreshToken
|
|
1628
|
-
});
|
|
1629
|
-
});
|
|
1630
1867
|
}
|
|
1631
1868
|
async start(req) {
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
scope: req.scope,
|
|
1869
|
+
const { strategy } = await this.implementation;
|
|
1870
|
+
const options = {
|
|
1871
|
+
scope: req.scope || this.scope || "openid profile email",
|
|
1636
1872
|
state: encodeState(req.state)
|
|
1637
|
-
}
|
|
1873
|
+
};
|
|
1874
|
+
const prompt = this.prompt || "none";
|
|
1875
|
+
if (prompt !== "auto") {
|
|
1876
|
+
options.prompt = prompt;
|
|
1877
|
+
}
|
|
1878
|
+
return await executeRedirectStrategy(req, strategy, options);
|
|
1638
1879
|
}
|
|
1639
1880
|
async handler(req) {
|
|
1640
|
-
const {
|
|
1881
|
+
const { strategy } = await this.implementation;
|
|
1882
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
|
|
1641
1883
|
return {
|
|
1642
1884
|
response: await this.handleResult(result),
|
|
1643
1885
|
refreshToken: privateInfo.refreshToken
|
|
1644
1886
|
};
|
|
1645
1887
|
}
|
|
1646
1888
|
async refresh(req) {
|
|
1647
|
-
const {
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1889
|
+
const { client } = await this.implementation;
|
|
1890
|
+
const tokenset = await client.refresh(req.refreshToken);
|
|
1891
|
+
if (!tokenset.access_token) {
|
|
1892
|
+
throw new Error("Refresh failed");
|
|
1893
|
+
}
|
|
1894
|
+
const userinfo = await client.userinfo(tokenset.access_token);
|
|
1895
|
+
return {
|
|
1896
|
+
response: await this.handleResult({ tokenset, userinfo }),
|
|
1897
|
+
refreshToken: tokenset.refresh_token
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
async setupStrategy(options) {
|
|
1901
|
+
const issuer = await openidClient.Issuer.discover(options.metadataUrl);
|
|
1902
|
+
const client = new issuer.Client({
|
|
1903
|
+
access_type: "offline",
|
|
1904
|
+
client_id: options.clientId,
|
|
1905
|
+
client_secret: options.clientSecret,
|
|
1906
|
+
redirect_uris: [options.callbackUrl],
|
|
1907
|
+
response_types: ["code"],
|
|
1908
|
+
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1909
|
+
scope: options.scope || ""
|
|
1910
|
+
});
|
|
1911
|
+
const strategy = new openidClient.Strategy({
|
|
1912
|
+
client,
|
|
1913
|
+
passReqToCallback: false
|
|
1914
|
+
}, (tokenset, userinfo, done) => {
|
|
1915
|
+
if (typeof done !== "function") {
|
|
1916
|
+
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1917
|
+
}
|
|
1918
|
+
done(void 0, { tokenset, userinfo }, {
|
|
1919
|
+
refreshToken: tokenset.refresh_token
|
|
1920
|
+
});
|
|
1654
1921
|
});
|
|
1922
|
+
strategy.error = console.error;
|
|
1923
|
+
return { strategy, client };
|
|
1655
1924
|
}
|
|
1656
1925
|
async handleResult(result) {
|
|
1657
|
-
result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
|
|
1658
1926
|
const { profile } = await this.authHandler(result);
|
|
1659
1927
|
const response = {
|
|
1660
1928
|
providerInfo: {
|
|
1661
|
-
idToken: result.
|
|
1662
|
-
accessToken: result.
|
|
1663
|
-
scope: result.
|
|
1664
|
-
expiresInSeconds: result.
|
|
1929
|
+
idToken: result.tokenset.id_token,
|
|
1930
|
+
accessToken: result.tokenset.access_token,
|
|
1931
|
+
scope: result.tokenset.scope,
|
|
1932
|
+
expiresInSeconds: result.tokenset.expires_in
|
|
1665
1933
|
},
|
|
1666
1934
|
profile
|
|
1667
1935
|
};
|
|
@@ -1678,35 +1946,18 @@ class BitbucketAuthProvider {
|
|
|
1678
1946
|
return response;
|
|
1679
1947
|
}
|
|
1680
1948
|
}
|
|
1681
|
-
const
|
|
1682
|
-
const {
|
|
1683
|
-
if (!
|
|
1684
|
-
throw new Error("
|
|
1685
|
-
}
|
|
1686
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1687
|
-
annotations: {
|
|
1688
|
-
"bitbucket.org/username": result.fullProfile.username
|
|
1689
|
-
}
|
|
1690
|
-
});
|
|
1691
|
-
const claims = getEntityClaims(entity);
|
|
1692
|
-
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1693
|
-
return { id: entity.metadata.name, entity, token };
|
|
1694
|
-
};
|
|
1695
|
-
const bitbucketUserIdSignInResolver = async (info, ctx) => {
|
|
1696
|
-
const { result } = info;
|
|
1697
|
-
if (!result.fullProfile.id) {
|
|
1698
|
-
throw new Error("Bitbucket profile contained no User ID");
|
|
1949
|
+
const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
1950
|
+
const { profile } = info;
|
|
1951
|
+
if (!profile.email) {
|
|
1952
|
+
throw new Error("Profile contained no email");
|
|
1699
1953
|
}
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
}
|
|
1954
|
+
const userId = profile.email.split("@")[0];
|
|
1955
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
1956
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1704
1957
|
});
|
|
1705
|
-
|
|
1706
|
-
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
1707
|
-
return { id: entity.metadata.name, entity, token };
|
|
1958
|
+
return { id: userId, token };
|
|
1708
1959
|
};
|
|
1709
|
-
const
|
|
1960
|
+
const createOidcProvider = (options) => {
|
|
1710
1961
|
return ({
|
|
1711
1962
|
providerId,
|
|
1712
1963
|
globalConfig,
|
|
@@ -1715,26 +1966,44 @@ const createBitbucketProvider = (options) => {
|
|
|
1715
1966
|
catalogApi,
|
|
1716
1967
|
logger
|
|
1717
1968
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1718
|
-
var _a;
|
|
1969
|
+
var _a, _b;
|
|
1719
1970
|
const clientId = envConfig.getString("clientId");
|
|
1720
1971
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1721
1972
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1973
|
+
const metadataUrl = envConfig.getString("metadataUrl");
|
|
1974
|
+
const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
|
|
1975
|
+
const scope = envConfig.getOptionalString("scope");
|
|
1976
|
+
const prompt = envConfig.getOptionalString("prompt");
|
|
1722
1977
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1723
1978
|
catalogApi,
|
|
1724
1979
|
tokenIssuer
|
|
1725
1980
|
});
|
|
1726
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({
|
|
1727
|
-
profile:
|
|
1981
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ userinfo }) => ({
|
|
1982
|
+
profile: {
|
|
1983
|
+
displayName: userinfo.name,
|
|
1984
|
+
email: userinfo.email,
|
|
1985
|
+
picture: userinfo.picture
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oAuth2DefaultSignInResolver;
|
|
1989
|
+
const signInResolver = (info) => signInResolverFn(info, {
|
|
1990
|
+
catalogIdentityClient,
|
|
1991
|
+
tokenIssuer,
|
|
1992
|
+
logger
|
|
1728
1993
|
});
|
|
1729
|
-
const provider = new
|
|
1994
|
+
const provider = new OidcAuthProvider({
|
|
1730
1995
|
clientId,
|
|
1731
1996
|
clientSecret,
|
|
1732
1997
|
callbackUrl,
|
|
1733
|
-
|
|
1998
|
+
tokenSignedResponseAlg,
|
|
1999
|
+
metadataUrl,
|
|
2000
|
+
scope,
|
|
2001
|
+
prompt,
|
|
2002
|
+
signInResolver,
|
|
1734
2003
|
authHandler,
|
|
2004
|
+
logger,
|
|
1735
2005
|
tokenIssuer,
|
|
1736
|
-
catalogIdentityClient
|
|
1737
|
-
logger
|
|
2006
|
+
catalogIdentityClient
|
|
1738
2007
|
});
|
|
1739
2008
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1740
2009
|
disableRefresh: false,
|
|
@@ -1744,140 +2013,117 @@ const createBitbucketProvider = (options) => {
|
|
|
1744
2013
|
});
|
|
1745
2014
|
};
|
|
1746
2015
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
...options,
|
|
1756
|
-
authorizationURL: `https://auth.atlassian.com/authorize`,
|
|
1757
|
-
tokenURL: `https://auth.atlassian.com/oauth/token`,
|
|
1758
|
-
scope: Array.from(/* @__PURE__ */ new Set([...defaultScopes, ...scopes]))
|
|
1759
|
-
};
|
|
1760
|
-
super(optionsWithURLs, verify);
|
|
1761
|
-
this.profileURL = "https://api.atlassian.com/me";
|
|
1762
|
-
this.name = "atlassian";
|
|
1763
|
-
this._oauth2.useAuthorizationHeaderforGET(true);
|
|
1764
|
-
}
|
|
1765
|
-
authorizationParams() {
|
|
1766
|
-
return {
|
|
1767
|
-
audience: "api.atlassian.com",
|
|
1768
|
-
prompt: "consent"
|
|
1769
|
-
};
|
|
1770
|
-
}
|
|
1771
|
-
userProfile(accessToken, done) {
|
|
1772
|
-
this._oauth2.get(this.profileURL, accessToken, (err, body) => {
|
|
1773
|
-
if (err) {
|
|
1774
|
-
return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
|
|
1775
|
-
}
|
|
1776
|
-
if (!body) {
|
|
1777
|
-
return done(new Error("Failed to fetch user profile, body cannot be empty"));
|
|
1778
|
-
}
|
|
1779
|
-
try {
|
|
1780
|
-
const json = typeof body !== "string" ? body.toString() : body;
|
|
1781
|
-
const profile = AtlassianStrategy.parse(json);
|
|
1782
|
-
return done(null, profile);
|
|
1783
|
-
} catch (e) {
|
|
1784
|
-
return done(new Error("Failed to parse user profile"));
|
|
2016
|
+
class OktaAuthProvider {
|
|
2017
|
+
constructor(options) {
|
|
2018
|
+
this._store = {
|
|
2019
|
+
store(_req, cb) {
|
|
2020
|
+
cb(null, null);
|
|
2021
|
+
},
|
|
2022
|
+
verify(_req, _state, cb) {
|
|
2023
|
+
cb(null, true);
|
|
1785
2024
|
}
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
static parse(json) {
|
|
1789
|
-
const resp = JSON.parse(json);
|
|
1790
|
-
return {
|
|
1791
|
-
id: resp.account_id,
|
|
1792
|
-
provider: "atlassian",
|
|
1793
|
-
username: resp.nickname,
|
|
1794
|
-
displayName: resp.name,
|
|
1795
|
-
emails: [{ value: resp.email }],
|
|
1796
|
-
photos: [{ value: resp.picture }]
|
|
1797
2025
|
};
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
}) => ({
|
|
1805
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1806
|
-
});
|
|
1807
|
-
class AtlassianAuthProvider {
|
|
1808
|
-
constructor(options) {
|
|
1809
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1810
|
-
this.logger = options.logger;
|
|
1811
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
1812
|
-
this.authHandler = options.authHandler;
|
|
1813
|
-
this.signInResolver = options.signInResolver;
|
|
1814
|
-
this._strategy = new AtlassianStrategy({
|
|
2026
|
+
this._signInResolver = options.signInResolver;
|
|
2027
|
+
this._authHandler = options.authHandler;
|
|
2028
|
+
this._tokenIssuer = options.tokenIssuer;
|
|
2029
|
+
this._catalogIdentityClient = options.catalogIdentityClient;
|
|
2030
|
+
this._logger = options.logger;
|
|
2031
|
+
this._strategy = new passportOktaOauth.Strategy({
|
|
1815
2032
|
clientID: options.clientId,
|
|
1816
2033
|
clientSecret: options.clientSecret,
|
|
1817
2034
|
callbackURL: options.callbackUrl,
|
|
1818
|
-
|
|
2035
|
+
audience: options.audience,
|
|
2036
|
+
passReqToCallback: false,
|
|
2037
|
+
store: this._store,
|
|
2038
|
+
response_type: "code"
|
|
1819
2039
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1820
2040
|
done(void 0, {
|
|
1821
|
-
fullProfile,
|
|
1822
2041
|
accessToken,
|
|
1823
2042
|
refreshToken,
|
|
1824
|
-
params
|
|
2043
|
+
params,
|
|
2044
|
+
fullProfile
|
|
2045
|
+
}, {
|
|
2046
|
+
refreshToken
|
|
1825
2047
|
});
|
|
1826
2048
|
});
|
|
1827
2049
|
}
|
|
1828
2050
|
async start(req) {
|
|
1829
2051
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
2052
|
+
accessType: "offline",
|
|
2053
|
+
prompt: "consent",
|
|
2054
|
+
scope: req.scope,
|
|
1830
2055
|
state: encodeState(req.state)
|
|
1831
2056
|
});
|
|
1832
2057
|
}
|
|
1833
2058
|
async handler(req) {
|
|
1834
|
-
|
|
1835
|
-
const { result } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2059
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1836
2060
|
return {
|
|
1837
2061
|
response: await this.handleResult(result),
|
|
1838
|
-
refreshToken:
|
|
2062
|
+
refreshToken: privateInfo.refreshToken
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
async refresh(req) {
|
|
2066
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2067
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2068
|
+
return {
|
|
2069
|
+
response: await this.handleResult({
|
|
2070
|
+
fullProfile,
|
|
2071
|
+
params,
|
|
2072
|
+
accessToken
|
|
2073
|
+
}),
|
|
2074
|
+
refreshToken
|
|
1839
2075
|
};
|
|
1840
2076
|
}
|
|
1841
2077
|
async handleResult(result) {
|
|
1842
|
-
const { profile } = await this.
|
|
2078
|
+
const { profile } = await this._authHandler(result);
|
|
1843
2079
|
const response = {
|
|
1844
2080
|
providerInfo: {
|
|
1845
2081
|
idToken: result.params.id_token,
|
|
1846
2082
|
accessToken: result.accessToken,
|
|
1847
|
-
refreshToken: result.refreshToken,
|
|
1848
2083
|
scope: result.params.scope,
|
|
1849
2084
|
expiresInSeconds: result.params.expires_in
|
|
1850
2085
|
},
|
|
1851
2086
|
profile
|
|
1852
2087
|
};
|
|
1853
|
-
if (this.
|
|
1854
|
-
response.backstageIdentity = await this.
|
|
2088
|
+
if (this._signInResolver) {
|
|
2089
|
+
response.backstageIdentity = await this._signInResolver({
|
|
1855
2090
|
result,
|
|
1856
2091
|
profile
|
|
1857
2092
|
}, {
|
|
1858
|
-
tokenIssuer: this.
|
|
1859
|
-
catalogIdentityClient: this.
|
|
1860
|
-
logger: this.
|
|
2093
|
+
tokenIssuer: this._tokenIssuer,
|
|
2094
|
+
catalogIdentityClient: this._catalogIdentityClient,
|
|
2095
|
+
logger: this._logger
|
|
1861
2096
|
});
|
|
1862
2097
|
}
|
|
1863
2098
|
return response;
|
|
1864
2099
|
}
|
|
1865
|
-
async refresh(req) {
|
|
1866
|
-
const {
|
|
1867
|
-
accessToken,
|
|
1868
|
-
params,
|
|
1869
|
-
refreshToken: newRefreshToken
|
|
1870
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1871
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1872
|
-
return this.handleResult({
|
|
1873
|
-
fullProfile,
|
|
1874
|
-
params,
|
|
1875
|
-
accessToken,
|
|
1876
|
-
refreshToken: newRefreshToken
|
|
1877
|
-
});
|
|
1878
|
-
}
|
|
1879
2100
|
}
|
|
1880
|
-
const
|
|
2101
|
+
const oktaEmailSignInResolver = async (info, ctx) => {
|
|
2102
|
+
const { profile } = info;
|
|
2103
|
+
if (!profile.email) {
|
|
2104
|
+
throw new Error("Okta profile contained no email");
|
|
2105
|
+
}
|
|
2106
|
+
const entity = await ctx.catalogIdentityClient.findUser({
|
|
2107
|
+
annotations: {
|
|
2108
|
+
"okta.com/email": profile.email
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
const claims = getEntityClaims(entity);
|
|
2112
|
+
const token = await ctx.tokenIssuer.issueToken({ claims });
|
|
2113
|
+
return { id: entity.metadata.name, entity, token };
|
|
2114
|
+
};
|
|
2115
|
+
const oktaDefaultSignInResolver = async (info, ctx) => {
|
|
2116
|
+
const { profile } = info;
|
|
2117
|
+
if (!profile.email) {
|
|
2118
|
+
throw new Error("Okta profile contained no email");
|
|
2119
|
+
}
|
|
2120
|
+
const userId = profile.email.split("@")[0];
|
|
2121
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
2122
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
2123
|
+
});
|
|
2124
|
+
return { id: userId, token };
|
|
2125
|
+
};
|
|
2126
|
+
const createOktaProvider = (_options) => {
|
|
1881
2127
|
return ({
|
|
1882
2128
|
providerId,
|
|
1883
2129
|
globalConfig,
|
|
@@ -1889,158 +2135,165 @@ const createAtlassianProvider = (options) => {
|
|
|
1889
2135
|
var _a, _b;
|
|
1890
2136
|
const clientId = envConfig.getString("clientId");
|
|
1891
2137
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1892
|
-
const
|
|
2138
|
+
const audience = envConfig.getString("audience");
|
|
1893
2139
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2140
|
+
if (!audience.startsWith("https://")) {
|
|
2141
|
+
throw new Error("URL for 'audience' must start with 'https://'.");
|
|
2142
|
+
}
|
|
1894
2143
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1895
2144
|
catalogApi,
|
|
1896
2145
|
tokenIssuer
|
|
1897
2146
|
});
|
|
1898
|
-
const authHandler = (
|
|
1899
|
-
|
|
2147
|
+
const authHandler = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({ fullProfile, params }) => ({
|
|
2148
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2149
|
+
});
|
|
2150
|
+
const signInResolverFn = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
|
|
2151
|
+
const signInResolver = (info) => signInResolverFn(info, {
|
|
2152
|
+
catalogIdentityClient,
|
|
2153
|
+
tokenIssuer,
|
|
2154
|
+
logger
|
|
2155
|
+
});
|
|
2156
|
+
const provider = new OktaAuthProvider({
|
|
2157
|
+
audience,
|
|
1900
2158
|
clientId,
|
|
1901
2159
|
clientSecret,
|
|
1902
|
-
scopes,
|
|
1903
2160
|
callbackUrl,
|
|
1904
2161
|
authHandler,
|
|
1905
|
-
signInResolver
|
|
2162
|
+
signInResolver,
|
|
2163
|
+
tokenIssuer,
|
|
1906
2164
|
catalogIdentityClient,
|
|
1907
|
-
logger
|
|
1908
|
-
tokenIssuer
|
|
2165
|
+
logger
|
|
1909
2166
|
});
|
|
1910
2167
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1911
|
-
disableRefresh:
|
|
2168
|
+
disableRefresh: false,
|
|
1912
2169
|
providerId,
|
|
1913
2170
|
tokenIssuer
|
|
1914
2171
|
});
|
|
1915
2172
|
});
|
|
1916
2173
|
};
|
|
1917
2174
|
|
|
1918
|
-
|
|
1919
|
-
const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
|
|
1920
|
-
const getJWTHeaders = (input) => {
|
|
1921
|
-
const encoded = input.split(".")[0];
|
|
1922
|
-
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
|
1923
|
-
};
|
|
1924
|
-
class AwsAlbAuthProvider {
|
|
2175
|
+
class OneLoginProvider {
|
|
1925
2176
|
constructor(options) {
|
|
1926
|
-
this.region = options.region;
|
|
1927
|
-
this.issuer = options.issuer;
|
|
1928
|
-
this.authHandler = options.authHandler;
|
|
1929
2177
|
this.signInResolver = options.signInResolver;
|
|
2178
|
+
this.authHandler = options.authHandler;
|
|
1930
2179
|
this.tokenIssuer = options.tokenIssuer;
|
|
1931
2180
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1932
2181
|
this.logger = options.logger;
|
|
1933
|
-
this.
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
id: claims.sub,
|
|
1971
|
-
displayName: claims.name,
|
|
1972
|
-
username: claims.email.split("@")[0].toLowerCase(),
|
|
1973
|
-
name: {
|
|
1974
|
-
familyName: claims.family_name,
|
|
1975
|
-
givenName: claims.given_name
|
|
1976
|
-
},
|
|
1977
|
-
emails: [{ value: claims.email.toLowerCase() }],
|
|
1978
|
-
photos: [{ value: claims.picture }]
|
|
1979
|
-
};
|
|
1980
|
-
return {
|
|
2182
|
+
this._strategy = new passportOneloginOauth.Strategy({
|
|
2183
|
+
issuer: options.issuer,
|
|
2184
|
+
clientID: options.clientId,
|
|
2185
|
+
clientSecret: options.clientSecret,
|
|
2186
|
+
callbackURL: options.callbackUrl,
|
|
2187
|
+
passReqToCallback: false
|
|
2188
|
+
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2189
|
+
done(void 0, {
|
|
2190
|
+
accessToken,
|
|
2191
|
+
refreshToken,
|
|
2192
|
+
params,
|
|
2193
|
+
fullProfile
|
|
2194
|
+
}, {
|
|
2195
|
+
refreshToken
|
|
2196
|
+
});
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
async start(req) {
|
|
2200
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
2201
|
+
accessType: "offline",
|
|
2202
|
+
prompt: "consent",
|
|
2203
|
+
scope: "openid",
|
|
2204
|
+
state: encodeState(req.state)
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
async handler(req) {
|
|
2208
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2209
|
+
return {
|
|
2210
|
+
response: await this.handleResult(result),
|
|
2211
|
+
refreshToken: privateInfo.refreshToken
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
async refresh(req) {
|
|
2215
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2216
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2217
|
+
return {
|
|
2218
|
+
response: await this.handleResult({
|
|
1981
2219
|
fullProfile,
|
|
1982
|
-
|
|
2220
|
+
params,
|
|
1983
2221
|
accessToken
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
}
|
|
2222
|
+
}),
|
|
2223
|
+
refreshToken
|
|
2224
|
+
};
|
|
1988
2225
|
}
|
|
1989
2226
|
async handleResult(result) {
|
|
1990
2227
|
const { profile } = await this.authHandler(result);
|
|
1991
|
-
const
|
|
1992
|
-
result,
|
|
1993
|
-
profile
|
|
1994
|
-
}, {
|
|
1995
|
-
tokenIssuer: this.tokenIssuer,
|
|
1996
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1997
|
-
logger: this.logger
|
|
1998
|
-
});
|
|
1999
|
-
return {
|
|
2228
|
+
const response = {
|
|
2000
2229
|
providerInfo: {
|
|
2230
|
+
idToken: result.params.id_token,
|
|
2001
2231
|
accessToken: result.accessToken,
|
|
2002
|
-
|
|
2232
|
+
scope: result.params.scope,
|
|
2233
|
+
expiresInSeconds: result.params.expires_in
|
|
2003
2234
|
},
|
|
2004
|
-
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
|
|
2005
2235
|
profile
|
|
2006
2236
|
};
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2237
|
+
if (this.signInResolver) {
|
|
2238
|
+
response.backstageIdentity = await this.signInResolver({
|
|
2239
|
+
result,
|
|
2240
|
+
profile
|
|
2241
|
+
}, {
|
|
2242
|
+
tokenIssuer: this.tokenIssuer,
|
|
2243
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
2244
|
+
logger: this.logger
|
|
2245
|
+
});
|
|
2012
2246
|
}
|
|
2013
|
-
|
|
2014
|
-
const keyValue = crypto__namespace.createPublicKey(keyText);
|
|
2015
|
-
this.keyCache.set(keyId, keyValue.export({ format: "pem", type: "spki" }));
|
|
2016
|
-
return keyValue;
|
|
2247
|
+
return response;
|
|
2017
2248
|
}
|
|
2018
2249
|
}
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2250
|
+
const defaultSignInResolver = async (info) => {
|
|
2251
|
+
const { profile } = info;
|
|
2252
|
+
if (!profile.email) {
|
|
2253
|
+
throw new Error("OIDC profile contained no email");
|
|
2254
|
+
}
|
|
2255
|
+
const id = profile.email.split("@")[0];
|
|
2256
|
+
return { id, token: "" };
|
|
2257
|
+
};
|
|
2258
|
+
const createOneLoginProvider = (options) => {
|
|
2259
|
+
return ({
|
|
2260
|
+
providerId,
|
|
2261
|
+
globalConfig,
|
|
2262
|
+
config,
|
|
2263
|
+
tokenIssuer,
|
|
2264
|
+
catalogApi,
|
|
2265
|
+
logger
|
|
2266
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2267
|
+
var _a, _b;
|
|
2268
|
+
const clientId = envConfig.getString("clientId");
|
|
2269
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
2270
|
+
const issuer = envConfig.getString("issuer");
|
|
2271
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2026
2272
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2027
2273
|
catalogApi,
|
|
2028
2274
|
tokenIssuer
|
|
2029
2275
|
});
|
|
2030
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
2031
|
-
profile: makeProfileInfo(fullProfile)
|
|
2276
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
2277
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2032
2278
|
});
|
|
2033
|
-
const signInResolver = options == null ? void 0 : options.signIn.resolver;
|
|
2034
|
-
|
|
2035
|
-
|
|
2279
|
+
const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
|
|
2280
|
+
const provider = new OneLoginProvider({
|
|
2281
|
+
clientId,
|
|
2282
|
+
clientSecret,
|
|
2283
|
+
callbackUrl,
|
|
2036
2284
|
issuer,
|
|
2037
|
-
signInResolver,
|
|
2038
2285
|
authHandler,
|
|
2286
|
+
signInResolver,
|
|
2039
2287
|
tokenIssuer,
|
|
2040
2288
|
catalogIdentityClient,
|
|
2041
2289
|
logger
|
|
2042
2290
|
});
|
|
2043
|
-
|
|
2291
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2292
|
+
disableRefresh: false,
|
|
2293
|
+
providerId,
|
|
2294
|
+
tokenIssuer
|
|
2295
|
+
});
|
|
2296
|
+
});
|
|
2044
2297
|
};
|
|
2045
2298
|
|
|
2046
2299
|
class SamlAuthProvider {
|
|
@@ -2151,191 +2404,6 @@ const createSamlProvider = (options) => {
|
|
|
2151
2404
|
};
|
|
2152
2405
|
};
|
|
2153
2406
|
|
|
2154
|
-
class Auth0Strategy extends OAuth2Strategy__default["default"] {
|
|
2155
|
-
constructor(options, verify) {
|
|
2156
|
-
const optionsWithURLs = {
|
|
2157
|
-
...options,
|
|
2158
|
-
authorizationURL: `https://${options.domain}/authorize`,
|
|
2159
|
-
tokenURL: `https://${options.domain}/oauth/token`,
|
|
2160
|
-
userInfoURL: `https://${options.domain}/userinfo`,
|
|
2161
|
-
apiUrl: `https://${options.domain}/api`
|
|
2162
|
-
};
|
|
2163
|
-
super(optionsWithURLs, verify);
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
class Auth0AuthProvider {
|
|
2168
|
-
constructor(options) {
|
|
2169
|
-
this._strategy = new Auth0Strategy({
|
|
2170
|
-
clientID: options.clientId,
|
|
2171
|
-
clientSecret: options.clientSecret,
|
|
2172
|
-
callbackURL: options.callbackUrl,
|
|
2173
|
-
domain: options.domain,
|
|
2174
|
-
passReqToCallback: false
|
|
2175
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2176
|
-
done(void 0, {
|
|
2177
|
-
fullProfile,
|
|
2178
|
-
accessToken,
|
|
2179
|
-
refreshToken,
|
|
2180
|
-
params
|
|
2181
|
-
}, {
|
|
2182
|
-
refreshToken
|
|
2183
|
-
});
|
|
2184
|
-
});
|
|
2185
|
-
}
|
|
2186
|
-
async start(req) {
|
|
2187
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
2188
|
-
accessType: "offline",
|
|
2189
|
-
prompt: "consent",
|
|
2190
|
-
scope: req.scope,
|
|
2191
|
-
state: encodeState(req.state)
|
|
2192
|
-
});
|
|
2193
|
-
}
|
|
2194
|
-
async handler(req) {
|
|
2195
|
-
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2196
|
-
const profile = makeProfileInfo(result.fullProfile, result.params.id_token);
|
|
2197
|
-
return {
|
|
2198
|
-
response: await this.populateIdentity({
|
|
2199
|
-
profile,
|
|
2200
|
-
providerInfo: {
|
|
2201
|
-
idToken: result.params.id_token,
|
|
2202
|
-
accessToken: result.accessToken,
|
|
2203
|
-
scope: result.params.scope,
|
|
2204
|
-
expiresInSeconds: result.params.expires_in
|
|
2205
|
-
}
|
|
2206
|
-
}),
|
|
2207
|
-
refreshToken: privateInfo.refreshToken
|
|
2208
|
-
};
|
|
2209
|
-
}
|
|
2210
|
-
async refresh(req) {
|
|
2211
|
-
const { accessToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2212
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2213
|
-
const profile = makeProfileInfo(fullProfile, params.id_token);
|
|
2214
|
-
return this.populateIdentity({
|
|
2215
|
-
providerInfo: {
|
|
2216
|
-
accessToken,
|
|
2217
|
-
idToken: params.id_token,
|
|
2218
|
-
expiresInSeconds: params.expires_in,
|
|
2219
|
-
scope: params.scope
|
|
2220
|
-
},
|
|
2221
|
-
profile
|
|
2222
|
-
});
|
|
2223
|
-
}
|
|
2224
|
-
async populateIdentity(response) {
|
|
2225
|
-
const { profile } = response;
|
|
2226
|
-
if (!profile.email) {
|
|
2227
|
-
throw new Error("Profile does not contain an email");
|
|
2228
|
-
}
|
|
2229
|
-
const id = profile.email.split("@")[0];
|
|
2230
|
-
return { ...response, backstageIdentity: { id, token: "" } };
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
const createAuth0Provider = (_options) => {
|
|
2234
|
-
return ({ providerId, globalConfig, config, tokenIssuer }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2235
|
-
const clientId = envConfig.getString("clientId");
|
|
2236
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2237
|
-
const domain = envConfig.getString("domain");
|
|
2238
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2239
|
-
const provider = new Auth0AuthProvider({
|
|
2240
|
-
clientId,
|
|
2241
|
-
clientSecret,
|
|
2242
|
-
callbackUrl,
|
|
2243
|
-
domain
|
|
2244
|
-
});
|
|
2245
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2246
|
-
disableRefresh: true,
|
|
2247
|
-
providerId,
|
|
2248
|
-
tokenIssuer
|
|
2249
|
-
});
|
|
2250
|
-
});
|
|
2251
|
-
};
|
|
2252
|
-
|
|
2253
|
-
class OneLoginProvider {
|
|
2254
|
-
constructor(options) {
|
|
2255
|
-
this._strategy = new passportOneloginOauth.Strategy({
|
|
2256
|
-
issuer: options.issuer,
|
|
2257
|
-
clientID: options.clientId,
|
|
2258
|
-
clientSecret: options.clientSecret,
|
|
2259
|
-
callbackURL: options.callbackUrl,
|
|
2260
|
-
passReqToCallback: false
|
|
2261
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2262
|
-
done(void 0, {
|
|
2263
|
-
accessToken,
|
|
2264
|
-
refreshToken,
|
|
2265
|
-
params,
|
|
2266
|
-
fullProfile
|
|
2267
|
-
}, {
|
|
2268
|
-
refreshToken
|
|
2269
|
-
});
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2272
|
-
async start(req) {
|
|
2273
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
2274
|
-
accessType: "offline",
|
|
2275
|
-
prompt: "consent",
|
|
2276
|
-
scope: "openid",
|
|
2277
|
-
state: encodeState(req.state)
|
|
2278
|
-
});
|
|
2279
|
-
}
|
|
2280
|
-
async handler(req) {
|
|
2281
|
-
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2282
|
-
const profile = makeProfileInfo(result.fullProfile, result.params.id_token);
|
|
2283
|
-
return {
|
|
2284
|
-
response: await this.populateIdentity({
|
|
2285
|
-
profile,
|
|
2286
|
-
providerInfo: {
|
|
2287
|
-
idToken: result.params.id_token,
|
|
2288
|
-
accessToken: result.accessToken,
|
|
2289
|
-
scope: result.params.scope,
|
|
2290
|
-
expiresInSeconds: result.params.expires_in
|
|
2291
|
-
}
|
|
2292
|
-
}),
|
|
2293
|
-
refreshToken: privateInfo.refreshToken
|
|
2294
|
-
};
|
|
2295
|
-
}
|
|
2296
|
-
async refresh(req) {
|
|
2297
|
-
const { accessToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2298
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2299
|
-
const profile = makeProfileInfo(fullProfile, params.id_token);
|
|
2300
|
-
return this.populateIdentity({
|
|
2301
|
-
providerInfo: {
|
|
2302
|
-
accessToken,
|
|
2303
|
-
idToken: params.id_token,
|
|
2304
|
-
expiresInSeconds: params.expires_in,
|
|
2305
|
-
scope: params.scope
|
|
2306
|
-
},
|
|
2307
|
-
profile
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
async populateIdentity(response) {
|
|
2311
|
-
const { profile } = response;
|
|
2312
|
-
if (!profile.email) {
|
|
2313
|
-
throw new Error("OIDC profile contained no email");
|
|
2314
|
-
}
|
|
2315
|
-
const id = profile.email.split("@")[0];
|
|
2316
|
-
return { ...response, backstageIdentity: { id, token: "" } };
|
|
2317
|
-
}
|
|
2318
|
-
}
|
|
2319
|
-
const createOneLoginProvider = (_options) => {
|
|
2320
|
-
return ({ providerId, globalConfig, config, tokenIssuer }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2321
|
-
const clientId = envConfig.getString("clientId");
|
|
2322
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2323
|
-
const issuer = envConfig.getString("issuer");
|
|
2324
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2325
|
-
const provider = new OneLoginProvider({
|
|
2326
|
-
clientId,
|
|
2327
|
-
clientSecret,
|
|
2328
|
-
callbackUrl,
|
|
2329
|
-
issuer
|
|
2330
|
-
});
|
|
2331
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2332
|
-
disableRefresh: false,
|
|
2333
|
-
providerId,
|
|
2334
|
-
tokenIssuer
|
|
2335
|
-
});
|
|
2336
|
-
});
|
|
2337
|
-
};
|
|
2338
|
-
|
|
2339
2407
|
const factories = {
|
|
2340
2408
|
google: createGoogleProvider(),
|
|
2341
2409
|
github: createGithubProvider(),
|
|
@@ -2801,6 +2869,7 @@ exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
|
2801
2869
|
exports.bitbucketUserIdSignInResolver = bitbucketUserIdSignInResolver;
|
|
2802
2870
|
exports.bitbucketUsernameSignInResolver = bitbucketUsernameSignInResolver;
|
|
2803
2871
|
exports.createAtlassianProvider = createAtlassianProvider;
|
|
2872
|
+
exports.createAuth0Provider = createAuth0Provider;
|
|
2804
2873
|
exports.createAwsAlbProvider = createAwsAlbProvider;
|
|
2805
2874
|
exports.createBitbucketProvider = createBitbucketProvider;
|
|
2806
2875
|
exports.createGithubProvider = createGithubProvider;
|
|
@@ -2810,6 +2879,7 @@ exports.createMicrosoftProvider = createMicrosoftProvider;
|
|
|
2810
2879
|
exports.createOAuth2Provider = createOAuth2Provider;
|
|
2811
2880
|
exports.createOidcProvider = createOidcProvider;
|
|
2812
2881
|
exports.createOktaProvider = createOktaProvider;
|
|
2882
|
+
exports.createOneLoginProvider = createOneLoginProvider;
|
|
2813
2883
|
exports.createOriginFilter = createOriginFilter;
|
|
2814
2884
|
exports.createRouter = createRouter;
|
|
2815
2885
|
exports.createSamlProvider = createSamlProvider;
|