@backstage/plugin-auth-backend 0.5.2 → 0.6.0

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