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