@backstage/plugin-auth-backend 0.4.10 → 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 +80 -0
- package/dist/index.cjs.js +1319 -1220
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +252 -117
- package/package.json +7 -8
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');
|
|
@@ -46,143 +45,81 @@ function _interopNamespace(e) {
|
|
|
46
45
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
47
46
|
Object.defineProperty(n, k, d.get ? d : {
|
|
48
47
|
enumerable: true,
|
|
49
|
-
get: function () {
|
|
50
|
-
return e[k];
|
|
51
|
-
}
|
|
48
|
+
get: function () { return e[k]; }
|
|
52
49
|
});
|
|
53
50
|
}
|
|
54
51
|
});
|
|
55
52
|
}
|
|
56
|
-
n[
|
|
53
|
+
n["default"] = e;
|
|
57
54
|
return Object.freeze(n);
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
61
58
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
62
59
|
var cookieParser__default = /*#__PURE__*/_interopDefaultLegacy(cookieParser);
|
|
63
|
-
var
|
|
60
|
+
var OAuth2Strategy__default = /*#__PURE__*/_interopDefaultLegacy(OAuth2Strategy);
|
|
64
61
|
var pickBy__default = /*#__PURE__*/_interopDefaultLegacy(pickBy);
|
|
65
62
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
66
63
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
67
|
-
var
|
|
68
|
-
var OAuth2Strategy__default = /*#__PURE__*/_interopDefaultLegacy(OAuth2Strategy);
|
|
64
|
+
var jwtDecoder__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecoder);
|
|
69
65
|
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
70
66
|
var NodeCache__default = /*#__PURE__*/_interopDefaultLegacy(NodeCache);
|
|
71
67
|
var session__default = /*#__PURE__*/_interopDefaultLegacy(session);
|
|
72
68
|
var passport__default = /*#__PURE__*/_interopDefaultLegacy(passport);
|
|
73
69
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
email = firstEmail.value;
|
|
80
|
-
}
|
|
81
|
-
let picture = void 0;
|
|
82
|
-
if (profile.avatarUrl) {
|
|
83
|
-
picture = profile.avatarUrl;
|
|
84
|
-
} else if (profile.photos && profile.photos.length > 0) {
|
|
85
|
-
const [firstPhoto] = profile.photos;
|
|
86
|
-
picture = firstPhoto.value;
|
|
87
|
-
}
|
|
88
|
-
let displayName = (_b = (_a = profile.displayName) != null ? _a : profile.username) != null ? _b : profile.id;
|
|
89
|
-
if ((!email || !picture || !displayName) && idToken) {
|
|
90
|
-
try {
|
|
91
|
-
const decoded = jwtDecoder__default['default'](idToken);
|
|
92
|
-
if (!email && decoded.email) {
|
|
93
|
-
email = decoded.email;
|
|
94
|
-
}
|
|
95
|
-
if (!picture && decoded.picture) {
|
|
96
|
-
picture = decoded.picture;
|
|
97
|
-
}
|
|
98
|
-
if (!displayName && decoded.name) {
|
|
99
|
-
displayName = decoded.name;
|
|
100
|
-
}
|
|
101
|
-
} catch (e) {
|
|
102
|
-
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");
|
|
103
75
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
const executeRedirectStrategy = async (req, providerStrategy, options) => {
|
|
112
|
-
return new Promise((resolve) => {
|
|
113
|
-
const strategy = Object.create(providerStrategy);
|
|
114
|
-
strategy.redirect = (url, status) => {
|
|
115
|
-
resolve({url, status: status != null ? status : void 0});
|
|
116
|
-
};
|
|
117
|
-
strategy.authenticate(req, {...options});
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
const executeFrameHandlerStrategy = async (req, providerStrategy) => {
|
|
121
|
-
return new Promise((resolve, reject) => {
|
|
122
|
-
const strategy = Object.create(providerStrategy);
|
|
123
|
-
strategy.success = (result, privateInfo) => {
|
|
124
|
-
resolve({result, privateInfo});
|
|
125
|
-
};
|
|
126
|
-
strategy.fail = (info) => {
|
|
127
|
-
var _a;
|
|
128
|
-
reject(new Error(`Authentication rejected, ${(_a = info.message) != null ? _a : ""}`));
|
|
129
|
-
};
|
|
130
|
-
strategy.error = (error) => {
|
|
131
|
-
var _a;
|
|
132
|
-
let message = `Authentication failed, ${error.message}`;
|
|
133
|
-
if ((_a = error.oauthError) == null ? void 0 : _a.data) {
|
|
134
|
-
try {
|
|
135
|
-
const errorData = JSON.parse(error.oauthError.data);
|
|
136
|
-
if (errorData.message) {
|
|
137
|
-
message += ` - ${errorData.message}`;
|
|
138
|
-
}
|
|
139
|
-
} catch (parseError) {
|
|
140
|
-
message += ` - ${error.oauthError}`;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
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]))
|
|
144
82
|
};
|
|
145
|
-
|
|
146
|
-
|
|
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"
|
|
147
92
|
};
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const executeRefreshTokenStrategy = async (providerStrategy, refreshToken, scope) => {
|
|
152
|
-
return new Promise((resolve, reject) => {
|
|
153
|
-
const anyStrategy = providerStrategy;
|
|
154
|
-
const OAuth2 = anyStrategy._oauth2.constructor;
|
|
155
|
-
const oauth2 = new OAuth2(anyStrategy._oauth2._clientId, anyStrategy._oauth2._clientSecret, anyStrategy._oauth2._baseSite, anyStrategy._oauth2._authorizeUrl, anyStrategy._refreshURL || anyStrategy._oauth2._accessTokenUrl, anyStrategy._oauth2._customHeaders);
|
|
156
|
-
oauth2.getOAuthAccessToken(refreshToken, {
|
|
157
|
-
scope,
|
|
158
|
-
grant_type: "refresh_token"
|
|
159
|
-
}, (err, accessToken, newRefreshToken, params) => {
|
|
93
|
+
}
|
|
94
|
+
userProfile(accessToken, done) {
|
|
95
|
+
this._oauth2.get(this.profileURL, accessToken, (err, body) => {
|
|
160
96
|
if (err) {
|
|
161
|
-
|
|
97
|
+
return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
|
|
162
98
|
}
|
|
163
|
-
if (!
|
|
164
|
-
|
|
99
|
+
if (!body) {
|
|
100
|
+
return done(new Error("Failed to fetch user profile, body cannot be empty"));
|
|
165
101
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
};
|
|
174
|
-
const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) => {
|
|
175
|
-
return new Promise((resolve, reject) => {
|
|
176
|
-
const anyStrategy = providerStrategy;
|
|
177
|
-
anyStrategy.userProfile(accessToken, (error, rawProfile) => {
|
|
178
|
-
if (error) {
|
|
179
|
-
reject(error);
|
|
180
|
-
} else {
|
|
181
|
-
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"));
|
|
182
108
|
}
|
|
183
109
|
});
|
|
184
|
-
}
|
|
185
|
-
|
|
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
|
+
}
|
|
186
123
|
|
|
187
124
|
const readState = (stateString) => {
|
|
188
125
|
var _a, _b;
|
|
@@ -193,7 +130,7 @@ const readState = (stateString) => {
|
|
|
193
130
|
return state;
|
|
194
131
|
};
|
|
195
132
|
const encodeState = (state) => {
|
|
196
|
-
const stateString = new URLSearchParams(pickBy__default[
|
|
133
|
+
const stateString = new URLSearchParams(pickBy__default["default"](state, (value) => value !== void 0)).toString();
|
|
197
134
|
return Buffer.from(stateString, "utf-8").toString("hex");
|
|
198
135
|
};
|
|
199
136
|
const verifyNonce = (req, providerId) => {
|
|
@@ -218,7 +155,7 @@ class OAuthEnvironmentHandler {
|
|
|
218
155
|
}
|
|
219
156
|
static mapConfig(config, factoryFunc) {
|
|
220
157
|
const envs = config.keys();
|
|
221
|
-
const handlers = new Map();
|
|
158
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
222
159
|
for (const env of envs) {
|
|
223
160
|
const envConfig = config.getConfig(env);
|
|
224
161
|
const handler = factoryFunc(envConfig);
|
|
@@ -287,11 +224,11 @@ const postMessageResponse = (res, appOrigin, response) => {
|
|
|
287
224
|
window.close();
|
|
288
225
|
}, 100); // same as the interval of the core-app-api lib/loginPopup.ts (to address race conditions)
|
|
289
226
|
`;
|
|
290
|
-
const hash = crypto__default[
|
|
227
|
+
const hash = crypto__default["default"].createHash("sha256").update(script).digest("base64");
|
|
291
228
|
res.setHeader("Content-Type", "text/html");
|
|
292
229
|
res.setHeader("X-Frame-Options", "sameorigin");
|
|
293
230
|
res.setHeader("Content-Security-Policy", `script-src 'sha256-${hash}'`);
|
|
294
|
-
res.end(`<html><body><script>${script}
|
|
231
|
+
res.end(`<html><body><script>${script}<\/script></body></html>`);
|
|
295
232
|
};
|
|
296
233
|
const ensuresXRequestedWith = (req) => {
|
|
297
234
|
const requiredHeader = req.header("X-Requested-With");
|
|
@@ -301,6 +238,25 @@ const ensuresXRequestedWith = (req) => {
|
|
|
301
238
|
return true;
|
|
302
239
|
};
|
|
303
240
|
|
|
241
|
+
function parseJwtPayload(token) {
|
|
242
|
+
const [_header, payload, _signature] = token.split(".");
|
|
243
|
+
return JSON.parse(Buffer.from(payload, "base64").toString());
|
|
244
|
+
}
|
|
245
|
+
function prepareBackstageIdentityResponse(result) {
|
|
246
|
+
const { sub, ent } = parseJwtPayload(result.token);
|
|
247
|
+
return {
|
|
248
|
+
...{
|
|
249
|
+
idToken: result.token,
|
|
250
|
+
...result
|
|
251
|
+
},
|
|
252
|
+
identity: {
|
|
253
|
+
type: "user",
|
|
254
|
+
userEntityRef: sub,
|
|
255
|
+
ownershipEntityRefs: ent != null ? ent : []
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
304
260
|
const THOUSAND_DAYS_MS = 1e3 * 24 * 60 * 60 * 1e3;
|
|
305
261
|
const TEN_MINUTES_MS = 600 * 1e3;
|
|
306
262
|
class OAuthAdapter {
|
|
@@ -352,7 +308,7 @@ class OAuthAdapter {
|
|
|
352
308
|
};
|
|
353
309
|
}
|
|
354
310
|
static fromConfig(config, handlers, options) {
|
|
355
|
-
const {origin: appOrigin} = new url.URL(config.appUrl);
|
|
311
|
+
const { origin: appOrigin } = new url.URL(config.appUrl);
|
|
356
312
|
const secure = config.baseUrl.startsWith("https://");
|
|
357
313
|
const url$1 = new url.URL(config.baseUrl);
|
|
358
314
|
const cookiePath = `${url$1.pathname}/${options.providerId}`;
|
|
@@ -376,11 +332,11 @@ class OAuthAdapter {
|
|
|
376
332
|
if (this.options.persistScopes) {
|
|
377
333
|
this.setScopesCookie(res, scope);
|
|
378
334
|
}
|
|
379
|
-
const nonce = crypto__default[
|
|
335
|
+
const nonce = crypto__default["default"].randomBytes(16).toString("base64");
|
|
380
336
|
this.setNonceCookie(res, nonce);
|
|
381
|
-
const state = {nonce, env, origin};
|
|
382
|
-
const forwardReq = Object.assign(req, {scope, state});
|
|
383
|
-
const {url, status} = await this.handlers.start(forwardReq);
|
|
337
|
+
const state = { nonce, env, origin };
|
|
338
|
+
const forwardReq = Object.assign(req, { scope, state });
|
|
339
|
+
const { url, status } = await this.handlers.start(forwardReq);
|
|
384
340
|
res.statusCode = status || 302;
|
|
385
341
|
res.setHeader("Location", url);
|
|
386
342
|
res.setHeader("Content-Length", "0");
|
|
@@ -402,7 +358,7 @@ class OAuthAdapter {
|
|
|
402
358
|
}
|
|
403
359
|
}
|
|
404
360
|
verifyNonce(req, this.options.providerId);
|
|
405
|
-
const {response, refreshToken} = await this.handlers.handler(req);
|
|
361
|
+
const { response, refreshToken } = await this.handlers.handler(req);
|
|
406
362
|
if (this.options.persistScopes) {
|
|
407
363
|
const grantedScopes = this.getScopesFromCookie(req, this.options.providerId);
|
|
408
364
|
response.providerInfo.scope = grantedScopes;
|
|
@@ -410,16 +366,16 @@ class OAuthAdapter {
|
|
|
410
366
|
if (refreshToken && !this.options.disableRefresh) {
|
|
411
367
|
this.setRefreshTokenCookie(res, refreshToken);
|
|
412
368
|
}
|
|
413
|
-
await this.populateIdentity(response.backstageIdentity);
|
|
369
|
+
const identity = await this.populateIdentity(response.backstageIdentity);
|
|
414
370
|
return postMessageResponse(res, appOrigin, {
|
|
415
371
|
type: "authorization_response",
|
|
416
|
-
response
|
|
372
|
+
response: { ...response, backstageIdentity: identity }
|
|
417
373
|
});
|
|
418
374
|
} catch (error) {
|
|
419
|
-
const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
375
|
+
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
420
376
|
return postMessageResponse(res, appOrigin, {
|
|
421
377
|
type: "authorization_response",
|
|
422
|
-
error: {name, message}
|
|
378
|
+
error: { name, message }
|
|
423
379
|
});
|
|
424
380
|
}
|
|
425
381
|
}
|
|
@@ -444,309 +400,655 @@ class OAuthAdapter {
|
|
|
444
400
|
throw new errors.InputError("Missing session cookie");
|
|
445
401
|
}
|
|
446
402
|
const scope = (_b = (_a = req.query.scope) == null ? void 0 : _a.toString()) != null ? _b : "";
|
|
447
|
-
const forwardReq = Object.assign(req, {scope, refreshToken});
|
|
448
|
-
const response = await this.handlers.refresh(forwardReq);
|
|
449
|
-
await this.populateIdentity(response.backstageIdentity);
|
|
450
|
-
if (
|
|
451
|
-
this.setRefreshTokenCookie(res,
|
|
403
|
+
const forwardReq = Object.assign(req, { scope, refreshToken });
|
|
404
|
+
const { response, refreshToken: newRefreshToken } = await this.handlers.refresh(forwardReq);
|
|
405
|
+
const backstageIdentity = await this.populateIdentity(response.backstageIdentity);
|
|
406
|
+
if (newRefreshToken && newRefreshToken !== refreshToken) {
|
|
407
|
+
this.setRefreshTokenCookie(res, newRefreshToken);
|
|
452
408
|
}
|
|
453
|
-
res.status(200).json(response);
|
|
409
|
+
res.status(200).json({ ...response, backstageIdentity });
|
|
454
410
|
} catch (error) {
|
|
455
411
|
throw new errors.AuthenticationError("Refresh failed", error);
|
|
456
412
|
}
|
|
457
413
|
}
|
|
458
414
|
async populateIdentity(identity) {
|
|
459
415
|
if (!identity) {
|
|
460
|
-
return;
|
|
416
|
+
return void 0;
|
|
461
417
|
}
|
|
462
|
-
if (
|
|
463
|
-
|
|
464
|
-
claims: {sub: identity.id}
|
|
465
|
-
});
|
|
466
|
-
} else if (!identity.token && identity.idToken) {
|
|
467
|
-
identity.token = identity.idToken;
|
|
418
|
+
if (identity.token) {
|
|
419
|
+
return prepareBackstageIdentityResponse(identity);
|
|
468
420
|
}
|
|
421
|
+
const userEntityRef = catalogModel.stringifyEntityRef(catalogModel.parseEntityRef(identity.id, {
|
|
422
|
+
defaultKind: "user",
|
|
423
|
+
defaultNamespace: catalogModel.ENTITY_DEFAULT_NAMESPACE
|
|
424
|
+
}));
|
|
425
|
+
const token = await this.options.tokenIssuer.issueToken({
|
|
426
|
+
claims: { sub: userEntityRef }
|
|
427
|
+
});
|
|
428
|
+
return prepareBackstageIdentityResponse({ ...identity, token });
|
|
469
429
|
}
|
|
470
430
|
}
|
|
471
431
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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;
|
|
476
438
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
const token = await this.tokenIssuer.issueToken({
|
|
485
|
-
claims: {sub: "backstage.io/auth-backend"}
|
|
486
|
-
});
|
|
487
|
-
const {items} = await this.catalogApi.getEntities({filter}, {token});
|
|
488
|
-
if (items.length !== 1) {
|
|
489
|
-
if (items.length > 1) {
|
|
490
|
-
throw new errors.ConflictError("User lookup resulted in multiple matches");
|
|
491
|
-
} else {
|
|
492
|
-
throw new errors.NotFoundError("User not found");
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
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;
|
|
496
445
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
|
|
504
|
-
defaultKind: "user",
|
|
505
|
-
defaultNamespace: "default"
|
|
506
|
-
});
|
|
507
|
-
return parsedRef;
|
|
508
|
-
} catch {
|
|
509
|
-
logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
|
|
510
|
-
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;
|
|
511
452
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
|
|
521
|
-
const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
|
|
522
|
-
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}`);
|
|
523
461
|
}
|
|
524
|
-
const memberOf = entities.flatMap((e) => {
|
|
525
|
-
var _a, _b;
|
|
526
|
-
return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.target)) != null ? _b : [];
|
|
527
|
-
});
|
|
528
|
-
const newEntityRefs = [
|
|
529
|
-
...new Set(resolvedEntityRefs.concat(memberOf).map(catalogModel.stringifyEntityRef))
|
|
530
|
-
];
|
|
531
|
-
logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
|
|
532
|
-
return newEntityRefs;
|
|
533
462
|
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function getEntityClaims(entity) {
|
|
537
|
-
var _a, _b;
|
|
538
|
-
const userRef = catalogModel.stringifyEntityRef(entity);
|
|
539
|
-
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 : [];
|
|
540
463
|
return {
|
|
541
|
-
|
|
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 : [];
|
|
611
|
+
return {
|
|
612
|
+
sub: userRef,
|
|
542
613
|
ent: [userRef, ...membershipRefs]
|
|
543
614
|
};
|
|
544
615
|
}
|
|
545
616
|
|
|
546
|
-
|
|
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 {
|
|
547
744
|
constructor(options) {
|
|
548
745
|
this.signInResolver = options.signInResolver;
|
|
549
746
|
this.authHandler = options.authHandler;
|
|
550
|
-
this.stateEncoder = options.stateEncoder;
|
|
551
747
|
this.tokenIssuer = options.tokenIssuer;
|
|
552
748
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
553
749
|
this.logger = options.logger;
|
|
554
|
-
this._strategy = new
|
|
750
|
+
this._strategy = new Auth0Strategy({
|
|
555
751
|
clientID: options.clientId,
|
|
556
752
|
clientSecret: options.clientSecret,
|
|
557
753
|
callbackURL: options.callbackUrl,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
authorizationURL: options.authorizationUrl
|
|
754
|
+
domain: options.domain,
|
|
755
|
+
passReqToCallback: false
|
|
561
756
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
562
|
-
done(void 0, {
|
|
757
|
+
done(void 0, {
|
|
758
|
+
fullProfile,
|
|
759
|
+
accessToken,
|
|
760
|
+
refreshToken,
|
|
761
|
+
params
|
|
762
|
+
}, {
|
|
763
|
+
refreshToken
|
|
764
|
+
});
|
|
563
765
|
});
|
|
564
766
|
}
|
|
565
|
-
async start(req) {
|
|
566
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
567
|
-
|
|
568
|
-
|
|
767
|
+
async start(req) {
|
|
768
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
769
|
+
accessType: "offline",
|
|
770
|
+
prompt: "consent",
|
|
771
|
+
scope: req.scope,
|
|
772
|
+
state: encodeState(req.state)
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
async handler(req) {
|
|
776
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
777
|
+
return {
|
|
778
|
+
response: await this.handleResult(result),
|
|
779
|
+
refreshToken: privateInfo.refreshToken
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
async refresh(req) {
|
|
783
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
784
|
+
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
785
|
+
return {
|
|
786
|
+
response: await this.handleResult({
|
|
787
|
+
fullProfile,
|
|
788
|
+
params,
|
|
789
|
+
accessToken
|
|
790
|
+
}),
|
|
791
|
+
refreshToken
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async handleResult(result) {
|
|
795
|
+
const { profile } = await this.authHandler(result);
|
|
796
|
+
const response = {
|
|
797
|
+
providerInfo: {
|
|
798
|
+
idToken: result.params.id_token,
|
|
799
|
+
accessToken: result.accessToken,
|
|
800
|
+
scope: result.params.scope,
|
|
801
|
+
expiresInSeconds: result.params.expires_in
|
|
802
|
+
},
|
|
803
|
+
profile
|
|
804
|
+
};
|
|
805
|
+
if (this.signInResolver) {
|
|
806
|
+
response.backstageIdentity = await this.signInResolver({
|
|
807
|
+
result,
|
|
808
|
+
profile
|
|
809
|
+
}, {
|
|
810
|
+
tokenIssuer: this.tokenIssuer,
|
|
811
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
812
|
+
logger: this.logger
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
return response;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
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: "" };
|
|
825
|
+
};
|
|
826
|
+
const createAuth0Provider = (options) => {
|
|
827
|
+
return ({
|
|
828
|
+
providerId,
|
|
829
|
+
globalConfig,
|
|
830
|
+
config,
|
|
831
|
+
tokenIssuer,
|
|
832
|
+
catalogApi,
|
|
833
|
+
logger
|
|
834
|
+
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
835
|
+
var _a, _b;
|
|
836
|
+
const clientId = envConfig.getString("clientId");
|
|
837
|
+
const clientSecret = envConfig.getString("clientSecret");
|
|
838
|
+
const domain = envConfig.getString("domain");
|
|
839
|
+
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
840
|
+
const catalogIdentityClient = new CatalogIdentityClient({
|
|
841
|
+
catalogApi,
|
|
842
|
+
tokenIssuer
|
|
843
|
+
});
|
|
844
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
845
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
846
|
+
});
|
|
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({
|
|
849
|
+
clientId,
|
|
850
|
+
clientSecret,
|
|
851
|
+
callbackUrl,
|
|
852
|
+
domain,
|
|
853
|
+
authHandler,
|
|
854
|
+
signInResolver,
|
|
855
|
+
tokenIssuer,
|
|
856
|
+
catalogIdentityClient,
|
|
857
|
+
logger
|
|
858
|
+
});
|
|
859
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
860
|
+
disableRefresh: true,
|
|
861
|
+
providerId,
|
|
862
|
+
tokenIssuer
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
};
|
|
866
|
+
|
|
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"));
|
|
872
|
+
};
|
|
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
|
|
569
947
|
});
|
|
570
|
-
}
|
|
571
|
-
async handler(req) {
|
|
572
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
573
948
|
return {
|
|
574
|
-
response: await this.handleResult(result),
|
|
575
|
-
refreshToken: privateInfo.refreshToken
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
async refresh(req) {
|
|
579
|
-
const {
|
|
580
|
-
accessToken,
|
|
581
|
-
refreshToken: newRefreshToken,
|
|
582
|
-
params
|
|
583
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
584
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
585
|
-
return this.handleResult({
|
|
586
|
-
fullProfile,
|
|
587
|
-
params,
|
|
588
|
-
accessToken,
|
|
589
|
-
refreshToken: newRefreshToken
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
async handleResult(result) {
|
|
593
|
-
const {profile} = await this.authHandler(result);
|
|
594
|
-
const expiresInStr = result.params.expires_in;
|
|
595
|
-
const response = {
|
|
596
949
|
providerInfo: {
|
|
597
950
|
accessToken: result.accessToken,
|
|
598
|
-
|
|
599
|
-
scope: result.params.scope,
|
|
600
|
-
expiresInSeconds: expiresInStr === void 0 ? void 0 : Number(expiresInStr)
|
|
951
|
+
expiresInSeconds: result.expiresInSeconds
|
|
601
952
|
},
|
|
953
|
+
backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
|
|
602
954
|
profile
|
|
603
955
|
};
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
tokenIssuer: this.tokenIssuer,
|
|
610
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
611
|
-
logger: this.logger
|
|
612
|
-
});
|
|
956
|
+
}
|
|
957
|
+
async getKey(keyId) {
|
|
958
|
+
const optionalCacheKey = this.keyCache.get(keyId);
|
|
959
|
+
if (optionalCacheKey) {
|
|
960
|
+
return crypto__namespace.createPublicKey(optionalCacheKey);
|
|
613
961
|
}
|
|
614
|
-
|
|
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;
|
|
615
966
|
}
|
|
616
967
|
}
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
};
|
|
625
|
-
const createGithubProvider = (options) => {
|
|
626
|
-
return ({
|
|
627
|
-
providerId,
|
|
628
|
-
globalConfig,
|
|
629
|
-
config,
|
|
630
|
-
tokenIssuer,
|
|
631
|
-
catalogApi,
|
|
632
|
-
logger
|
|
633
|
-
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
634
|
-
var _a, _b, _c;
|
|
635
|
-
const clientId = envConfig.getString("clientId");
|
|
636
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
637
|
-
const enterpriseInstanceUrl = envConfig.getOptionalString("enterpriseInstanceUrl");
|
|
638
|
-
const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
|
|
639
|
-
const authorizationUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/authorize` : void 0;
|
|
640
|
-
const tokenUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/access_token` : void 0;
|
|
641
|
-
const userProfileUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/api/v3/user` : void 0;
|
|
642
|
-
const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
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
|
+
}
|
|
643
975
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
644
976
|
catalogApi,
|
|
645
977
|
tokenIssuer
|
|
646
978
|
});
|
|
647
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile}) => ({
|
|
979
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
648
980
|
profile: makeProfileInfo(fullProfile)
|
|
649
981
|
});
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
logger
|
|
655
|
-
});
|
|
656
|
-
const stateEncoder = (_c = options == null ? void 0 : options.stateEncoder) != null ? _c : async (req) => {
|
|
657
|
-
return {encodedState: encodeState(req.state)};
|
|
658
|
-
};
|
|
659
|
-
const provider = new GithubAuthProvider({
|
|
660
|
-
clientId,
|
|
661
|
-
clientSecret,
|
|
662
|
-
callbackUrl,
|
|
663
|
-
tokenUrl,
|
|
664
|
-
userProfileUrl,
|
|
665
|
-
authorizationUrl,
|
|
982
|
+
const signInResolver = options == null ? void 0 : options.signIn.resolver;
|
|
983
|
+
return new AwsAlbAuthProvider({
|
|
984
|
+
region,
|
|
985
|
+
issuer,
|
|
666
986
|
signInResolver,
|
|
667
987
|
authHandler,
|
|
668
988
|
tokenIssuer,
|
|
669
989
|
catalogIdentityClient,
|
|
670
|
-
stateEncoder,
|
|
671
990
|
logger
|
|
672
991
|
});
|
|
673
|
-
|
|
674
|
-
persistScopes: true,
|
|
675
|
-
providerId,
|
|
676
|
-
tokenIssuer
|
|
677
|
-
});
|
|
678
|
-
});
|
|
992
|
+
};
|
|
679
993
|
};
|
|
680
994
|
|
|
681
|
-
|
|
682
|
-
const {profile, result} = info;
|
|
683
|
-
let id = result.fullProfile.id;
|
|
684
|
-
if (profile.email) {
|
|
685
|
-
id = profile.email.split("@")[0];
|
|
686
|
-
}
|
|
687
|
-
const token = await ctx.tokenIssuer.issueToken({
|
|
688
|
-
claims: {sub: id, ent: [`user:default/${id}`]}
|
|
689
|
-
});
|
|
690
|
-
return {id, token};
|
|
691
|
-
};
|
|
692
|
-
const gitlabDefaultAuthHandler = async ({
|
|
693
|
-
fullProfile,
|
|
694
|
-
params
|
|
695
|
-
}) => ({
|
|
696
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
697
|
-
});
|
|
698
|
-
class GitlabAuthProvider {
|
|
995
|
+
class BitbucketAuthProvider {
|
|
699
996
|
constructor(options) {
|
|
997
|
+
this.signInResolver = options.signInResolver;
|
|
998
|
+
this.authHandler = options.authHandler;
|
|
999
|
+
this.tokenIssuer = options.tokenIssuer;
|
|
700
1000
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
701
1001
|
this.logger = options.logger;
|
|
702
|
-
this.
|
|
703
|
-
this.authHandler = options.authHandler;
|
|
704
|
-
this.signInResolver = options.signInResolver;
|
|
705
|
-
this._strategy = new passportGitlab2.Strategy({
|
|
1002
|
+
this._strategy = new passportBitbucketOauth2.Strategy({
|
|
706
1003
|
clientID: options.clientId,
|
|
707
1004
|
clientSecret: options.clientSecret,
|
|
708
1005
|
callbackURL: options.callbackUrl,
|
|
709
|
-
|
|
1006
|
+
passReqToCallback: false
|
|
710
1007
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
711
|
-
done(void 0, {
|
|
1008
|
+
done(void 0, {
|
|
1009
|
+
fullProfile,
|
|
1010
|
+
params,
|
|
1011
|
+
accessToken,
|
|
1012
|
+
refreshToken
|
|
1013
|
+
}, {
|
|
712
1014
|
refreshToken
|
|
713
1015
|
});
|
|
714
1016
|
});
|
|
715
1017
|
}
|
|
716
1018
|
async start(req) {
|
|
717
1019
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
1020
|
+
accessType: "offline",
|
|
1021
|
+
prompt: "consent",
|
|
718
1022
|
scope: req.scope,
|
|
719
1023
|
state: encodeState(req.state)
|
|
720
1024
|
});
|
|
721
1025
|
}
|
|
722
1026
|
async handler(req) {
|
|
723
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1027
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
724
1028
|
return {
|
|
725
1029
|
response: await this.handleResult(result),
|
|
726
1030
|
refreshToken: privateInfo.refreshToken
|
|
727
1031
|
};
|
|
728
1032
|
}
|
|
729
1033
|
async refresh(req) {
|
|
730
|
-
const {
|
|
731
|
-
accessToken,
|
|
732
|
-
refreshToken: newRefreshToken,
|
|
733
|
-
params
|
|
734
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1034
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
735
1035
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
736
|
-
return
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1036
|
+
return {
|
|
1037
|
+
response: await this.handleResult({
|
|
1038
|
+
fullProfile,
|
|
1039
|
+
params,
|
|
1040
|
+
accessToken
|
|
1041
|
+
}),
|
|
1042
|
+
refreshToken
|
|
1043
|
+
};
|
|
742
1044
|
}
|
|
743
1045
|
async handleResult(result) {
|
|
744
|
-
|
|
1046
|
+
result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
|
|
1047
|
+
const { profile } = await this.authHandler(result);
|
|
745
1048
|
const response = {
|
|
746
1049
|
providerInfo: {
|
|
747
1050
|
idToken: result.params.id_token,
|
|
748
1051
|
accessToken: result.accessToken,
|
|
749
|
-
refreshToken: result.refreshToken,
|
|
750
1052
|
scope: result.params.scope,
|
|
751
1053
|
expiresInSeconds: result.params.expires_in
|
|
752
1054
|
},
|
|
@@ -765,7 +1067,35 @@ class GitlabAuthProvider {
|
|
|
765
1067
|
return response;
|
|
766
1068
|
}
|
|
767
1069
|
}
|
|
768
|
-
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) => {
|
|
769
1099
|
return ({
|
|
770
1100
|
providerId,
|
|
771
1101
|
globalConfig,
|
|
@@ -774,33 +1104,26 @@ const createGitlabProvider = (options) => {
|
|
|
774
1104
|
catalogApi,
|
|
775
1105
|
logger
|
|
776
1106
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
777
|
-
var _a
|
|
1107
|
+
var _a;
|
|
778
1108
|
const clientId = envConfig.getString("clientId");
|
|
779
1109
|
const clientSecret = envConfig.getString("clientSecret");
|
|
780
|
-
const audience = envConfig.getOptionalString("audience");
|
|
781
|
-
const baseUrl = audience || "https://gitlab.com";
|
|
782
1110
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
783
1111
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
784
1112
|
catalogApi,
|
|
785
1113
|
tokenIssuer
|
|
786
1114
|
});
|
|
787
|
-
const authHandler = (
|
|
788
|
-
|
|
789
|
-
const signInResolver = (info) => signInResolverFn(info, {
|
|
790
|
-
catalogIdentityClient,
|
|
791
|
-
tokenIssuer,
|
|
792
|
-
logger
|
|
1115
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1116
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
793
1117
|
});
|
|
794
|
-
const provider = new
|
|
1118
|
+
const provider = new BitbucketAuthProvider({
|
|
795
1119
|
clientId,
|
|
796
1120
|
clientSecret,
|
|
797
1121
|
callbackUrl,
|
|
798
|
-
|
|
1122
|
+
signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
|
|
799
1123
|
authHandler,
|
|
800
|
-
|
|
1124
|
+
tokenIssuer,
|
|
801
1125
|
catalogIdentityClient,
|
|
802
|
-
logger
|
|
803
|
-
tokenIssuer
|
|
1126
|
+
logger
|
|
804
1127
|
});
|
|
805
1128
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
806
1129
|
disableRefresh: false,
|
|
@@ -810,62 +1133,58 @@ const createGitlabProvider = (options) => {
|
|
|
810
1133
|
});
|
|
811
1134
|
};
|
|
812
1135
|
|
|
813
|
-
class
|
|
1136
|
+
class GithubAuthProvider {
|
|
814
1137
|
constructor(options) {
|
|
815
1138
|
this.signInResolver = options.signInResolver;
|
|
816
1139
|
this.authHandler = options.authHandler;
|
|
1140
|
+
this.stateEncoder = options.stateEncoder;
|
|
817
1141
|
this.tokenIssuer = options.tokenIssuer;
|
|
818
1142
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
819
1143
|
this.logger = options.logger;
|
|
820
|
-
this._strategy = new
|
|
1144
|
+
this._strategy = new passportGithub2.Strategy({
|
|
821
1145
|
clientID: options.clientId,
|
|
822
1146
|
clientSecret: options.clientSecret,
|
|
823
1147
|
callbackURL: options.callbackUrl,
|
|
824
|
-
|
|
1148
|
+
tokenURL: options.tokenUrl,
|
|
1149
|
+
userProfileURL: options.userProfileUrl,
|
|
1150
|
+
authorizationURL: options.authorizationUrl
|
|
825
1151
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
826
|
-
done(void 0, {
|
|
827
|
-
fullProfile,
|
|
828
|
-
params,
|
|
829
|
-
accessToken,
|
|
830
|
-
refreshToken
|
|
831
|
-
}, {
|
|
832
|
-
refreshToken
|
|
833
|
-
});
|
|
1152
|
+
done(void 0, { fullProfile, params, accessToken }, { refreshToken });
|
|
834
1153
|
});
|
|
835
1154
|
}
|
|
836
1155
|
async start(req) {
|
|
837
1156
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
838
|
-
accessType: "offline",
|
|
839
|
-
prompt: "consent",
|
|
840
1157
|
scope: req.scope,
|
|
841
|
-
state:
|
|
1158
|
+
state: (await this.stateEncoder(req)).encodedState
|
|
842
1159
|
});
|
|
843
1160
|
}
|
|
844
1161
|
async handler(req) {
|
|
845
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1162
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
846
1163
|
return {
|
|
847
1164
|
response: await this.handleResult(result),
|
|
848
1165
|
refreshToken: privateInfo.refreshToken
|
|
849
1166
|
};
|
|
850
1167
|
}
|
|
851
1168
|
async refresh(req) {
|
|
852
|
-
const {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1169
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
853
1170
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
854
|
-
return
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1171
|
+
return {
|
|
1172
|
+
response: await this.handleResult({
|
|
1173
|
+
fullProfile,
|
|
1174
|
+
params,
|
|
1175
|
+
accessToken
|
|
1176
|
+
}),
|
|
1177
|
+
refreshToken
|
|
1178
|
+
};
|
|
860
1179
|
}
|
|
861
1180
|
async handleResult(result) {
|
|
862
|
-
const {profile} = await this.authHandler(result);
|
|
1181
|
+
const { profile } = await this.authHandler(result);
|
|
1182
|
+
const expiresInStr = result.params.expires_in;
|
|
863
1183
|
const response = {
|
|
864
1184
|
providerInfo: {
|
|
865
|
-
idToken: result.params.id_token,
|
|
866
1185
|
accessToken: result.accessToken,
|
|
867
1186
|
scope: result.params.scope,
|
|
868
|
-
expiresInSeconds:
|
|
1187
|
+
expiresInSeconds: expiresInStr === void 0 ? void 0 : Number(expiresInStr)
|
|
869
1188
|
},
|
|
870
1189
|
profile
|
|
871
1190
|
};
|
|
@@ -882,43 +1201,15 @@ class GoogleAuthProvider {
|
|
|
882
1201
|
return response;
|
|
883
1202
|
}
|
|
884
1203
|
}
|
|
885
|
-
const
|
|
886
|
-
const {
|
|
887
|
-
|
|
888
|
-
throw new Error("Google profile contained no email");
|
|
889
|
-
}
|
|
890
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
891
|
-
annotations: {
|
|
892
|
-
"google.com/email": profile.email
|
|
893
|
-
}
|
|
894
|
-
});
|
|
895
|
-
const claims = getEntityClaims(entity);
|
|
896
|
-
const token = await ctx.tokenIssuer.issueToken({claims});
|
|
897
|
-
return {id: entity.metadata.name, entity, token};
|
|
898
|
-
};
|
|
899
|
-
const googleDefaultSignInResolver = async (info, ctx) => {
|
|
900
|
-
const {profile} = info;
|
|
901
|
-
if (!profile.email) {
|
|
902
|
-
throw new Error("Google profile contained no email");
|
|
903
|
-
}
|
|
904
|
-
let userId;
|
|
905
|
-
try {
|
|
906
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
907
|
-
annotations: {
|
|
908
|
-
"google.com/email": profile.email
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
userId = entity.metadata.name;
|
|
912
|
-
} catch (error) {
|
|
913
|
-
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`);
|
|
914
|
-
userId = profile.email.split("@")[0];
|
|
915
|
-
}
|
|
1204
|
+
const githubDefaultSignInResolver = async (info, ctx) => {
|
|
1205
|
+
const { fullProfile } = info.result;
|
|
1206
|
+
const userId = fullProfile.username || fullProfile.id;
|
|
916
1207
|
const token = await ctx.tokenIssuer.issueToken({
|
|
917
|
-
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1208
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
918
1209
|
});
|
|
919
|
-
return {id: userId, token};
|
|
1210
|
+
return { id: userId, token };
|
|
920
1211
|
};
|
|
921
|
-
const
|
|
1212
|
+
const createGithubProvider = (options) => {
|
|
922
1213
|
return ({
|
|
923
1214
|
providerId,
|
|
924
1215
|
globalConfig,
|
|
@@ -927,57 +1218,86 @@ const createGoogleProvider = (options) => {
|
|
|
927
1218
|
catalogApi,
|
|
928
1219
|
logger
|
|
929
1220
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
930
|
-
var _a, _b;
|
|
1221
|
+
var _a, _b, _c;
|
|
931
1222
|
const clientId = envConfig.getString("clientId");
|
|
932
1223
|
const clientSecret = envConfig.getString("clientSecret");
|
|
933
|
-
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`;
|
|
934
1230
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
935
1231
|
catalogApi,
|
|
936
1232
|
tokenIssuer
|
|
937
1233
|
});
|
|
938
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile
|
|
939
|
-
profile: makeProfileInfo(fullProfile
|
|
1234
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
1235
|
+
profile: makeProfileInfo(fullProfile)
|
|
940
1236
|
});
|
|
941
|
-
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;
|
|
942
1238
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
943
1239
|
catalogIdentityClient,
|
|
944
1240
|
tokenIssuer,
|
|
945
1241
|
logger
|
|
946
1242
|
});
|
|
947
|
-
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({
|
|
948
1247
|
clientId,
|
|
949
1248
|
clientSecret,
|
|
950
1249
|
callbackUrl,
|
|
1250
|
+
tokenUrl,
|
|
1251
|
+
userProfileUrl,
|
|
1252
|
+
authorizationUrl,
|
|
951
1253
|
signInResolver,
|
|
952
1254
|
authHandler,
|
|
953
1255
|
tokenIssuer,
|
|
954
1256
|
catalogIdentityClient,
|
|
1257
|
+
stateEncoder,
|
|
955
1258
|
logger
|
|
956
1259
|
});
|
|
957
1260
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
958
|
-
|
|
1261
|
+
persistScopes: true,
|
|
959
1262
|
providerId,
|
|
960
1263
|
tokenIssuer
|
|
961
1264
|
});
|
|
962
1265
|
});
|
|
963
1266
|
};
|
|
964
1267
|
|
|
965
|
-
|
|
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 {
|
|
966
1286
|
constructor(options) {
|
|
967
|
-
this.signInResolver = options.signInResolver;
|
|
968
|
-
this.authHandler = options.authHandler;
|
|
969
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
970
|
-
this.logger = options.logger;
|
|
971
1287
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
972
|
-
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({
|
|
973
1293
|
clientID: options.clientId,
|
|
974
1294
|
clientSecret: options.clientSecret,
|
|
975
1295
|
callbackURL: options.callbackUrl,
|
|
976
|
-
|
|
977
|
-
tokenURL: options.tokenUrl,
|
|
978
|
-
passReqToCallback: false
|
|
1296
|
+
baseURL: options.baseUrl
|
|
979
1297
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
980
|
-
done(void 0, {fullProfile,
|
|
1298
|
+
done(void 0, { fullProfile, params, accessToken }, {
|
|
1299
|
+
refreshToken
|
|
1300
|
+
});
|
|
981
1301
|
});
|
|
982
1302
|
}
|
|
983
1303
|
async start(req) {
|
|
@@ -987,26 +1307,26 @@ class MicrosoftAuthProvider {
|
|
|
987
1307
|
});
|
|
988
1308
|
}
|
|
989
1309
|
async handler(req) {
|
|
990
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1310
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
991
1311
|
return {
|
|
992
1312
|
response: await this.handleResult(result),
|
|
993
1313
|
refreshToken: privateInfo.refreshToken
|
|
994
1314
|
};
|
|
995
1315
|
}
|
|
996
1316
|
async refresh(req) {
|
|
997
|
-
const {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1317
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
998
1318
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
999
|
-
return
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1319
|
+
return {
|
|
1320
|
+
response: await this.handleResult({
|
|
1321
|
+
fullProfile,
|
|
1322
|
+
params,
|
|
1323
|
+
accessToken
|
|
1324
|
+
}),
|
|
1325
|
+
refreshToken
|
|
1326
|
+
};
|
|
1005
1327
|
}
|
|
1006
1328
|
async handleResult(result) {
|
|
1007
|
-
const
|
|
1008
|
-
result.fullProfile.photos = photo ? [{value: photo}] : void 0;
|
|
1009
|
-
const {profile} = await this.authHandler(result);
|
|
1329
|
+
const { profile } = await this.authHandler(result);
|
|
1010
1330
|
const response = {
|
|
1011
1331
|
providerInfo: {
|
|
1012
1332
|
idToken: result.params.id_token,
|
|
@@ -1028,50 +1348,8 @@ class MicrosoftAuthProvider {
|
|
|
1028
1348
|
}
|
|
1029
1349
|
return response;
|
|
1030
1350
|
}
|
|
1031
|
-
getUserPhoto(accessToken) {
|
|
1032
|
-
return new Promise((resolve) => {
|
|
1033
|
-
got__default['default'].get("https://graph.microsoft.com/v1.0/me/photos/48x48/$value", {
|
|
1034
|
-
encoding: "binary",
|
|
1035
|
-
responseType: "buffer",
|
|
1036
|
-
headers: {
|
|
1037
|
-
Authorization: `Bearer ${accessToken}`
|
|
1038
|
-
}
|
|
1039
|
-
}).then((photoData) => {
|
|
1040
|
-
const photoURL = `data:image/jpeg;base64,${Buffer.from(photoData.body).toString("base64")}`;
|
|
1041
|
-
resolve(photoURL);
|
|
1042
|
-
}).catch((error) => {
|
|
1043
|
-
this.logger.warn(`Could not retrieve user profile photo from Microsoft Graph API: ${error}`);
|
|
1044
|
-
resolve(void 0);
|
|
1045
|
-
});
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
1351
|
}
|
|
1049
|
-
const
|
|
1050
|
-
const {profile} = info;
|
|
1051
|
-
if (!profile.email) {
|
|
1052
|
-
throw new Error("Microsoft profile contained no email");
|
|
1053
|
-
}
|
|
1054
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1055
|
-
annotations: {
|
|
1056
|
-
"microsoft.com/email": profile.email
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
const claims = getEntityClaims(entity);
|
|
1060
|
-
const token = await ctx.tokenIssuer.issueToken({claims});
|
|
1061
|
-
return {id: entity.metadata.name, entity, token};
|
|
1062
|
-
};
|
|
1063
|
-
const microsoftDefaultSignInResolver = async (info, ctx) => {
|
|
1064
|
-
const {profile} = info;
|
|
1065
|
-
if (!profile.email) {
|
|
1066
|
-
throw new Error("Profile contained no email");
|
|
1067
|
-
}
|
|
1068
|
-
const userId = profile.email.split("@")[0];
|
|
1069
|
-
const token = await ctx.tokenIssuer.issueToken({
|
|
1070
|
-
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1071
|
-
});
|
|
1072
|
-
return {id: userId, token};
|
|
1073
|
-
};
|
|
1074
|
-
const createMicrosoftProvider = (options) => {
|
|
1352
|
+
const createGitlabProvider = (options) => {
|
|
1075
1353
|
return ({
|
|
1076
1354
|
providerId,
|
|
1077
1355
|
globalConfig,
|
|
@@ -1080,32 +1358,28 @@ const createMicrosoftProvider = (options) => {
|
|
|
1080
1358
|
catalogApi,
|
|
1081
1359
|
logger
|
|
1082
1360
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1083
|
-
var _a, _b;
|
|
1361
|
+
var _a, _b, _c;
|
|
1084
1362
|
const clientId = envConfig.getString("clientId");
|
|
1085
1363
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1086
|
-
const
|
|
1364
|
+
const audience = envConfig.getOptionalString("audience");
|
|
1365
|
+
const baseUrl = audience || "https://gitlab.com";
|
|
1087
1366
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1088
|
-
const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
|
|
1089
|
-
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
1090
1367
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1091
1368
|
catalogApi,
|
|
1092
1369
|
tokenIssuer
|
|
1093
1370
|
});
|
|
1094
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ?
|
|
1095
|
-
|
|
1096
|
-
});
|
|
1097
|
-
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;
|
|
1098
1373
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1099
1374
|
catalogIdentityClient,
|
|
1100
1375
|
tokenIssuer,
|
|
1101
1376
|
logger
|
|
1102
1377
|
});
|
|
1103
|
-
const provider = new
|
|
1378
|
+
const provider = new GitlabAuthProvider({
|
|
1104
1379
|
clientId,
|
|
1105
1380
|
clientSecret,
|
|
1106
1381
|
callbackUrl,
|
|
1107
|
-
|
|
1108
|
-
tokenUrl,
|
|
1382
|
+
baseUrl,
|
|
1109
1383
|
authHandler,
|
|
1110
1384
|
signInResolver,
|
|
1111
1385
|
catalogIdentityClient,
|
|
@@ -1120,30 +1394,24 @@ const createMicrosoftProvider = (options) => {
|
|
|
1120
1394
|
});
|
|
1121
1395
|
};
|
|
1122
1396
|
|
|
1123
|
-
class
|
|
1397
|
+
class GoogleAuthProvider {
|
|
1124
1398
|
constructor(options) {
|
|
1125
1399
|
this.signInResolver = options.signInResolver;
|
|
1126
1400
|
this.authHandler = options.authHandler;
|
|
1127
1401
|
this.tokenIssuer = options.tokenIssuer;
|
|
1128
1402
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1129
1403
|
this.logger = options.logger;
|
|
1130
|
-
this._strategy = new
|
|
1404
|
+
this._strategy = new passportGoogleOauth20.Strategy({
|
|
1131
1405
|
clientID: options.clientId,
|
|
1132
1406
|
clientSecret: options.clientSecret,
|
|
1133
1407
|
callbackURL: options.callbackUrl,
|
|
1134
|
-
|
|
1135
|
-
tokenURL: options.tokenUrl,
|
|
1136
|
-
passReqToCallback: false,
|
|
1137
|
-
scope: options.scope,
|
|
1138
|
-
customHeaders: {
|
|
1139
|
-
Authorization: `Basic ${this.encodeClientCredentials(options.clientId, options.clientSecret)}`
|
|
1140
|
-
}
|
|
1408
|
+
passReqToCallback: false
|
|
1141
1409
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1142
1410
|
done(void 0, {
|
|
1143
1411
|
fullProfile,
|
|
1412
|
+
params,
|
|
1144
1413
|
accessToken,
|
|
1145
|
-
refreshToken
|
|
1146
|
-
params
|
|
1414
|
+
refreshToken
|
|
1147
1415
|
}, {
|
|
1148
1416
|
refreshToken
|
|
1149
1417
|
});
|
|
@@ -1158,36 +1426,32 @@ class OAuth2AuthProvider {
|
|
|
1158
1426
|
});
|
|
1159
1427
|
}
|
|
1160
1428
|
async handler(req) {
|
|
1161
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1429
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1162
1430
|
return {
|
|
1163
1431
|
response: await this.handleResult(result),
|
|
1164
1432
|
refreshToken: privateInfo.refreshToken
|
|
1165
1433
|
};
|
|
1166
1434
|
}
|
|
1167
1435
|
async refresh(req) {
|
|
1168
|
-
const
|
|
1169
|
-
const {
|
|
1170
|
-
accessToken,
|
|
1171
|
-
params,
|
|
1172
|
-
refreshToken: updatedRefreshToken
|
|
1173
|
-
} = refreshTokenResponse;
|
|
1436
|
+
const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1174
1437
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1175
|
-
return
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1438
|
+
return {
|
|
1439
|
+
response: await this.handleResult({
|
|
1440
|
+
fullProfile,
|
|
1441
|
+
params,
|
|
1442
|
+
accessToken
|
|
1443
|
+
}),
|
|
1444
|
+
refreshToken
|
|
1445
|
+
};
|
|
1181
1446
|
}
|
|
1182
1447
|
async handleResult(result) {
|
|
1183
|
-
const {profile} = await this.authHandler(result);
|
|
1448
|
+
const { profile } = await this.authHandler(result);
|
|
1184
1449
|
const response = {
|
|
1185
1450
|
providerInfo: {
|
|
1186
1451
|
idToken: result.params.id_token,
|
|
1187
1452
|
accessToken: result.accessToken,
|
|
1188
1453
|
scope: result.params.scope,
|
|
1189
|
-
expiresInSeconds: result.params.expires_in
|
|
1190
|
-
refreshToken: result.refreshToken
|
|
1454
|
+
expiresInSeconds: result.params.expires_in
|
|
1191
1455
|
},
|
|
1192
1456
|
profile
|
|
1193
1457
|
};
|
|
@@ -1203,22 +1467,44 @@ class OAuth2AuthProvider {
|
|
|
1203
1467
|
}
|
|
1204
1468
|
return response;
|
|
1205
1469
|
}
|
|
1206
|
-
encodeClientCredentials(clientID, clientSecret) {
|
|
1207
|
-
return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
|
|
1208
|
-
}
|
|
1209
1470
|
}
|
|
1210
|
-
const
|
|
1211
|
-
const {profile} = info;
|
|
1471
|
+
const googleEmailSignInResolver = async (info, ctx) => {
|
|
1472
|
+
const { profile } = info;
|
|
1212
1473
|
if (!profile.email) {
|
|
1213
|
-
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];
|
|
1214
1501
|
}
|
|
1215
|
-
const userId = profile.email.split("@")[0];
|
|
1216
1502
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1217
|
-
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1503
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1218
1504
|
});
|
|
1219
|
-
return {id: userId, token};
|
|
1505
|
+
return { id: userId, token };
|
|
1220
1506
|
};
|
|
1221
|
-
const
|
|
1507
|
+
const createGoogleProvider = (options) => {
|
|
1222
1508
|
return ({
|
|
1223
1509
|
providerId,
|
|
1224
1510
|
globalConfig,
|
|
@@ -1227,127 +1513,94 @@ const createOAuth2Provider = (options) => {
|
|
|
1227
1513
|
catalogApi,
|
|
1228
1514
|
logger
|
|
1229
1515
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1230
|
-
var _a, _b
|
|
1516
|
+
var _a, _b;
|
|
1231
1517
|
const clientId = envConfig.getString("clientId");
|
|
1232
1518
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1233
1519
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1234
|
-
const authorizationUrl = envConfig.getString("authorizationUrl");
|
|
1235
|
-
const tokenUrl = envConfig.getString("tokenUrl");
|
|
1236
|
-
const scope = envConfig.getOptionalString("scope");
|
|
1237
|
-
const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
|
|
1238
1520
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1239
1521
|
catalogApi,
|
|
1240
1522
|
tokenIssuer
|
|
1241
1523
|
});
|
|
1242
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile, params}) => ({
|
|
1524
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1243
1525
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1244
1526
|
});
|
|
1245
|
-
const signInResolverFn = (
|
|
1527
|
+
const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : googleDefaultSignInResolver;
|
|
1246
1528
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1247
1529
|
catalogIdentityClient,
|
|
1248
1530
|
tokenIssuer,
|
|
1249
1531
|
logger
|
|
1250
1532
|
});
|
|
1251
|
-
const provider = new
|
|
1533
|
+
const provider = new GoogleAuthProvider({
|
|
1252
1534
|
clientId,
|
|
1253
1535
|
clientSecret,
|
|
1254
|
-
tokenIssuer,
|
|
1255
|
-
catalogIdentityClient,
|
|
1256
1536
|
callbackUrl,
|
|
1257
1537
|
signInResolver,
|
|
1258
1538
|
authHandler,
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
scope,
|
|
1539
|
+
tokenIssuer,
|
|
1540
|
+
catalogIdentityClient,
|
|
1262
1541
|
logger
|
|
1263
1542
|
});
|
|
1264
1543
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1265
|
-
disableRefresh,
|
|
1544
|
+
disableRefresh: false,
|
|
1266
1545
|
providerId,
|
|
1267
1546
|
tokenIssuer
|
|
1268
1547
|
});
|
|
1269
1548
|
});
|
|
1270
1549
|
};
|
|
1271
1550
|
|
|
1272
|
-
class
|
|
1551
|
+
class MicrosoftAuthProvider {
|
|
1273
1552
|
constructor(options) {
|
|
1274
|
-
this.implementation = this.setupStrategy(options);
|
|
1275
|
-
this.scope = options.scope;
|
|
1276
|
-
this.prompt = options.prompt;
|
|
1277
1553
|
this.signInResolver = options.signInResolver;
|
|
1278
1554
|
this.authHandler = options.authHandler;
|
|
1279
1555
|
this.tokenIssuer = options.tokenIssuer;
|
|
1280
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1281
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
|
+
});
|
|
1282
1568
|
}
|
|
1283
1569
|
async start(req) {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
scope: req.scope || this.scope || "openid profile email",
|
|
1570
|
+
return await executeRedirectStrategy(req, this._strategy, {
|
|
1571
|
+
scope: req.scope,
|
|
1287
1572
|
state: encodeState(req.state)
|
|
1288
|
-
};
|
|
1289
|
-
const prompt = this.prompt || "none";
|
|
1290
|
-
if (prompt !== "auto") {
|
|
1291
|
-
options.prompt = prompt;
|
|
1292
|
-
}
|
|
1293
|
-
return await executeRedirectStrategy(req, strategy, options);
|
|
1573
|
+
});
|
|
1294
1574
|
}
|
|
1295
1575
|
async handler(req) {
|
|
1296
|
-
const {
|
|
1297
|
-
const strategyResponse = await executeFrameHandlerStrategy(req, strategy);
|
|
1298
|
-
const {
|
|
1299
|
-
result: {userinfo, tokenset},
|
|
1300
|
-
privateInfo
|
|
1301
|
-
} = strategyResponse;
|
|
1302
|
-
const identityResponse = await this.handleResult({tokenset, userinfo});
|
|
1576
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1303
1577
|
return {
|
|
1304
|
-
response:
|
|
1578
|
+
response: await this.handleResult(result),
|
|
1305
1579
|
refreshToken: privateInfo.refreshToken
|
|
1306
1580
|
};
|
|
1307
1581
|
}
|
|
1308
1582
|
async refresh(req) {
|
|
1309
|
-
const {
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
const client = new issuer.Client({
|
|
1320
|
-
access_type: "offline",
|
|
1321
|
-
client_id: options.clientId,
|
|
1322
|
-
client_secret: options.clientSecret,
|
|
1323
|
-
redirect_uris: [options.callbackUrl],
|
|
1324
|
-
response_types: ["code"],
|
|
1325
|
-
id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
|
|
1326
|
-
scope: options.scope || ""
|
|
1327
|
-
});
|
|
1328
|
-
const strategy = new openidClient.Strategy({
|
|
1329
|
-
client,
|
|
1330
|
-
passReqToCallback: false
|
|
1331
|
-
}, (tokenset, userinfo, done) => {
|
|
1332
|
-
if (typeof done !== "function") {
|
|
1333
|
-
throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
|
|
1334
|
-
}
|
|
1335
|
-
done(void 0, {tokenset, userinfo}, {
|
|
1336
|
-
refreshToken: tokenset.refresh_token
|
|
1337
|
-
});
|
|
1338
|
-
});
|
|
1339
|
-
strategy.error = console.error;
|
|
1340
|
-
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
|
+
};
|
|
1341
1593
|
}
|
|
1342
1594
|
async handleResult(result) {
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1595
|
+
const photo = await this.getUserPhoto(result.accessToken);
|
|
1596
|
+
result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
|
|
1597
|
+
const { profile } = await this.authHandler(result);
|
|
1598
|
+
const response = {
|
|
1599
|
+
providerInfo: {
|
|
1600
|
+
idToken: result.params.id_token,
|
|
1601
|
+
accessToken: result.accessToken,
|
|
1602
|
+
scope: result.params.scope,
|
|
1603
|
+
expiresInSeconds: result.params.expires_in
|
|
1351
1604
|
},
|
|
1352
1605
|
profile
|
|
1353
1606
|
};
|
|
@@ -1363,19 +1616,48 @@ class OidcAuthProvider {
|
|
|
1363
1616
|
}
|
|
1364
1617
|
return response;
|
|
1365
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
|
+
}
|
|
1366
1634
|
}
|
|
1367
|
-
const
|
|
1368
|
-
const {profile} = info;
|
|
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) => {
|
|
1650
|
+
const { profile } = info;
|
|
1369
1651
|
if (!profile.email) {
|
|
1370
1652
|
throw new Error("Profile contained no email");
|
|
1371
1653
|
}
|
|
1372
1654
|
const userId = profile.email.split("@")[0];
|
|
1373
1655
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1374
|
-
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1656
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1375
1657
|
});
|
|
1376
|
-
return {id: userId, token};
|
|
1658
|
+
return { id: userId, token };
|
|
1377
1659
|
};
|
|
1378
|
-
const
|
|
1660
|
+
const createMicrosoftProvider = (options) => {
|
|
1379
1661
|
return ({
|
|
1380
1662
|
providerId,
|
|
1381
1663
|
globalConfig,
|
|
@@ -1387,41 +1669,34 @@ const createOidcProvider = (options) => {
|
|
|
1387
1669
|
var _a, _b;
|
|
1388
1670
|
const clientId = envConfig.getString("clientId");
|
|
1389
1671
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1672
|
+
const tenantId = envConfig.getString("tenantId");
|
|
1390
1673
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1391
|
-
const
|
|
1392
|
-
const
|
|
1393
|
-
const scope = envConfig.getOptionalString("scope");
|
|
1394
|
-
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`;
|
|
1395
1676
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1396
1677
|
catalogApi,
|
|
1397
1678
|
tokenIssuer
|
|
1398
1679
|
});
|
|
1399
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({
|
|
1400
|
-
profile:
|
|
1401
|
-
displayName: userinfo.name,
|
|
1402
|
-
email: userinfo.email,
|
|
1403
|
-
picture: userinfo.picture
|
|
1404
|
-
}
|
|
1680
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1681
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1405
1682
|
});
|
|
1406
|
-
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;
|
|
1407
1684
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1408
1685
|
catalogIdentityClient,
|
|
1409
1686
|
tokenIssuer,
|
|
1410
1687
|
logger
|
|
1411
1688
|
});
|
|
1412
|
-
const provider = new
|
|
1689
|
+
const provider = new MicrosoftAuthProvider({
|
|
1413
1690
|
clientId,
|
|
1414
1691
|
clientSecret,
|
|
1415
1692
|
callbackUrl,
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
scope,
|
|
1419
|
-
prompt,
|
|
1420
|
-
signInResolver,
|
|
1693
|
+
authorizationUrl,
|
|
1694
|
+
tokenUrl,
|
|
1421
1695
|
authHandler,
|
|
1696
|
+
signInResolver,
|
|
1697
|
+
catalogIdentityClient,
|
|
1422
1698
|
logger,
|
|
1423
|
-
tokenIssuer
|
|
1424
|
-
catalogIdentityClient
|
|
1699
|
+
tokenIssuer
|
|
1425
1700
|
});
|
|
1426
1701
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1427
1702
|
disableRefresh: false,
|
|
@@ -1431,35 +1706,30 @@ const createOidcProvider = (options) => {
|
|
|
1431
1706
|
});
|
|
1432
1707
|
};
|
|
1433
1708
|
|
|
1434
|
-
class
|
|
1709
|
+
class OAuth2AuthProvider {
|
|
1435
1710
|
constructor(options) {
|
|
1436
|
-
this.
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
}
|
|
1443
|
-
};
|
|
1444
|
-
this._signInResolver = options.signInResolver;
|
|
1445
|
-
this._authHandler = options.authHandler;
|
|
1446
|
-
this._tokenIssuer = options.tokenIssuer;
|
|
1447
|
-
this._catalogIdentityClient = options.catalogIdentityClient;
|
|
1448
|
-
this._logger = options.logger;
|
|
1449
|
-
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({
|
|
1450
1717
|
clientID: options.clientId,
|
|
1451
1718
|
clientSecret: options.clientSecret,
|
|
1452
1719
|
callbackURL: options.callbackUrl,
|
|
1453
|
-
|
|
1720
|
+
authorizationURL: options.authorizationUrl,
|
|
1721
|
+
tokenURL: options.tokenUrl,
|
|
1454
1722
|
passReqToCallback: false,
|
|
1455
|
-
|
|
1456
|
-
|
|
1723
|
+
scope: options.scope,
|
|
1724
|
+
customHeaders: options.includeBasicAuth ? {
|
|
1725
|
+
Authorization: `Basic ${this.encodeClientCredentials(options.clientId, options.clientSecret)}`
|
|
1726
|
+
} : void 0
|
|
1457
1727
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1458
1728
|
done(void 0, {
|
|
1729
|
+
fullProfile,
|
|
1459
1730
|
accessToken,
|
|
1460
1731
|
refreshToken,
|
|
1461
|
-
params
|
|
1462
|
-
fullProfile
|
|
1732
|
+
params
|
|
1463
1733
|
}, {
|
|
1464
1734
|
refreshToken
|
|
1465
1735
|
});
|
|
@@ -1474,24 +1744,27 @@ class OktaAuthProvider {
|
|
|
1474
1744
|
});
|
|
1475
1745
|
}
|
|
1476
1746
|
async handler(req) {
|
|
1477
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1747
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1478
1748
|
return {
|
|
1479
1749
|
response: await this.handleResult(result),
|
|
1480
1750
|
refreshToken: privateInfo.refreshToken
|
|
1481
1751
|
};
|
|
1482
1752
|
}
|
|
1483
1753
|
async refresh(req) {
|
|
1484
|
-
const
|
|
1754
|
+
const refreshTokenResponse = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1755
|
+
const { accessToken, params, refreshToken } = refreshTokenResponse;
|
|
1485
1756
|
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1486
|
-
return
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1757
|
+
return {
|
|
1758
|
+
response: await this.handleResult({
|
|
1759
|
+
fullProfile,
|
|
1760
|
+
params,
|
|
1761
|
+
accessToken
|
|
1762
|
+
}),
|
|
1763
|
+
refreshToken
|
|
1764
|
+
};
|
|
1492
1765
|
}
|
|
1493
1766
|
async handleResult(result) {
|
|
1494
|
-
const {profile} = await this.
|
|
1767
|
+
const { profile } = await this.authHandler(result);
|
|
1495
1768
|
const response = {
|
|
1496
1769
|
providerInfo: {
|
|
1497
1770
|
idToken: result.params.id_token,
|
|
@@ -1501,45 +1774,34 @@ class OktaAuthProvider {
|
|
|
1501
1774
|
},
|
|
1502
1775
|
profile
|
|
1503
1776
|
};
|
|
1504
|
-
if (this.
|
|
1505
|
-
response.backstageIdentity = await this.
|
|
1777
|
+
if (this.signInResolver) {
|
|
1778
|
+
response.backstageIdentity = await this.signInResolver({
|
|
1506
1779
|
result,
|
|
1507
1780
|
profile
|
|
1508
1781
|
}, {
|
|
1509
|
-
tokenIssuer: this.
|
|
1510
|
-
catalogIdentityClient: this.
|
|
1511
|
-
logger: this.
|
|
1782
|
+
tokenIssuer: this.tokenIssuer,
|
|
1783
|
+
catalogIdentityClient: this.catalogIdentityClient,
|
|
1784
|
+
logger: this.logger
|
|
1512
1785
|
});
|
|
1513
1786
|
}
|
|
1514
1787
|
return response;
|
|
1515
1788
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
const {profile} = info;
|
|
1519
|
-
if (!profile.email) {
|
|
1520
|
-
throw new Error("Okta profile contained no email");
|
|
1789
|
+
encodeClientCredentials(clientID, clientSecret) {
|
|
1790
|
+
return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
|
|
1521
1791
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
}
|
|
1526
|
-
});
|
|
1527
|
-
const claims = getEntityClaims(entity);
|
|
1528
|
-
const token = await ctx.tokenIssuer.issueToken({claims});
|
|
1529
|
-
return {id: entity.metadata.name, entity, token};
|
|
1530
|
-
};
|
|
1531
|
-
const oktaDefaultSignInResolver = async (info, ctx) => {
|
|
1532
|
-
const {profile} = info;
|
|
1792
|
+
}
|
|
1793
|
+
const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
|
|
1794
|
+
const { profile } = info;
|
|
1533
1795
|
if (!profile.email) {
|
|
1534
|
-
throw new Error("
|
|
1796
|
+
throw new Error("Profile contained no email");
|
|
1535
1797
|
}
|
|
1536
1798
|
const userId = profile.email.split("@")[0];
|
|
1537
1799
|
const token = await ctx.tokenIssuer.issueToken({
|
|
1538
|
-
claims: {sub: userId, ent: [`user:default/${userId}`]}
|
|
1800
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1539
1801
|
});
|
|
1540
|
-
return {id: userId, token};
|
|
1802
|
+
return { id: userId, token };
|
|
1541
1803
|
};
|
|
1542
|
-
const
|
|
1804
|
+
const createOAuth2Provider = (options) => {
|
|
1543
1805
|
return ({
|
|
1544
1806
|
providerId,
|
|
1545
1807
|
globalConfig,
|
|
@@ -1548,103 +1810,126 @@ const createOktaProvider = (_options) => {
|
|
|
1548
1810
|
catalogApi,
|
|
1549
1811
|
logger
|
|
1550
1812
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1551
|
-
var _a, _b;
|
|
1813
|
+
var _a, _b, _c;
|
|
1552
1814
|
const clientId = envConfig.getString("clientId");
|
|
1553
1815
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1554
|
-
const audience = envConfig.getString("audience");
|
|
1555
1816
|
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
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;
|
|
1559
1822
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1560
1823
|
catalogApi,
|
|
1561
1824
|
tokenIssuer
|
|
1562
1825
|
});
|
|
1563
|
-
const authHandler = (
|
|
1826
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
1564
1827
|
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1565
1828
|
});
|
|
1566
|
-
const signInResolverFn = (
|
|
1829
|
+
const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver$1;
|
|
1567
1830
|
const signInResolver = (info) => signInResolverFn(info, {
|
|
1568
1831
|
catalogIdentityClient,
|
|
1569
1832
|
tokenIssuer,
|
|
1570
1833
|
logger
|
|
1571
1834
|
});
|
|
1572
|
-
const provider = new
|
|
1573
|
-
audience,
|
|
1835
|
+
const provider = new OAuth2AuthProvider({
|
|
1574
1836
|
clientId,
|
|
1575
1837
|
clientSecret,
|
|
1576
|
-
callbackUrl,
|
|
1577
|
-
authHandler,
|
|
1578
|
-
signInResolver,
|
|
1579
1838
|
tokenIssuer,
|
|
1580
1839
|
catalogIdentityClient,
|
|
1581
|
-
|
|
1840
|
+
callbackUrl,
|
|
1841
|
+
signInResolver,
|
|
1842
|
+
authHandler,
|
|
1843
|
+
authorizationUrl,
|
|
1844
|
+
tokenUrl,
|
|
1845
|
+
scope,
|
|
1846
|
+
logger,
|
|
1847
|
+
includeBasicAuth
|
|
1582
1848
|
});
|
|
1583
1849
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1584
|
-
disableRefresh
|
|
1850
|
+
disableRefresh,
|
|
1585
1851
|
providerId,
|
|
1586
1852
|
tokenIssuer
|
|
1587
1853
|
});
|
|
1588
1854
|
});
|
|
1589
1855
|
};
|
|
1590
1856
|
|
|
1591
|
-
class
|
|
1857
|
+
class OidcAuthProvider {
|
|
1592
1858
|
constructor(options) {
|
|
1859
|
+
this.implementation = this.setupStrategy(options);
|
|
1860
|
+
this.scope = options.scope;
|
|
1861
|
+
this.prompt = options.prompt;
|
|
1593
1862
|
this.signInResolver = options.signInResolver;
|
|
1594
1863
|
this.authHandler = options.authHandler;
|
|
1595
1864
|
this.tokenIssuer = options.tokenIssuer;
|
|
1596
1865
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1597
1866
|
this.logger = options.logger;
|
|
1598
|
-
this._strategy = new passportBitbucketOauth2.Strategy({
|
|
1599
|
-
clientID: options.clientId,
|
|
1600
|
-
clientSecret: options.clientSecret,
|
|
1601
|
-
callbackURL: options.callbackUrl,
|
|
1602
|
-
passReqToCallback: false
|
|
1603
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1604
|
-
done(void 0, {
|
|
1605
|
-
fullProfile,
|
|
1606
|
-
params,
|
|
1607
|
-
accessToken,
|
|
1608
|
-
refreshToken
|
|
1609
|
-
}, {
|
|
1610
|
-
refreshToken
|
|
1611
|
-
});
|
|
1612
|
-
});
|
|
1613
1867
|
}
|
|
1614
1868
|
async start(req) {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
scope: req.scope,
|
|
1869
|
+
const { strategy } = await this.implementation;
|
|
1870
|
+
const options = {
|
|
1871
|
+
scope: req.scope || this.scope || "openid profile email",
|
|
1619
1872
|
state: encodeState(req.state)
|
|
1620
|
-
}
|
|
1873
|
+
};
|
|
1874
|
+
const prompt = this.prompt || "none";
|
|
1875
|
+
if (prompt !== "auto") {
|
|
1876
|
+
options.prompt = prompt;
|
|
1877
|
+
}
|
|
1878
|
+
return await executeRedirectStrategy(req, strategy, options);
|
|
1621
1879
|
}
|
|
1622
1880
|
async handler(req) {
|
|
1623
|
-
const {
|
|
1881
|
+
const { strategy } = await this.implementation;
|
|
1882
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
|
|
1624
1883
|
return {
|
|
1625
1884
|
response: await this.handleResult(result),
|
|
1626
1885
|
refreshToken: privateInfo.refreshToken
|
|
1627
1886
|
};
|
|
1628
1887
|
}
|
|
1629
1888
|
async refresh(req) {
|
|
1630
|
-
const {
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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
|
+
});
|
|
1637
1921
|
});
|
|
1922
|
+
strategy.error = console.error;
|
|
1923
|
+
return { strategy, client };
|
|
1638
1924
|
}
|
|
1639
1925
|
async handleResult(result) {
|
|
1640
|
-
|
|
1641
|
-
const {profile} = await this.authHandler(result);
|
|
1926
|
+
const { profile } = await this.authHandler(result);
|
|
1642
1927
|
const response = {
|
|
1643
1928
|
providerInfo: {
|
|
1644
|
-
idToken: result.
|
|
1645
|
-
accessToken: result.
|
|
1646
|
-
scope: result.
|
|
1647
|
-
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
|
|
1648
1933
|
},
|
|
1649
1934
|
profile
|
|
1650
1935
|
};
|
|
@@ -1655,41 +1940,24 @@ class BitbucketAuthProvider {
|
|
|
1655
1940
|
}, {
|
|
1656
1941
|
tokenIssuer: this.tokenIssuer,
|
|
1657
1942
|
catalogIdentityClient: this.catalogIdentityClient,
|
|
1658
|
-
logger: this.logger
|
|
1659
|
-
});
|
|
1660
|
-
}
|
|
1661
|
-
return response;
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
const bitbucketUsernameSignInResolver = async (info, ctx) => {
|
|
1665
|
-
const {result} = info;
|
|
1666
|
-
if (!result.fullProfile.username) {
|
|
1667
|
-
throw new Error("Bitbucket profile contained no Username");
|
|
1668
|
-
}
|
|
1669
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1670
|
-
annotations: {
|
|
1671
|
-
"bitbucket.org/username": result.fullProfile.username
|
|
1672
|
-
}
|
|
1673
|
-
});
|
|
1674
|
-
const claims = getEntityClaims(entity);
|
|
1675
|
-
const token = await ctx.tokenIssuer.issueToken({claims});
|
|
1676
|
-
return {id: entity.metadata.name, entity, token};
|
|
1677
|
-
};
|
|
1678
|
-
const bitbucketUserIdSignInResolver = async (info, ctx) => {
|
|
1679
|
-
const {result} = info;
|
|
1680
|
-
if (!result.fullProfile.id) {
|
|
1681
|
-
throw new Error("Bitbucket profile contained no User ID");
|
|
1682
|
-
}
|
|
1683
|
-
const entity = await ctx.catalogIdentityClient.findUser({
|
|
1684
|
-
annotations: {
|
|
1685
|
-
"bitbucket.org/user-id": result.fullProfile.id
|
|
1943
|
+
logger: this.logger
|
|
1944
|
+
});
|
|
1686
1945
|
}
|
|
1946
|
+
return response;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
const oAuth2DefaultSignInResolver = async (info, ctx) => {
|
|
1950
|
+
const { profile } = info;
|
|
1951
|
+
if (!profile.email) {
|
|
1952
|
+
throw new Error("Profile contained no email");
|
|
1953
|
+
}
|
|
1954
|
+
const userId = profile.email.split("@")[0];
|
|
1955
|
+
const token = await ctx.tokenIssuer.issueToken({
|
|
1956
|
+
claims: { sub: userId, ent: [`user:default/${userId}`] }
|
|
1687
1957
|
});
|
|
1688
|
-
|
|
1689
|
-
const token = await ctx.tokenIssuer.issueToken({claims});
|
|
1690
|
-
return {id: entity.metadata.name, entity, token};
|
|
1958
|
+
return { id: userId, token };
|
|
1691
1959
|
};
|
|
1692
|
-
const
|
|
1960
|
+
const createOidcProvider = (options) => {
|
|
1693
1961
|
return ({
|
|
1694
1962
|
providerId,
|
|
1695
1963
|
globalConfig,
|
|
@@ -1698,26 +1966,44 @@ const createBitbucketProvider = (options) => {
|
|
|
1698
1966
|
catalogApi,
|
|
1699
1967
|
logger
|
|
1700
1968
|
}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
1701
|
-
var _a;
|
|
1969
|
+
var _a, _b;
|
|
1702
1970
|
const clientId = envConfig.getString("clientId");
|
|
1703
1971
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1704
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");
|
|
1705
1977
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1706
1978
|
catalogApi,
|
|
1707
1979
|
tokenIssuer
|
|
1708
1980
|
});
|
|
1709
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({
|
|
1710
|
-
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
|
+
}
|
|
1711
1987
|
});
|
|
1712
|
-
const
|
|
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
|
|
1993
|
+
});
|
|
1994
|
+
const provider = new OidcAuthProvider({
|
|
1713
1995
|
clientId,
|
|
1714
1996
|
clientSecret,
|
|
1715
1997
|
callbackUrl,
|
|
1716
|
-
|
|
1998
|
+
tokenSignedResponseAlg,
|
|
1999
|
+
metadataUrl,
|
|
2000
|
+
scope,
|
|
2001
|
+
prompt,
|
|
2002
|
+
signInResolver,
|
|
1717
2003
|
authHandler,
|
|
2004
|
+
logger,
|
|
1718
2005
|
tokenIssuer,
|
|
1719
|
-
catalogIdentityClient
|
|
1720
|
-
logger
|
|
2006
|
+
catalogIdentityClient
|
|
1721
2007
|
});
|
|
1722
2008
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1723
2009
|
disableRefresh: false,
|
|
@@ -1727,140 +2013,117 @@ const createBitbucketProvider = (options) => {
|
|
|
1727
2013
|
});
|
|
1728
2014
|
};
|
|
1729
2015
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
...options,
|
|
1739
|
-
authorizationURL: `https://auth.atlassian.com/authorize`,
|
|
1740
|
-
tokenURL: `https://auth.atlassian.com/oauth/token`,
|
|
1741
|
-
scope: Array.from(new Set([...defaultScopes, ...scopes]))
|
|
1742
|
-
};
|
|
1743
|
-
super(optionsWithURLs, verify);
|
|
1744
|
-
this.profileURL = "https://api.atlassian.com/me";
|
|
1745
|
-
this.name = "atlassian";
|
|
1746
|
-
this._oauth2.useAuthorizationHeaderforGET(true);
|
|
1747
|
-
}
|
|
1748
|
-
authorizationParams() {
|
|
1749
|
-
return {
|
|
1750
|
-
audience: "api.atlassian.com",
|
|
1751
|
-
prompt: "consent"
|
|
1752
|
-
};
|
|
1753
|
-
}
|
|
1754
|
-
userProfile(accessToken, done) {
|
|
1755
|
-
this._oauth2.get(this.profileURL, accessToken, (err, body) => {
|
|
1756
|
-
if (err) {
|
|
1757
|
-
return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
|
|
1758
|
-
}
|
|
1759
|
-
if (!body) {
|
|
1760
|
-
return done(new Error("Failed to fetch user profile, body cannot be empty"));
|
|
1761
|
-
}
|
|
1762
|
-
try {
|
|
1763
|
-
const json = typeof body !== "string" ? body.toString() : body;
|
|
1764
|
-
const profile = AtlassianStrategy.parse(json);
|
|
1765
|
-
return done(null, profile);
|
|
1766
|
-
} catch (e) {
|
|
1767
|
-
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);
|
|
1768
2024
|
}
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
static parse(json) {
|
|
1772
|
-
const resp = JSON.parse(json);
|
|
1773
|
-
return {
|
|
1774
|
-
id: resp.account_id,
|
|
1775
|
-
provider: "atlassian",
|
|
1776
|
-
username: resp.nickname,
|
|
1777
|
-
displayName: resp.name,
|
|
1778
|
-
emails: [{value: resp.email}],
|
|
1779
|
-
photos: [{value: resp.picture}]
|
|
1780
2025
|
};
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
}) => ({
|
|
1788
|
-
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
1789
|
-
});
|
|
1790
|
-
class AtlassianAuthProvider {
|
|
1791
|
-
constructor(options) {
|
|
1792
|
-
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1793
|
-
this.logger = options.logger;
|
|
1794
|
-
this.tokenIssuer = options.tokenIssuer;
|
|
1795
|
-
this.authHandler = options.authHandler;
|
|
1796
|
-
this.signInResolver = options.signInResolver;
|
|
1797
|
-
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({
|
|
1798
2032
|
clientID: options.clientId,
|
|
1799
2033
|
clientSecret: options.clientSecret,
|
|
1800
2034
|
callbackURL: options.callbackUrl,
|
|
1801
|
-
|
|
2035
|
+
audience: options.audience,
|
|
2036
|
+
passReqToCallback: false,
|
|
2037
|
+
store: this._store,
|
|
2038
|
+
response_type: "code"
|
|
1802
2039
|
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
1803
2040
|
done(void 0, {
|
|
1804
|
-
fullProfile,
|
|
1805
2041
|
accessToken,
|
|
1806
2042
|
refreshToken,
|
|
1807
|
-
params
|
|
2043
|
+
params,
|
|
2044
|
+
fullProfile
|
|
2045
|
+
}, {
|
|
2046
|
+
refreshToken
|
|
1808
2047
|
});
|
|
1809
2048
|
});
|
|
1810
2049
|
}
|
|
1811
2050
|
async start(req) {
|
|
1812
2051
|
return await executeRedirectStrategy(req, this._strategy, {
|
|
2052
|
+
accessType: "offline",
|
|
2053
|
+
prompt: "consent",
|
|
2054
|
+
scope: req.scope,
|
|
1813
2055
|
state: encodeState(req.state)
|
|
1814
2056
|
});
|
|
1815
2057
|
}
|
|
1816
2058
|
async handler(req) {
|
|
1817
|
-
|
|
1818
|
-
const {result} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2059
|
+
const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
|
|
1819
2060
|
return {
|
|
1820
2061
|
response: await this.handleResult(result),
|
|
1821
|
-
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
|
|
1822
2075
|
};
|
|
1823
2076
|
}
|
|
1824
2077
|
async handleResult(result) {
|
|
1825
|
-
const {profile} = await this.
|
|
2078
|
+
const { profile } = await this._authHandler(result);
|
|
1826
2079
|
const response = {
|
|
1827
2080
|
providerInfo: {
|
|
1828
2081
|
idToken: result.params.id_token,
|
|
1829
2082
|
accessToken: result.accessToken,
|
|
1830
|
-
refreshToken: result.refreshToken,
|
|
1831
2083
|
scope: result.params.scope,
|
|
1832
2084
|
expiresInSeconds: result.params.expires_in
|
|
1833
2085
|
},
|
|
1834
2086
|
profile
|
|
1835
2087
|
};
|
|
1836
|
-
if (this.
|
|
1837
|
-
response.backstageIdentity = await this.
|
|
2088
|
+
if (this._signInResolver) {
|
|
2089
|
+
response.backstageIdentity = await this._signInResolver({
|
|
1838
2090
|
result,
|
|
1839
2091
|
profile
|
|
1840
2092
|
}, {
|
|
1841
|
-
tokenIssuer: this.
|
|
1842
|
-
catalogIdentityClient: this.
|
|
1843
|
-
logger: this.
|
|
2093
|
+
tokenIssuer: this._tokenIssuer,
|
|
2094
|
+
catalogIdentityClient: this._catalogIdentityClient,
|
|
2095
|
+
logger: this._logger
|
|
1844
2096
|
});
|
|
1845
2097
|
}
|
|
1846
2098
|
return response;
|
|
1847
2099
|
}
|
|
1848
|
-
async refresh(req) {
|
|
1849
|
-
const {
|
|
1850
|
-
accessToken,
|
|
1851
|
-
params,
|
|
1852
|
-
refreshToken: newRefreshToken
|
|
1853
|
-
} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
1854
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
1855
|
-
return this.handleResult({
|
|
1856
|
-
fullProfile,
|
|
1857
|
-
params,
|
|
1858
|
-
accessToken,
|
|
1859
|
-
refreshToken: newRefreshToken
|
|
1860
|
-
});
|
|
1861
|
-
}
|
|
1862
2100
|
}
|
|
1863
|
-
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) => {
|
|
1864
2127
|
return ({
|
|
1865
2128
|
providerId,
|
|
1866
2129
|
globalConfig,
|
|
@@ -1872,158 +2135,165 @@ const createAtlassianProvider = (options) => {
|
|
|
1872
2135
|
var _a, _b;
|
|
1873
2136
|
const clientId = envConfig.getString("clientId");
|
|
1874
2137
|
const clientSecret = envConfig.getString("clientSecret");
|
|
1875
|
-
const
|
|
2138
|
+
const audience = envConfig.getString("audience");
|
|
1876
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
|
+
}
|
|
1877
2143
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
1878
2144
|
catalogApi,
|
|
1879
2145
|
tokenIssuer
|
|
1880
2146
|
});
|
|
1881
|
-
const authHandler = (
|
|
1882
|
-
|
|
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,
|
|
1883
2158
|
clientId,
|
|
1884
2159
|
clientSecret,
|
|
1885
|
-
scopes,
|
|
1886
2160
|
callbackUrl,
|
|
1887
2161
|
authHandler,
|
|
1888
|
-
signInResolver
|
|
2162
|
+
signInResolver,
|
|
2163
|
+
tokenIssuer,
|
|
1889
2164
|
catalogIdentityClient,
|
|
1890
|
-
logger
|
|
1891
|
-
tokenIssuer
|
|
2165
|
+
logger
|
|
1892
2166
|
});
|
|
1893
2167
|
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
1894
|
-
disableRefresh:
|
|
2168
|
+
disableRefresh: false,
|
|
1895
2169
|
providerId,
|
|
1896
2170
|
tokenIssuer
|
|
1897
2171
|
});
|
|
1898
2172
|
});
|
|
1899
2173
|
};
|
|
1900
2174
|
|
|
1901
|
-
|
|
1902
|
-
const ALB_ACCESSTOKEN_HEADER = "x-amzn-oidc-accesstoken";
|
|
1903
|
-
const getJWTHeaders = (input) => {
|
|
1904
|
-
const encoded = input.split(".")[0];
|
|
1905
|
-
return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
|
|
1906
|
-
};
|
|
1907
|
-
class AwsAlbAuthProvider {
|
|
2175
|
+
class OneLoginProvider {
|
|
1908
2176
|
constructor(options) {
|
|
1909
|
-
this.region = options.region;
|
|
1910
|
-
this.issuer = options.issuer;
|
|
1911
|
-
this.authHandler = options.authHandler;
|
|
1912
2177
|
this.signInResolver = options.signInResolver;
|
|
2178
|
+
this.authHandler = options.authHandler;
|
|
1913
2179
|
this.tokenIssuer = options.tokenIssuer;
|
|
1914
2180
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
1915
2181
|
this.logger = options.logger;
|
|
1916
|
-
this.
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
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
|
+
});
|
|
1920
2198
|
}
|
|
1921
|
-
async
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
res.status(401);
|
|
1929
|
-
res.end();
|
|
1930
|
-
}
|
|
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
|
+
});
|
|
1931
2206
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
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
|
+
};
|
|
1934
2213
|
}
|
|
1935
|
-
async
|
|
1936
|
-
const
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
}
|
|
1941
|
-
if (accessToken === void 0) {
|
|
1942
|
-
throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`);
|
|
1943
|
-
}
|
|
1944
|
-
try {
|
|
1945
|
-
const headers = getJWTHeaders(jwt);
|
|
1946
|
-
const key = await this.getKey(headers.kid);
|
|
1947
|
-
const claims = jose.JWT.verify(jwt, key);
|
|
1948
|
-
if (this.issuer && claims.iss !== this.issuer) {
|
|
1949
|
-
throw new errors.AuthenticationError("Issuer mismatch on JWT token");
|
|
1950
|
-
}
|
|
1951
|
-
const fullProfile = {
|
|
1952
|
-
provider: "unknown",
|
|
1953
|
-
id: claims.sub,
|
|
1954
|
-
displayName: claims.name,
|
|
1955
|
-
username: claims.email.split("@")[0].toLowerCase(),
|
|
1956
|
-
name: {
|
|
1957
|
-
familyName: claims.family_name,
|
|
1958
|
-
givenName: claims.given_name
|
|
1959
|
-
},
|
|
1960
|
-
emails: [{value: claims.email.toLowerCase()}],
|
|
1961
|
-
photos: [{value: claims.picture}]
|
|
1962
|
-
};
|
|
1963
|
-
return {
|
|
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({
|
|
1964
2219
|
fullProfile,
|
|
1965
|
-
|
|
2220
|
+
params,
|
|
1966
2221
|
accessToken
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
}
|
|
2222
|
+
}),
|
|
2223
|
+
refreshToken
|
|
2224
|
+
};
|
|
1971
2225
|
}
|
|
1972
2226
|
async handleResult(result) {
|
|
1973
|
-
const {profile} = await this.authHandler(result);
|
|
1974
|
-
const
|
|
1975
|
-
result,
|
|
1976
|
-
profile
|
|
1977
|
-
}, {
|
|
1978
|
-
tokenIssuer: this.tokenIssuer,
|
|
1979
|
-
catalogIdentityClient: this.catalogIdentityClient,
|
|
1980
|
-
logger: this.logger
|
|
1981
|
-
});
|
|
1982
|
-
return {
|
|
2227
|
+
const { profile } = await this.authHandler(result);
|
|
2228
|
+
const response = {
|
|
1983
2229
|
providerInfo: {
|
|
2230
|
+
idToken: result.params.id_token,
|
|
1984
2231
|
accessToken: result.accessToken,
|
|
1985
|
-
|
|
2232
|
+
scope: result.params.scope,
|
|
2233
|
+
expiresInSeconds: result.params.expires_in
|
|
1986
2234
|
},
|
|
1987
|
-
backstageIdentity,
|
|
1988
2235
|
profile
|
|
1989
2236
|
};
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
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
|
+
});
|
|
1995
2246
|
}
|
|
1996
|
-
|
|
1997
|
-
const keyValue = crypto__namespace.createPublicKey(keyText);
|
|
1998
|
-
this.keyCache.set(keyId, keyValue.export({format: "pem", type: "spki"}));
|
|
1999
|
-
return keyValue;
|
|
2247
|
+
return response;
|
|
2000
2248
|
}
|
|
2001
2249
|
}
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
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`;
|
|
2009
2272
|
const catalogIdentityClient = new CatalogIdentityClient({
|
|
2010
2273
|
catalogApi,
|
|
2011
2274
|
tokenIssuer
|
|
2012
2275
|
});
|
|
2013
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile}) => ({
|
|
2014
|
-
profile: makeProfileInfo(fullProfile)
|
|
2276
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
|
|
2277
|
+
profile: makeProfileInfo(fullProfile, params.id_token)
|
|
2015
2278
|
});
|
|
2016
|
-
const signInResolver = options == null ? void 0 : options.signIn.resolver;
|
|
2017
|
-
|
|
2018
|
-
|
|
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,
|
|
2019
2284
|
issuer,
|
|
2020
|
-
signInResolver,
|
|
2021
2285
|
authHandler,
|
|
2286
|
+
signInResolver,
|
|
2022
2287
|
tokenIssuer,
|
|
2023
2288
|
catalogIdentityClient,
|
|
2024
2289
|
logger
|
|
2025
2290
|
});
|
|
2026
|
-
|
|
2291
|
+
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2292
|
+
disableRefresh: false,
|
|
2293
|
+
providerId,
|
|
2294
|
+
tokenIssuer
|
|
2295
|
+
});
|
|
2296
|
+
});
|
|
2027
2297
|
};
|
|
2028
2298
|
|
|
2029
2299
|
class SamlAuthProvider {
|
|
@@ -2034,24 +2304,24 @@ class SamlAuthProvider {
|
|
|
2034
2304
|
this.tokenIssuer = options.tokenIssuer;
|
|
2035
2305
|
this.catalogIdentityClient = options.catalogIdentityClient;
|
|
2036
2306
|
this.logger = options.logger;
|
|
2037
|
-
this.strategy = new passportSaml.Strategy({...options}, (fullProfile, done) => {
|
|
2038
|
-
done(void 0, {fullProfile});
|
|
2307
|
+
this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
|
|
2308
|
+
done(void 0, { fullProfile });
|
|
2039
2309
|
});
|
|
2040
2310
|
}
|
|
2041
2311
|
async start(req, res) {
|
|
2042
|
-
const {url} = await executeRedirectStrategy(req, this.strategy, {});
|
|
2312
|
+
const { url } = await executeRedirectStrategy(req, this.strategy, {});
|
|
2043
2313
|
res.redirect(url);
|
|
2044
2314
|
}
|
|
2045
2315
|
async frameHandler(req, res) {
|
|
2046
2316
|
try {
|
|
2047
|
-
const {result} = await executeFrameHandlerStrategy(req, this.strategy);
|
|
2048
|
-
const {profile} = await this.authHandler(result);
|
|
2317
|
+
const { result } = await executeFrameHandlerStrategy(req, this.strategy);
|
|
2318
|
+
const { profile } = await this.authHandler(result);
|
|
2049
2319
|
const response = {
|
|
2050
2320
|
profile,
|
|
2051
2321
|
providerInfo: {}
|
|
2052
2322
|
};
|
|
2053
2323
|
if (this.signInResolver) {
|
|
2054
|
-
|
|
2324
|
+
const signInResponse = await this.signInResolver({
|
|
2055
2325
|
result,
|
|
2056
2326
|
profile
|
|
2057
2327
|
}, {
|
|
@@ -2059,16 +2329,17 @@ class SamlAuthProvider {
|
|
|
2059
2329
|
catalogIdentityClient: this.catalogIdentityClient,
|
|
2060
2330
|
logger: this.logger
|
|
2061
2331
|
});
|
|
2332
|
+
response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
|
|
2062
2333
|
}
|
|
2063
2334
|
return postMessageResponse(res, this.appUrl, {
|
|
2064
2335
|
type: "authorization_response",
|
|
2065
2336
|
response
|
|
2066
2337
|
});
|
|
2067
2338
|
} catch (error) {
|
|
2068
|
-
const {name, message} = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
2339
|
+
const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
|
|
2069
2340
|
return postMessageResponse(res, this.appUrl, {
|
|
2070
2341
|
type: "authorization_response",
|
|
2071
|
-
error: {name, message}
|
|
2342
|
+
error: { name, message }
|
|
2072
2343
|
});
|
|
2073
2344
|
}
|
|
2074
2345
|
}
|
|
@@ -2079,9 +2350,9 @@ class SamlAuthProvider {
|
|
|
2079
2350
|
const samlDefaultSignInResolver = async (info, ctx) => {
|
|
2080
2351
|
const id = info.result.fullProfile.nameID;
|
|
2081
2352
|
const token = await ctx.tokenIssuer.issueToken({
|
|
2082
|
-
claims: {sub: id}
|
|
2353
|
+
claims: { sub: id }
|
|
2083
2354
|
});
|
|
2084
|
-
return {id, token};
|
|
2355
|
+
return { id, token };
|
|
2085
2356
|
};
|
|
2086
2357
|
const createSamlProvider = (options) => {
|
|
2087
2358
|
return ({
|
|
@@ -2097,7 +2368,7 @@ const createSamlProvider = (options) => {
|
|
|
2097
2368
|
catalogApi,
|
|
2098
2369
|
tokenIssuer
|
|
2099
2370
|
});
|
|
2100
|
-
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({fullProfile}) => ({
|
|
2371
|
+
const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
|
|
2101
2372
|
profile: {
|
|
2102
2373
|
email: fullProfile.email,
|
|
2103
2374
|
displayName: fullProfile.displayName
|
|
@@ -2133,191 +2404,6 @@ const createSamlProvider = (options) => {
|
|
|
2133
2404
|
};
|
|
2134
2405
|
};
|
|
2135
2406
|
|
|
2136
|
-
class Auth0Strategy extends OAuth2Strategy__default['default'] {
|
|
2137
|
-
constructor(options, verify) {
|
|
2138
|
-
const optionsWithURLs = {
|
|
2139
|
-
...options,
|
|
2140
|
-
authorizationURL: `https://${options.domain}/authorize`,
|
|
2141
|
-
tokenURL: `https://${options.domain}/oauth/token`,
|
|
2142
|
-
userInfoURL: `https://${options.domain}/userinfo`,
|
|
2143
|
-
apiUrl: `https://${options.domain}/api`
|
|
2144
|
-
};
|
|
2145
|
-
super(optionsWithURLs, verify);
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
class Auth0AuthProvider {
|
|
2150
|
-
constructor(options) {
|
|
2151
|
-
this._strategy = new Auth0Strategy({
|
|
2152
|
-
clientID: options.clientId,
|
|
2153
|
-
clientSecret: options.clientSecret,
|
|
2154
|
-
callbackURL: options.callbackUrl,
|
|
2155
|
-
domain: options.domain,
|
|
2156
|
-
passReqToCallback: false
|
|
2157
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2158
|
-
done(void 0, {
|
|
2159
|
-
fullProfile,
|
|
2160
|
-
accessToken,
|
|
2161
|
-
refreshToken,
|
|
2162
|
-
params
|
|
2163
|
-
}, {
|
|
2164
|
-
refreshToken
|
|
2165
|
-
});
|
|
2166
|
-
});
|
|
2167
|
-
}
|
|
2168
|
-
async start(req) {
|
|
2169
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
2170
|
-
accessType: "offline",
|
|
2171
|
-
prompt: "consent",
|
|
2172
|
-
scope: req.scope,
|
|
2173
|
-
state: encodeState(req.state)
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
async handler(req) {
|
|
2177
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2178
|
-
const profile = makeProfileInfo(result.fullProfile, result.params.id_token);
|
|
2179
|
-
return {
|
|
2180
|
-
response: await this.populateIdentity({
|
|
2181
|
-
profile,
|
|
2182
|
-
providerInfo: {
|
|
2183
|
-
idToken: result.params.id_token,
|
|
2184
|
-
accessToken: result.accessToken,
|
|
2185
|
-
scope: result.params.scope,
|
|
2186
|
-
expiresInSeconds: result.params.expires_in
|
|
2187
|
-
}
|
|
2188
|
-
}),
|
|
2189
|
-
refreshToken: privateInfo.refreshToken
|
|
2190
|
-
};
|
|
2191
|
-
}
|
|
2192
|
-
async refresh(req) {
|
|
2193
|
-
const {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2194
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2195
|
-
const profile = makeProfileInfo(fullProfile, params.id_token);
|
|
2196
|
-
return this.populateIdentity({
|
|
2197
|
-
providerInfo: {
|
|
2198
|
-
accessToken,
|
|
2199
|
-
idToken: params.id_token,
|
|
2200
|
-
expiresInSeconds: params.expires_in,
|
|
2201
|
-
scope: params.scope
|
|
2202
|
-
},
|
|
2203
|
-
profile
|
|
2204
|
-
});
|
|
2205
|
-
}
|
|
2206
|
-
async populateIdentity(response) {
|
|
2207
|
-
const {profile} = response;
|
|
2208
|
-
if (!profile.email) {
|
|
2209
|
-
throw new Error("Profile does not contain an email");
|
|
2210
|
-
}
|
|
2211
|
-
const id = profile.email.split("@")[0];
|
|
2212
|
-
return {...response, backstageIdentity: {id}};
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
const createAuth0Provider = (_options) => {
|
|
2216
|
-
return ({providerId, globalConfig, config, tokenIssuer}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2217
|
-
const clientId = envConfig.getString("clientId");
|
|
2218
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2219
|
-
const domain = envConfig.getString("domain");
|
|
2220
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2221
|
-
const provider = new Auth0AuthProvider({
|
|
2222
|
-
clientId,
|
|
2223
|
-
clientSecret,
|
|
2224
|
-
callbackUrl,
|
|
2225
|
-
domain
|
|
2226
|
-
});
|
|
2227
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2228
|
-
disableRefresh: true,
|
|
2229
|
-
providerId,
|
|
2230
|
-
tokenIssuer
|
|
2231
|
-
});
|
|
2232
|
-
});
|
|
2233
|
-
};
|
|
2234
|
-
|
|
2235
|
-
class OneLoginProvider {
|
|
2236
|
-
constructor(options) {
|
|
2237
|
-
this._strategy = new passportOneloginOauth.Strategy({
|
|
2238
|
-
issuer: options.issuer,
|
|
2239
|
-
clientID: options.clientId,
|
|
2240
|
-
clientSecret: options.clientSecret,
|
|
2241
|
-
callbackURL: options.callbackUrl,
|
|
2242
|
-
passReqToCallback: false
|
|
2243
|
-
}, (accessToken, refreshToken, params, fullProfile, done) => {
|
|
2244
|
-
done(void 0, {
|
|
2245
|
-
accessToken,
|
|
2246
|
-
refreshToken,
|
|
2247
|
-
params,
|
|
2248
|
-
fullProfile
|
|
2249
|
-
}, {
|
|
2250
|
-
refreshToken
|
|
2251
|
-
});
|
|
2252
|
-
});
|
|
2253
|
-
}
|
|
2254
|
-
async start(req) {
|
|
2255
|
-
return await executeRedirectStrategy(req, this._strategy, {
|
|
2256
|
-
accessType: "offline",
|
|
2257
|
-
prompt: "consent",
|
|
2258
|
-
scope: "openid",
|
|
2259
|
-
state: encodeState(req.state)
|
|
2260
|
-
});
|
|
2261
|
-
}
|
|
2262
|
-
async handler(req) {
|
|
2263
|
-
const {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
|
|
2264
|
-
const profile = makeProfileInfo(result.fullProfile, result.params.id_token);
|
|
2265
|
-
return {
|
|
2266
|
-
response: await this.populateIdentity({
|
|
2267
|
-
profile,
|
|
2268
|
-
providerInfo: {
|
|
2269
|
-
idToken: result.params.id_token,
|
|
2270
|
-
accessToken: result.accessToken,
|
|
2271
|
-
scope: result.params.scope,
|
|
2272
|
-
expiresInSeconds: result.params.expires_in
|
|
2273
|
-
}
|
|
2274
|
-
}),
|
|
2275
|
-
refreshToken: privateInfo.refreshToken
|
|
2276
|
-
};
|
|
2277
|
-
}
|
|
2278
|
-
async refresh(req) {
|
|
2279
|
-
const {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
|
|
2280
|
-
const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
|
|
2281
|
-
const profile = makeProfileInfo(fullProfile, params.id_token);
|
|
2282
|
-
return this.populateIdentity({
|
|
2283
|
-
providerInfo: {
|
|
2284
|
-
accessToken,
|
|
2285
|
-
idToken: params.id_token,
|
|
2286
|
-
expiresInSeconds: params.expires_in,
|
|
2287
|
-
scope: params.scope
|
|
2288
|
-
},
|
|
2289
|
-
profile
|
|
2290
|
-
});
|
|
2291
|
-
}
|
|
2292
|
-
async populateIdentity(response) {
|
|
2293
|
-
const {profile} = response;
|
|
2294
|
-
if (!profile.email) {
|
|
2295
|
-
throw new Error("OIDC profile contained no email");
|
|
2296
|
-
}
|
|
2297
|
-
const id = profile.email.split("@")[0];
|
|
2298
|
-
return {...response, backstageIdentity: {id}};
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
const createOneLoginProvider = (_options) => {
|
|
2302
|
-
return ({providerId, globalConfig, config, tokenIssuer}) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
|
|
2303
|
-
const clientId = envConfig.getString("clientId");
|
|
2304
|
-
const clientSecret = envConfig.getString("clientSecret");
|
|
2305
|
-
const issuer = envConfig.getString("issuer");
|
|
2306
|
-
const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
|
|
2307
|
-
const provider = new OneLoginProvider({
|
|
2308
|
-
clientId,
|
|
2309
|
-
clientSecret,
|
|
2310
|
-
callbackUrl,
|
|
2311
|
-
issuer
|
|
2312
|
-
});
|
|
2313
|
-
return OAuthAdapter.fromConfig(globalConfig, provider, {
|
|
2314
|
-
disableRefresh: false,
|
|
2315
|
-
providerId,
|
|
2316
|
-
tokenIssuer
|
|
2317
|
-
});
|
|
2318
|
-
});
|
|
2319
|
-
};
|
|
2320
|
-
|
|
2321
2407
|
const factories = {
|
|
2322
2408
|
google: createGoogleProvider(),
|
|
2323
2409
|
github: createGithubProvider(),
|
|
@@ -2335,8 +2421,8 @@ const factories = {
|
|
|
2335
2421
|
};
|
|
2336
2422
|
|
|
2337
2423
|
function createOidcRouter(options) {
|
|
2338
|
-
const {baseUrl, tokenIssuer} = options;
|
|
2339
|
-
const router = Router__default[
|
|
2424
|
+
const { baseUrl, tokenIssuer } = options;
|
|
2425
|
+
const router = Router__default["default"]();
|
|
2340
2426
|
const config = {
|
|
2341
2427
|
issuer: baseUrl,
|
|
2342
2428
|
token_endpoint: `${baseUrl}/v1/token`,
|
|
@@ -2354,8 +2440,8 @@ function createOidcRouter(options) {
|
|
|
2354
2440
|
res.json(config);
|
|
2355
2441
|
});
|
|
2356
2442
|
router.get("/.well-known/jwks.json", async (_req, res) => {
|
|
2357
|
-
const {keys} = await tokenIssuer.listPublicKeys();
|
|
2358
|
-
res.json({keys});
|
|
2443
|
+
const { keys } = await tokenIssuer.listPublicKeys();
|
|
2444
|
+
res.json({ keys });
|
|
2359
2445
|
});
|
|
2360
2446
|
router.get("/v1/token", (_req, res) => {
|
|
2361
2447
|
res.status(501).send("Not Implemented");
|
|
@@ -2375,21 +2461,30 @@ class IdentityClient {
|
|
|
2375
2461
|
this.keyStoreUpdated = 0;
|
|
2376
2462
|
}
|
|
2377
2463
|
async authenticate(token) {
|
|
2464
|
+
var _a;
|
|
2378
2465
|
if (!token) {
|
|
2379
|
-
throw new
|
|
2466
|
+
throw new errors.AuthenticationError("No token specified");
|
|
2380
2467
|
}
|
|
2381
2468
|
const key = await this.getKey(token);
|
|
2382
2469
|
if (!key) {
|
|
2383
|
-
throw new
|
|
2470
|
+
throw new errors.AuthenticationError("No signing key matching token found");
|
|
2384
2471
|
}
|
|
2385
2472
|
const decoded = jose.JWT.IdToken.verify(token, key, {
|
|
2386
2473
|
algorithms: ["ES256"],
|
|
2387
2474
|
audience: "backstage",
|
|
2388
2475
|
issuer: this.issuer
|
|
2389
2476
|
});
|
|
2477
|
+
if (!decoded.sub) {
|
|
2478
|
+
throw new errors.AuthenticationError("No user sub found in token");
|
|
2479
|
+
}
|
|
2390
2480
|
const user = {
|
|
2391
2481
|
id: decoded.sub,
|
|
2392
|
-
|
|
2482
|
+
token,
|
|
2483
|
+
identity: {
|
|
2484
|
+
type: "user",
|
|
2485
|
+
userEntityRef: decoded.sub,
|
|
2486
|
+
ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
|
|
2487
|
+
}
|
|
2393
2488
|
};
|
|
2394
2489
|
return user;
|
|
2395
2490
|
}
|
|
@@ -2401,19 +2496,19 @@ class IdentityClient {
|
|
|
2401
2496
|
return matches == null ? void 0 : matches[1];
|
|
2402
2497
|
}
|
|
2403
2498
|
async getKey(rawJwtToken) {
|
|
2404
|
-
const {header, payload} = jose.JWT.decode(rawJwtToken, {
|
|
2499
|
+
const { header, payload } = jose.JWT.decode(rawJwtToken, {
|
|
2405
2500
|
complete: true
|
|
2406
2501
|
});
|
|
2407
|
-
const keyStoreHasKey = !!this.keyStore.get({kid: header.kid});
|
|
2502
|
+
const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
|
|
2408
2503
|
const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
|
|
2409
2504
|
if (!keyStoreHasKey && issuedAfterLastRefresh) {
|
|
2410
2505
|
await this.refreshKeyStore();
|
|
2411
2506
|
}
|
|
2412
|
-
return this.keyStore.get({kid: header.kid});
|
|
2507
|
+
return this.keyStore.get({ kid: header.kid });
|
|
2413
2508
|
}
|
|
2414
2509
|
async listPublicKeys() {
|
|
2415
2510
|
const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
|
|
2416
|
-
const response = await fetch__default[
|
|
2511
|
+
const response = await fetch__default["default"](url);
|
|
2417
2512
|
if (!response.ok) {
|
|
2418
2513
|
const payload = await response.text();
|
|
2419
2514
|
const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
|
|
@@ -2449,13 +2544,13 @@ class TokenFactory {
|
|
|
2449
2544
|
const iat = Math.floor(Date.now() / MS_IN_S);
|
|
2450
2545
|
const exp = iat + this.keyDurationSeconds;
|
|
2451
2546
|
this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
|
|
2452
|
-
return jose.JWS.sign({iss, sub, aud, iat, exp, ent}, key, {
|
|
2547
|
+
return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
|
|
2453
2548
|
alg: key.alg,
|
|
2454
2549
|
kid: key.kid
|
|
2455
2550
|
});
|
|
2456
2551
|
}
|
|
2457
2552
|
async listPublicKeys() {
|
|
2458
|
-
const {items: keys} = await this.keyStore.listKeys();
|
|
2553
|
+
const { items: keys } = await this.keyStore.listKeys();
|
|
2459
2554
|
const validKeys = [];
|
|
2460
2555
|
const expiredKeys = [];
|
|
2461
2556
|
for (const key of keys) {
|
|
@@ -2469,13 +2564,13 @@ class TokenFactory {
|
|
|
2469
2564
|
}
|
|
2470
2565
|
}
|
|
2471
2566
|
if (expiredKeys.length > 0) {
|
|
2472
|
-
const kids = expiredKeys.map(({key}) => key.kid);
|
|
2567
|
+
const kids = expiredKeys.map(({ key }) => key.kid);
|
|
2473
2568
|
this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
|
|
2474
2569
|
this.keyStore.removeKeys(kids).catch((error) => {
|
|
2475
2570
|
this.logger.error(`Failed to remove expired keys, ${error}`);
|
|
2476
2571
|
});
|
|
2477
2572
|
}
|
|
2478
|
-
return {keys: validKeys.map(({key}) => key)};
|
|
2573
|
+
return { keys: validKeys.map(({ key }) => key) };
|
|
2479
2574
|
}
|
|
2480
2575
|
async getKey() {
|
|
2481
2576
|
if (this.privateKeyPromise) {
|
|
@@ -2513,7 +2608,7 @@ class TokenFactory {
|
|
|
2513
2608
|
const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
|
|
2514
2609
|
const TABLE = "signing_keys";
|
|
2515
2610
|
const parseDate = (date) => {
|
|
2516
|
-
const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, {zone: "UTC"}) : luxon.DateTime.fromJSDate(date);
|
|
2611
|
+
const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
|
|
2517
2612
|
if (!parsedDate.isValid) {
|
|
2518
2613
|
throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
|
|
2519
2614
|
}
|
|
@@ -2521,7 +2616,7 @@ const parseDate = (date) => {
|
|
|
2521
2616
|
};
|
|
2522
2617
|
class DatabaseKeyStore {
|
|
2523
2618
|
static async create(options) {
|
|
2524
|
-
const {database} = options;
|
|
2619
|
+
const { database } = options;
|
|
2525
2620
|
await database.migrate.latest({
|
|
2526
2621
|
directory: migrationsDir
|
|
2527
2622
|
});
|
|
@@ -2552,7 +2647,7 @@ class DatabaseKeyStore {
|
|
|
2552
2647
|
|
|
2553
2648
|
class MemoryKeyStore {
|
|
2554
2649
|
constructor() {
|
|
2555
|
-
this.keys = new Map();
|
|
2650
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
2556
2651
|
}
|
|
2557
2652
|
async addKey(key) {
|
|
2558
2653
|
this.keys.set(key.kid, {
|
|
@@ -2567,7 +2662,7 @@ class MemoryKeyStore {
|
|
|
2567
2662
|
}
|
|
2568
2663
|
async listKeys() {
|
|
2569
2664
|
return {
|
|
2570
|
-
items: Array.from(this.keys).map(([, {createdAt, key: keyStr}]) => ({
|
|
2665
|
+
items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
|
|
2571
2666
|
createdAt,
|
|
2572
2667
|
key: JSON.parse(keyStr)
|
|
2573
2668
|
}))
|
|
@@ -2584,7 +2679,7 @@ class FirestoreKeyStore {
|
|
|
2584
2679
|
this.timeout = timeout;
|
|
2585
2680
|
}
|
|
2586
2681
|
static async create(settings) {
|
|
2587
|
-
const {path, timeout, ...firestoreSettings} = settings != null ? settings : {};
|
|
2682
|
+
const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
|
|
2588
2683
|
const database = new firestore.Firestore(firestoreSettings);
|
|
2589
2684
|
return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
|
|
2590
2685
|
}
|
|
@@ -2632,7 +2727,7 @@ class FirestoreKeyStore {
|
|
|
2632
2727
|
class KeyStores {
|
|
2633
2728
|
static async fromConfig(config, options) {
|
|
2634
2729
|
var _a;
|
|
2635
|
-
const {logger, database} = options != null ? options : {};
|
|
2730
|
+
const { logger, database } = options != null ? options : {};
|
|
2636
2731
|
const ks = config.getOptionalConfig("auth.keyStore");
|
|
2637
2732
|
const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
|
|
2638
2733
|
logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
|
|
@@ -2665,36 +2760,37 @@ class KeyStores {
|
|
|
2665
2760
|
}
|
|
2666
2761
|
}
|
|
2667
2762
|
|
|
2668
|
-
async function createRouter({
|
|
2669
|
-
logger,
|
|
2670
|
-
|
|
2671
|
-
discovery,
|
|
2672
|
-
database,
|
|
2673
|
-
providerFactories
|
|
2674
|
-
}) {
|
|
2675
|
-
const router = Router__default['default']();
|
|
2763
|
+
async function createRouter(options) {
|
|
2764
|
+
const { logger, config, discovery, database, providerFactories } = options;
|
|
2765
|
+
const router = Router__default["default"]();
|
|
2676
2766
|
const appUrl = config.getString("app.baseUrl");
|
|
2677
2767
|
const authUrl = await discovery.getExternalBaseUrl("auth");
|
|
2678
|
-
const keyStore = await KeyStores.fromConfig(config, {logger, database});
|
|
2768
|
+
const keyStore = await KeyStores.fromConfig(config, { logger, database });
|
|
2679
2769
|
const keyDurationSeconds = 3600;
|
|
2680
2770
|
const tokenIssuer = new TokenFactory({
|
|
2681
2771
|
issuer: authUrl,
|
|
2682
2772
|
keyStore,
|
|
2683
2773
|
keyDurationSeconds,
|
|
2684
|
-
logger: logger.child({component: "token-factory"})
|
|
2774
|
+
logger: logger.child({ component: "token-factory" })
|
|
2685
2775
|
});
|
|
2686
|
-
const catalogApi = new catalogClient.CatalogClient({discoveryApi: discovery});
|
|
2776
|
+
const catalogApi = new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
2687
2777
|
const secret = config.getOptionalString("auth.session.secret");
|
|
2688
2778
|
if (secret) {
|
|
2689
|
-
router.use(cookieParser__default[
|
|
2690
|
-
|
|
2691
|
-
router.use(
|
|
2692
|
-
|
|
2779
|
+
router.use(cookieParser__default["default"](secret));
|
|
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
|
+
}));
|
|
2787
|
+
router.use(passport__default["default"].initialize());
|
|
2788
|
+
router.use(passport__default["default"].session());
|
|
2693
2789
|
} else {
|
|
2694
|
-
router.use(cookieParser__default[
|
|
2790
|
+
router.use(cookieParser__default["default"]());
|
|
2695
2791
|
}
|
|
2696
|
-
router.use(express__default[
|
|
2697
|
-
router.use(express__default[
|
|
2792
|
+
router.use(express__default["default"].urlencoded({ extended: false }));
|
|
2793
|
+
router.use(express__default["default"].json());
|
|
2698
2794
|
const allProviderFactories = {
|
|
2699
2795
|
...factories,
|
|
2700
2796
|
...providerFactories
|
|
@@ -2708,14 +2804,14 @@ async function createRouter({
|
|
|
2708
2804
|
try {
|
|
2709
2805
|
const provider = providerFactory({
|
|
2710
2806
|
providerId,
|
|
2711
|
-
globalConfig: {baseUrl: authUrl, appUrl, isOriginAllowed},
|
|
2807
|
+
globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },
|
|
2712
2808
|
config: providersConfig.getConfig(providerId),
|
|
2713
2809
|
logger,
|
|
2714
2810
|
tokenIssuer,
|
|
2715
2811
|
discovery,
|
|
2716
2812
|
catalogApi
|
|
2717
2813
|
});
|
|
2718
|
-
const r = Router__default[
|
|
2814
|
+
const r = Router__default["default"]();
|
|
2719
2815
|
r.get("/start", provider.start.bind(provider));
|
|
2720
2816
|
r.get("/handler/frame", provider.frameHandler.bind(provider));
|
|
2721
2817
|
r.post("/handler/frame", provider.frameHandler.bind(provider));
|
|
@@ -2747,7 +2843,7 @@ async function createRouter({
|
|
|
2747
2843
|
baseUrl: authUrl
|
|
2748
2844
|
}));
|
|
2749
2845
|
router.use("/:provider/", (req) => {
|
|
2750
|
-
const {provider} = req.params;
|
|
2846
|
+
const { provider } = req.params;
|
|
2751
2847
|
throw new errors.NotFoundError(`Unknown auth provider '${provider}'`);
|
|
2752
2848
|
});
|
|
2753
2849
|
return router;
|
|
@@ -2755,9 +2851,9 @@ async function createRouter({
|
|
|
2755
2851
|
function createOriginFilter(config) {
|
|
2756
2852
|
var _a;
|
|
2757
2853
|
const appUrl = config.getString("app.baseUrl");
|
|
2758
|
-
const {origin: appOrigin} = new URL(appUrl);
|
|
2854
|
+
const { origin: appOrigin } = new URL(appUrl);
|
|
2759
2855
|
const allowedOrigins = config.getOptionalStringArray("auth.experimentalExtraAllowedOrigins");
|
|
2760
|
-
const allowedOriginPatterns = (_a = allowedOrigins == null ? void 0 : allowedOrigins.map((pattern) => new minimatch.Minimatch(pattern, {nocase: true, noglobstar: true}))) != null ? _a : [];
|
|
2856
|
+
const allowedOriginPatterns = (_a = allowedOrigins == null ? void 0 : allowedOrigins.map((pattern) => new minimatch.Minimatch(pattern, { nocase: true, noglobstar: true }))) != null ? _a : [];
|
|
2761
2857
|
return (origin) => {
|
|
2762
2858
|
if (origin === appOrigin) {
|
|
2763
2859
|
return true;
|
|
@@ -2773,6 +2869,7 @@ exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
|
|
|
2773
2869
|
exports.bitbucketUserIdSignInResolver = bitbucketUserIdSignInResolver;
|
|
2774
2870
|
exports.bitbucketUsernameSignInResolver = bitbucketUsernameSignInResolver;
|
|
2775
2871
|
exports.createAtlassianProvider = createAtlassianProvider;
|
|
2872
|
+
exports.createAuth0Provider = createAuth0Provider;
|
|
2776
2873
|
exports.createAwsAlbProvider = createAwsAlbProvider;
|
|
2777
2874
|
exports.createBitbucketProvider = createBitbucketProvider;
|
|
2778
2875
|
exports.createGithubProvider = createGithubProvider;
|
|
@@ -2782,6 +2879,7 @@ exports.createMicrosoftProvider = createMicrosoftProvider;
|
|
|
2782
2879
|
exports.createOAuth2Provider = createOAuth2Provider;
|
|
2783
2880
|
exports.createOidcProvider = createOidcProvider;
|
|
2784
2881
|
exports.createOktaProvider = createOktaProvider;
|
|
2882
|
+
exports.createOneLoginProvider = createOneLoginProvider;
|
|
2785
2883
|
exports.createOriginFilter = createOriginFilter;
|
|
2786
2884
|
exports.createRouter = createRouter;
|
|
2787
2885
|
exports.createSamlProvider = createSamlProvider;
|
|
@@ -2793,6 +2891,7 @@ exports.googleEmailSignInResolver = googleEmailSignInResolver;
|
|
|
2793
2891
|
exports.microsoftEmailSignInResolver = microsoftEmailSignInResolver;
|
|
2794
2892
|
exports.oktaEmailSignInResolver = oktaEmailSignInResolver;
|
|
2795
2893
|
exports.postMessageResponse = postMessageResponse;
|
|
2894
|
+
exports.prepareBackstageIdentityResponse = prepareBackstageIdentityResponse;
|
|
2796
2895
|
exports.readState = readState;
|
|
2797
2896
|
exports.verifyNonce = verifyNonce;
|
|
2798
2897
|
//# sourceMappingURL=index.cjs.js.map
|