@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/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 passportGithub2 = require('passport-github2');
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['default'] = e;
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 jwtDecoder__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecoder);
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 got__default = /*#__PURE__*/_interopDefaultLegacy(got);
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 makeProfileInfo = (profile, idToken) => {
75
- var _a, _b;
76
- let email = void 0;
77
- if (profile.emails && profile.emails.length > 0) {
78
- const [firstEmail] = profile.emails;
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
- return {
106
- email,
107
- picture,
108
- displayName
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
- strategy.redirect = () => {
146
- reject(new Error("Unexpected redirect"));
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
- strategy.authenticate(req, {});
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
- reject(new Error(`Failed to refresh access token ${err.toString()}`));
97
+ return done(new OAuth2Strategy.InternalOAuthError("Failed to fetch user profile", err.statusCode));
162
98
  }
163
- if (!accessToken) {
164
- reject(new Error(`Failed to refresh access token, no access token received`));
99
+ if (!body) {
100
+ return done(new Error("Failed to fetch user profile, body cannot be empty"));
165
101
  }
166
- resolve({
167
- accessToken,
168
- refreshToken: newRefreshToken,
169
- params
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['default'](state, (value) => value !== void 0)).toString();
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['default'].createHash("sha256").update(script).digest("base64");
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}</script></body></html>`);
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['default'].randomBytes(16).toString("base64");
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 (response.providerInfo.refreshToken && response.providerInfo.refreshToken !== refreshToken) {
451
- this.setRefreshTokenCookie(res, response.providerInfo.refreshToken);
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 (!(identity.token || identity.idToken)) {
463
- identity.token = await this.options.tokenIssuer.issueToken({
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
- class CatalogIdentityClient {
473
- constructor(options) {
474
- this.catalogApi = options.catalogApi;
475
- this.tokenIssuer = options.tokenIssuer;
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
- async findUser(query) {
478
- const filter = {
479
- kind: "user"
480
- };
481
- for (const [key, value] of Object.entries(query.annotations)) {
482
- filter[`metadata.annotations.${key}`] = value;
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
- async resolveCatalogMembership({
498
- entityRefs,
499
- logger
500
- }) {
501
- const resolvedEntityRefs = entityRefs.map((ref) => {
502
- try {
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
- }).filter((ref) => ref !== null);
513
- const filter = resolvedEntityRefs.map((ref) => ({
514
- kind: ref.kind,
515
- "metadata.namespace": ref.namespace,
516
- "metadata.name": ref.name
517
- }));
518
- const entities = await this.catalogApi.getEntities({filter}).then((r) => r.items);
519
- if (entityRefs.length !== entities.length) {
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
- sub: userRef,
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
- class GithubAuthProvider {
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 passportGithub2.Strategy({
750
+ this._strategy = new Auth0Strategy({
555
751
  clientID: options.clientId,
556
752
  clientSecret: options.clientSecret,
557
753
  callbackURL: options.callbackUrl,
558
- tokenURL: options.tokenUrl,
559
- userProfileURL: options.userProfileUrl,
560
- authorizationURL: options.authorizationUrl
754
+ domain: options.domain,
755
+ passReqToCallback: false
561
756
  }, (accessToken, refreshToken, params, fullProfile, done) => {
562
- done(void 0, {fullProfile, params, accessToken}, {refreshToken});
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
- scope: req.scope,
568
- state: (await this.stateEncoder(req)).encodedState
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
- refreshToken: result.refreshToken,
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
- if (this.signInResolver) {
605
- response.backstageIdentity = await this.signInResolver({
606
- result,
607
- profile
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
- return response;
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 githubDefaultSignInResolver = async (info, ctx) => {
618
- const {fullProfile} = info.result;
619
- const userId = fullProfile.username || fullProfile.id;
620
- const token = await ctx.tokenIssuer.issueToken({
621
- claims: {sub: userId, ent: [`user:default/${userId}`]}
622
- });
623
- return {id: userId, token};
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 signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : githubDefaultSignInResolver;
651
- const signInResolver = (info) => signInResolverFn(info, {
652
- catalogIdentityClient,
653
- tokenIssuer,
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
- return OAuthAdapter.fromConfig(globalConfig, provider, {
674
- persistScopes: true,
675
- providerId,
676
- tokenIssuer
677
- });
678
- });
992
+ };
679
993
  };
680
994
 
681
- const gitlabDefaultSignInResolver = async (info, ctx) => {
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.tokenIssuer = options.tokenIssuer;
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
- baseURL: options.baseUrl
1006
+ passReqToCallback: false
710
1007
  }, (accessToken, refreshToken, params, fullProfile, done) => {
711
- done(void 0, {fullProfile, params, accessToken}, {
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 this.handleResult({
737
- fullProfile,
738
- params,
739
- accessToken,
740
- refreshToken: newRefreshToken
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
- const {profile} = await this.authHandler(result);
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 createGitlabProvider = (options) => {
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, _b, _c;
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 = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
788
- const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : gitlabDefaultSignInResolver;
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 GitlabAuthProvider({
1118
+ const provider = new BitbucketAuthProvider({
795
1119
  clientId,
796
1120
  clientSecret,
797
1121
  callbackUrl,
798
- baseUrl,
1122
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
799
1123
  authHandler,
800
- signInResolver,
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 GoogleAuthProvider {
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 passportGoogleOauth20.Strategy({
1144
+ this._strategy = new passportGithub2.Strategy({
821
1145
  clientID: options.clientId,
822
1146
  clientSecret: options.clientSecret,
823
1147
  callbackURL: options.callbackUrl,
824
- passReqToCallback: false
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: encodeState(req.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 this.handleResult({
855
- fullProfile,
856
- params,
857
- accessToken,
858
- refreshToken: req.refreshToken
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: result.params.expires_in
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 googleEmailSignInResolver = async (info, ctx) => {
886
- const {profile} = info;
887
- if (!profile.email) {
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 createGoogleProvider = (options) => {
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 callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
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, params}) => ({
939
- profile: makeProfileInfo(fullProfile, params.id_token)
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 : googleDefaultSignInResolver;
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 provider = new GoogleAuthProvider({
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
- disableRefresh: false,
1261
+ persistScopes: true,
959
1262
  providerId,
960
1263
  tokenIssuer
961
1264
  });
962
1265
  });
963
1266
  };
964
1267
 
965
- class MicrosoftAuthProvider {
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._strategy = new passportMicrosoft.Strategy({
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
- authorizationURL: options.authorizationUrl,
977
- tokenURL: options.tokenUrl,
978
- passReqToCallback: false
1296
+ baseURL: options.baseUrl
979
1297
  }, (accessToken, refreshToken, params, fullProfile, done) => {
980
- done(void 0, {fullProfile, accessToken, params}, {refreshToken});
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 this.handleResult({
1000
- fullProfile,
1001
- params,
1002
- accessToken,
1003
- refreshToken: req.refreshToken
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 photo = await this.getUserPhoto(result.accessToken);
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 microsoftEmailSignInResolver = async (info, ctx) => {
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 tenantId = envConfig.getString("tenantId");
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) ? options.authHandler : async ({fullProfile, params}) => ({
1095
- profile: makeProfileInfo(fullProfile, params.id_token)
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 MicrosoftAuthProvider({
1378
+ const provider = new GitlabAuthProvider({
1104
1379
  clientId,
1105
1380
  clientSecret,
1106
1381
  callbackUrl,
1107
- authorizationUrl,
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 OAuth2AuthProvider {
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 OAuth2Strategy.Strategy({
1404
+ this._strategy = new passportGoogleOauth20.Strategy({
1131
1405
  clientID: options.clientId,
1132
1406
  clientSecret: options.clientSecret,
1133
1407
  callbackURL: options.callbackUrl,
1134
- authorizationURL: options.authorizationUrl,
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 refreshTokenResponse = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
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 this.handleResult({
1176
- fullProfile,
1177
- params,
1178
- accessToken,
1179
- refreshToken: updatedRefreshToken
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 oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
1211
- const {profile} = info;
1471
+ const googleEmailSignInResolver = async (info, ctx) => {
1472
+ const { profile } = info;
1212
1473
  if (!profile.email) {
1213
- throw new Error("Profile contained no email");
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 createOAuth2Provider = (options) => {
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, _c;
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 = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver$1;
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 OAuth2AuthProvider({
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
- authorizationUrl,
1260
- tokenUrl,
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 OidcAuthProvider {
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
- const {strategy} = await this.implementation;
1285
- const options = {
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 {strategy} = await this.implementation;
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: identityResponse,
1578
+ response: await this.handleResult(result),
1305
1579
  refreshToken: privateInfo.refreshToken
1306
1580
  };
1307
1581
  }
1308
1582
  async refresh(req) {
1309
- const {client} = await this.implementation;
1310
- const tokenset = await client.refresh(req.refreshToken);
1311
- if (!tokenset.access_token) {
1312
- throw new Error("Refresh failed");
1313
- }
1314
- const profile = await client.userinfo(tokenset.access_token);
1315
- return this.handleResult({tokenset, userinfo: profile});
1316
- }
1317
- async setupStrategy(options) {
1318
- const issuer = await openidClient.Issuer.discover(options.metadataUrl);
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 {profile} = await this.authHandler(result);
1344
- const response = {
1345
- providerInfo: {
1346
- idToken: result.tokenset.id_token,
1347
- accessToken: result.tokenset.access_token,
1348
- refreshToken: result.tokenset.refresh_token,
1349
- scope: result.tokenset.scope,
1350
- expiresInSeconds: result.tokenset.expires_in
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 oAuth2DefaultSignInResolver = async (info, ctx) => {
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 createOidcProvider = (options) => {
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 metadataUrl = envConfig.getString("metadataUrl");
1392
- const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
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 ({userinfo}) => ({
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 : oAuth2DefaultSignInResolver;
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 OidcAuthProvider({
1689
+ const provider = new MicrosoftAuthProvider({
1413
1690
  clientId,
1414
1691
  clientSecret,
1415
1692
  callbackUrl,
1416
- tokenSignedResponseAlg,
1417
- metadataUrl,
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 OktaAuthProvider {
1709
+ class OAuth2AuthProvider {
1435
1710
  constructor(options) {
1436
- this._store = {
1437
- store(_req, cb) {
1438
- cb(null, null);
1439
- },
1440
- verify(_req, _state, cb) {
1441
- cb(null, true);
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
- audience: options.audience,
1720
+ authorizationURL: options.authorizationUrl,
1721
+ tokenURL: options.tokenUrl,
1454
1722
  passReqToCallback: false,
1455
- store: this._store,
1456
- response_type: "code"
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 {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
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 this.handleResult({
1487
- fullProfile,
1488
- params,
1489
- accessToken,
1490
- refreshToken: req.refreshToken
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._authHandler(result);
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._signInResolver) {
1505
- response.backstageIdentity = await this._signInResolver({
1777
+ if (this.signInResolver) {
1778
+ response.backstageIdentity = await this.signInResolver({
1506
1779
  result,
1507
1780
  profile
1508
1781
  }, {
1509
- tokenIssuer: this._tokenIssuer,
1510
- catalogIdentityClient: this._catalogIdentityClient,
1511
- logger: this._logger
1782
+ tokenIssuer: this.tokenIssuer,
1783
+ catalogIdentityClient: this.catalogIdentityClient,
1784
+ logger: this.logger
1512
1785
  });
1513
1786
  }
1514
1787
  return response;
1515
1788
  }
1516
- }
1517
- const oktaEmailSignInResolver = async (info, ctx) => {
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
- const entity = await ctx.catalogIdentityClient.findUser({
1523
- annotations: {
1524
- "okta.com/email": profile.email
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("Okta profile contained no email");
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 createOktaProvider = (_options) => {
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
- if (!audience.startsWith("https://")) {
1557
- throw new Error("URL for 'audience' must start with 'https://'.");
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 = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({fullProfile, params}) => ({
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 = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
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 OktaAuthProvider({
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
- logger
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: false,
1850
+ disableRefresh,
1585
1851
  providerId,
1586
1852
  tokenIssuer
1587
1853
  });
1588
1854
  });
1589
1855
  };
1590
1856
 
1591
- class BitbucketAuthProvider {
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
- return await executeRedirectStrategy(req, this._strategy, {
1616
- accessType: "offline",
1617
- prompt: "consent",
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 {result, privateInfo} = await executeFrameHandlerStrategy(req, this._strategy);
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 {accessToken, params} = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
1631
- const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
1632
- return this.handleResult({
1633
- fullProfile,
1634
- params,
1635
- accessToken,
1636
- refreshToken: req.refreshToken
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
- result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
1641
- const {profile} = await this.authHandler(result);
1926
+ const { profile } = await this.authHandler(result);
1642
1927
  const response = {
1643
1928
  providerInfo: {
1644
- idToken: result.params.id_token,
1645
- accessToken: result.accessToken,
1646
- scope: result.params.scope,
1647
- expiresInSeconds: result.params.expires_in
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
- const claims = getEntityClaims(entity);
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 createBitbucketProvider = (options) => {
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 ({fullProfile, params}) => ({
1710
- profile: makeProfileInfo(fullProfile, params.id_token)
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 provider = new BitbucketAuthProvider({
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
- signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
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
- const defaultScopes = ["offline_access", "read:me"];
1731
- class AtlassianStrategy extends OAuth2Strategy__default['default'] {
1732
- constructor(options, verify) {
1733
- if (!options.scope) {
1734
- throw new TypeError("Atlassian requires a scope option");
1735
- }
1736
- const scopes = options.scope.split(" ");
1737
- const optionsWithURLs = {
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
- const atlassianDefaultAuthHandler = async ({
1785
- fullProfile,
1786
- params
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
- scope: options.scopes
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
- var _a;
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: (_a = result.refreshToken) != null ? _a : ""
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.authHandler(result);
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.signInResolver) {
1837
- response.backstageIdentity = await this.signInResolver({
2088
+ if (this._signInResolver) {
2089
+ response.backstageIdentity = await this._signInResolver({
1838
2090
  result,
1839
2091
  profile
1840
2092
  }, {
1841
- tokenIssuer: this.tokenIssuer,
1842
- catalogIdentityClient: this.catalogIdentityClient,
1843
- logger: this.logger
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 createAtlassianProvider = (options) => {
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 scopes = envConfig.getString("scopes");
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 = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
1882
- const provider = new AtlassianAuthProvider({
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: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
2162
+ signInResolver,
2163
+ tokenIssuer,
1889
2164
  catalogIdentityClient,
1890
- logger,
1891
- tokenIssuer
2165
+ logger
1892
2166
  });
1893
2167
  return OAuthAdapter.fromConfig(globalConfig, provider, {
1894
- disableRefresh: true,
2168
+ disableRefresh: false,
1895
2169
  providerId,
1896
2170
  tokenIssuer
1897
2171
  });
1898
2172
  });
1899
2173
  };
1900
2174
 
1901
- const ALB_JWT_HEADER = "x-amzn-oidc-data";
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.keyCache = new NodeCache__default['default']({stdTTL: 3600});
1917
- }
1918
- frameHandler() {
1919
- return Promise.resolve(void 0);
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 refresh(req, res) {
1922
- try {
1923
- const result = await this.getResult(req);
1924
- const response = await this.handleResult(result);
1925
- res.json(response);
1926
- } catch (e) {
1927
- this.logger.error("Exception occurred during AWS ALB token refresh", e);
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
- start() {
1933
- return Promise.resolve(void 0);
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 getResult(req) {
1936
- const jwt = req.header(ALB_JWT_HEADER);
1937
- const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);
1938
- if (jwt === void 0) {
1939
- throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_JWT_HEADER}`);
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
- expiresInSeconds: claims.exp,
2220
+ params,
1966
2221
  accessToken
1967
- };
1968
- } catch (e) {
1969
- throw new Error(`Exception occurred during JWT processing: ${e}`);
1970
- }
2222
+ }),
2223
+ refreshToken
2224
+ };
1971
2225
  }
1972
2226
  async handleResult(result) {
1973
- const {profile} = await this.authHandler(result);
1974
- const backstageIdentity = await this.signInResolver({
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
- expiresInSeconds: result.expiresInSeconds
2232
+ scope: result.params.scope,
2233
+ expiresInSeconds: result.params.expires_in
1986
2234
  },
1987
- backstageIdentity,
1988
2235
  profile
1989
2236
  };
1990
- }
1991
- async getKey(keyId) {
1992
- const optionalCacheKey = this.keyCache.get(keyId);
1993
- if (optionalCacheKey) {
1994
- return crypto__namespace.createPublicKey(optionalCacheKey);
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
- const keyText = await fetch__default['default'](`https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`).then((response) => response.text());
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 createAwsAlbProvider = (options) => {
2003
- return ({config, tokenIssuer, catalogApi, logger}) => {
2004
- const region = config.getString("region");
2005
- const issuer = config.getOptionalString("iss");
2006
- if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
2007
- throw new Error("SignInResolver is required to use this authentication provider");
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
- return new AwsAlbAuthProvider({
2018
- region,
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
- response.backstageIdentity = await this.signInResolver({
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['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 Error("No token specified");
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 Error("No signing key matching token found");
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
- idToken: token
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['default'](url);
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
- config,
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['default'](secret));
2690
- router.use(session__default['default']({secret, saveUninitialized: false, resave: false}));
2691
- router.use(passport__default['default'].initialize());
2692
- router.use(passport__default['default'].session());
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['default']());
2790
+ router.use(cookieParser__default["default"]());
2695
2791
  }
2696
- router.use(express__default['default'].urlencoded({extended: false}));
2697
- router.use(express__default['default'].json());
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['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