@backstage/plugin-auth-backend 0.5.1 → 0.6.2

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