@backstage/plugin-auth-backend 0.6.2 → 0.9.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -20,17 +20,17 @@ var passportGithub2 = require('passport-github2');
20
20
  var passportGitlab2 = require('passport-gitlab2');
21
21
  var passportGoogleOauth20 = require('passport-google-oauth20');
22
22
  var passportMicrosoft = require('passport-microsoft');
23
+ var uuid = require('uuid');
24
+ var luxon = require('luxon');
25
+ var backendCommon = require('@backstage/backend-common');
26
+ var firestore = require('@google-cloud/firestore');
27
+ var lodash = require('lodash');
23
28
  var openidClient = require('openid-client');
24
29
  var passportOktaOauth = require('passport-okta-oauth');
25
30
  var passportOneloginOauth = require('passport-onelogin-oauth');
26
31
  var passportSaml = require('passport-saml');
27
32
  var googleAuthLibrary = require('google-auth-library');
28
33
  var catalogClient = require('@backstage/catalog-client');
29
- var uuid = require('uuid');
30
- var luxon = require('luxon');
31
- var backendCommon = require('@backstage/backend-common');
32
- var firestore = require('@google-cloud/firestore');
33
- var lodash = require('lodash');
34
34
  var session = require('express-session');
35
35
  var passport = require('passport');
36
36
  var minimatch = require('minimatch');
@@ -149,6 +149,16 @@ const verifyNonce = (req, providerId) => {
149
149
  throw new Error("Invalid nonce");
150
150
  }
151
151
  };
152
+ const getCookieConfig = (authUrl, providerId) => {
153
+ const { hostname: cookieDomain, pathname, protocol } = authUrl;
154
+ const secure = protocol === "https:";
155
+ const cookiePath = pathname.endsWith(`${providerId}/handler/frame`) ? pathname.slice(0, -"/handler/frame".length) : `${pathname}/${providerId}`;
156
+ return {
157
+ cookieDomain,
158
+ cookiePath,
159
+ secure
160
+ };
161
+ };
152
162
 
153
163
  class OAuthEnvironmentHandler {
154
164
  constructor(handlers) {
@@ -245,6 +255,10 @@ function parseJwtPayload(token) {
245
255
  }
246
256
  function prepareBackstageIdentityResponse(result) {
247
257
  const { sub, ent } = parseJwtPayload(result.token);
258
+ const userEntityRef = catalogModel.stringifyEntityRef(catalogModel.parseEntityRef(sub, {
259
+ defaultKind: "user",
260
+ defaultNamespace: catalogModel.ENTITY_DEFAULT_NAMESPACE
261
+ }));
248
262
  return {
249
263
  ...{
250
264
  idToken: result.token,
@@ -252,7 +266,7 @@ function prepareBackstageIdentityResponse(result) {
252
266
  },
253
267
  identity: {
254
268
  type: "user",
255
- userEntityRef: sub,
269
+ userEntityRef,
256
270
  ownershipEntityRefs: ent != null ? ent : []
257
271
  }
258
272
  };
@@ -309,14 +323,14 @@ class OAuthAdapter {
309
323
  };
310
324
  }
311
325
  static fromConfig(config, handlers, options) {
326
+ var _a;
312
327
  const { origin: appOrigin } = new url.URL(config.appUrl);
313
- const secure = config.baseUrl.startsWith("https://");
314
- const url$1 = new url.URL(config.baseUrl);
315
- const cookiePath = `${url$1.pathname}/${options.providerId}`;
328
+ const authUrl = new url.URL((_a = options.callbackUrl) != null ? _a : config.baseUrl);
329
+ const { cookieDomain, cookiePath, secure } = getCookieConfig(authUrl, options.providerId);
316
330
  return new OAuthAdapter(handlers, {
317
331
  ...options,
318
332
  appOrigin,
319
- cookieDomain: url$1.hostname,
333
+ cookieDomain,
320
334
  cookiePath,
321
335
  secure,
322
336
  isOriginAllowed: config.isOriginAllowed
@@ -546,7 +560,7 @@ const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) =>
546
560
  class CatalogIdentityClient {
547
561
  constructor(options) {
548
562
  this.catalogApi = options.catalogApi;
549
- this.tokenIssuer = options.tokenIssuer;
563
+ this.tokenManager = options.tokenManager;
550
564
  }
551
565
  async findUser(query) {
552
566
  const filter = {
@@ -555,9 +569,7 @@ class CatalogIdentityClient {
555
569
  for (const [key, value] of Object.entries(query.annotations)) {
556
570
  filter[`metadata.annotations.${key}`] = value;
557
571
  }
558
- const token = await this.tokenIssuer.issueToken({
559
- claims: { sub: "backstage.io/auth-backend" }
560
- });
572
+ const { token } = await this.tokenManager.getToken();
561
573
  const { items } = await this.catalogApi.getEntities({ filter }, { token });
562
574
  if (items.length !== 1) {
563
575
  if (items.length > 1) {
@@ -587,7 +599,8 @@ class CatalogIdentityClient {
587
599
  "metadata.namespace": ref.namespace,
588
600
  "metadata.name": ref.name
589
601
  }));
590
- const entities = await this.catalogApi.getEntities({ filter }).then((r) => r.items);
602
+ const { token } = await this.tokenManager.getToken();
603
+ const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
591
604
  if (entityRefs.length !== entities.length) {
592
605
  const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
593
606
  const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
@@ -655,7 +668,12 @@ class AtlassianAuthProvider {
655
668
  };
656
669
  }
657
670
  async handleResult(result) {
658
- const { profile } = await this.authHandler(result);
671
+ const context = {
672
+ logger: this.logger,
673
+ catalogIdentityClient: this.catalogIdentityClient,
674
+ tokenIssuer: this.tokenIssuer
675
+ };
676
+ const { profile } = await this.authHandler(result, context);
659
677
  const response = {
660
678
  providerInfo: {
661
679
  idToken: result.params.id_token,
@@ -669,11 +687,7 @@ class AtlassianAuthProvider {
669
687
  response.backstageIdentity = await this.signInResolver({
670
688
  result,
671
689
  profile
672
- }, {
673
- tokenIssuer: this.tokenIssuer,
674
- catalogIdentityClient: this.catalogIdentityClient,
675
- logger: this.logger
676
- });
690
+ }, context);
677
691
  }
678
692
  return response;
679
693
  }
@@ -696,6 +710,7 @@ const createAtlassianProvider = (options) => {
696
710
  globalConfig,
697
711
  config,
698
712
  tokenIssuer,
713
+ tokenManager,
699
714
  catalogApi,
700
715
  logger
701
716
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -706,7 +721,7 @@ const createAtlassianProvider = (options) => {
706
721
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
707
722
  const catalogIdentityClient = new CatalogIdentityClient({
708
723
  catalogApi,
709
- tokenIssuer
724
+ tokenManager
710
725
  });
711
726
  const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
712
727
  const provider = new AtlassianAuthProvider({
@@ -793,7 +808,12 @@ class Auth0AuthProvider {
793
808
  };
794
809
  }
795
810
  async handleResult(result) {
796
- const { profile } = await this.authHandler(result);
811
+ const context = {
812
+ logger: this.logger,
813
+ catalogIdentityClient: this.catalogIdentityClient,
814
+ tokenIssuer: this.tokenIssuer
815
+ };
816
+ const { profile } = await this.authHandler(result, context);
797
817
  const response = {
798
818
  providerInfo: {
799
819
  idToken: result.params.id_token,
@@ -807,11 +827,7 @@ class Auth0AuthProvider {
807
827
  response.backstageIdentity = await this.signInResolver({
808
828
  result,
809
829
  profile
810
- }, {
811
- tokenIssuer: this.tokenIssuer,
812
- catalogIdentityClient: this.catalogIdentityClient,
813
- logger: this.logger
814
- });
830
+ }, context);
815
831
  }
816
832
  return response;
817
833
  }
@@ -830,6 +846,7 @@ const createAuth0Provider = (options) => {
830
846
  globalConfig,
831
847
  config,
832
848
  tokenIssuer,
849
+ tokenManager,
833
850
  catalogApi,
834
851
  logger
835
852
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -840,7 +857,7 @@ const createAuth0Provider = (options) => {
840
857
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
841
858
  const catalogIdentityClient = new CatalogIdentityClient({
842
859
  catalogApi,
843
- tokenIssuer
860
+ tokenManager
844
861
  });
845
862
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
846
863
  profile: makeProfileInfo(fullProfile, params.id_token)
@@ -937,15 +954,16 @@ class AwsAlbAuthProvider {
937
954
  }
938
955
  }
939
956
  async handleResult(result) {
940
- const { profile } = await this.authHandler(result);
941
- const backstageIdentity = await this.signInResolver({
942
- result,
943
- profile
944
- }, {
957
+ const context = {
945
958
  tokenIssuer: this.tokenIssuer,
946
959
  catalogIdentityClient: this.catalogIdentityClient,
947
960
  logger: this.logger
948
- });
961
+ };
962
+ const { profile } = await this.authHandler(result, context);
963
+ const backstageIdentity = await this.signInResolver({
964
+ result,
965
+ profile
966
+ }, context);
949
967
  return {
950
968
  providerInfo: {
951
969
  accessToken: result.accessToken,
@@ -967,7 +985,7 @@ class AwsAlbAuthProvider {
967
985
  }
968
986
  }
969
987
  const createAwsAlbProvider = (options) => {
970
- return ({ config, tokenIssuer, catalogApi, logger }) => {
988
+ return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
971
989
  const region = config.getString("region");
972
990
  const issuer = config.getOptionalString("iss");
973
991
  if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
@@ -975,7 +993,7 @@ const createAwsAlbProvider = (options) => {
975
993
  }
976
994
  const catalogIdentityClient = new CatalogIdentityClient({
977
995
  catalogApi,
978
- tokenIssuer
996
+ tokenManager
979
997
  });
980
998
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
981
999
  profile: makeProfileInfo(fullProfile)
@@ -1045,7 +1063,12 @@ class BitbucketAuthProvider {
1045
1063
  }
1046
1064
  async handleResult(result) {
1047
1065
  result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
1048
- const { profile } = await this.authHandler(result);
1066
+ const context = {
1067
+ logger: this.logger,
1068
+ catalogIdentityClient: this.catalogIdentityClient,
1069
+ tokenIssuer: this.tokenIssuer
1070
+ };
1071
+ const { profile } = await this.authHandler(result, context);
1049
1072
  const response = {
1050
1073
  providerInfo: {
1051
1074
  idToken: result.params.id_token,
@@ -1059,11 +1082,7 @@ class BitbucketAuthProvider {
1059
1082
  response.backstageIdentity = await this.signInResolver({
1060
1083
  result,
1061
1084
  profile
1062
- }, {
1063
- tokenIssuer: this.tokenIssuer,
1064
- catalogIdentityClient: this.catalogIdentityClient,
1065
- logger: this.logger
1066
- });
1085
+ }, context);
1067
1086
  }
1068
1087
  return response;
1069
1088
  }
@@ -1102,6 +1121,7 @@ const createBitbucketProvider = (options) => {
1102
1121
  globalConfig,
1103
1122
  config,
1104
1123
  tokenIssuer,
1124
+ tokenManager,
1105
1125
  catalogApi,
1106
1126
  logger
1107
1127
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1111,7 +1131,7 @@ const createBitbucketProvider = (options) => {
1111
1131
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1112
1132
  const catalogIdentityClient = new CatalogIdentityClient({
1113
1133
  catalogApi,
1114
- tokenIssuer
1134
+ tokenManager
1115
1135
  });
1116
1136
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1117
1137
  profile: makeProfileInfo(fullProfile, params.id_token)
@@ -1179,7 +1199,12 @@ class GithubAuthProvider {
1179
1199
  };
1180
1200
  }
1181
1201
  async handleResult(result) {
1182
- const { profile } = await this.authHandler(result);
1202
+ const context = {
1203
+ logger: this.logger,
1204
+ catalogIdentityClient: this.catalogIdentityClient,
1205
+ tokenIssuer: this.tokenIssuer
1206
+ };
1207
+ const { profile } = await this.authHandler(result, context);
1183
1208
  const expiresInStr = result.params.expires_in;
1184
1209
  const response = {
1185
1210
  providerInfo: {
@@ -1193,11 +1218,7 @@ class GithubAuthProvider {
1193
1218
  response.backstageIdentity = await this.signInResolver({
1194
1219
  result,
1195
1220
  profile
1196
- }, {
1197
- tokenIssuer: this.tokenIssuer,
1198
- catalogIdentityClient: this.catalogIdentityClient,
1199
- logger: this.logger
1200
- });
1221
+ }, context);
1201
1222
  }
1202
1223
  return response;
1203
1224
  }
@@ -1206,7 +1227,10 @@ const githubDefaultSignInResolver = async (info, ctx) => {
1206
1227
  const { fullProfile } = info.result;
1207
1228
  const userId = fullProfile.username || fullProfile.id;
1208
1229
  const token = await ctx.tokenIssuer.issueToken({
1209
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1230
+ claims: {
1231
+ sub: `user:default/${userId}`,
1232
+ ent: [`user:default/${userId}`]
1233
+ }
1210
1234
  });
1211
1235
  return { id: userId, token };
1212
1236
  };
@@ -1216,6 +1240,7 @@ const createGithubProvider = (options) => {
1216
1240
  globalConfig,
1217
1241
  config,
1218
1242
  tokenIssuer,
1243
+ tokenManager,
1219
1244
  catalogApi,
1220
1245
  logger
1221
1246
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1230,7 +1255,7 @@ const createGithubProvider = (options) => {
1230
1255
  const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1231
1256
  const catalogIdentityClient = new CatalogIdentityClient({
1232
1257
  catalogApi,
1233
- tokenIssuer
1258
+ tokenManager
1234
1259
  });
1235
1260
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
1236
1261
  profile: makeProfileInfo(fullProfile)
@@ -1261,7 +1286,8 @@ const createGithubProvider = (options) => {
1261
1286
  return OAuthAdapter.fromConfig(globalConfig, provider, {
1262
1287
  persistScopes: true,
1263
1288
  providerId,
1264
- tokenIssuer
1289
+ tokenIssuer,
1290
+ callbackUrl
1265
1291
  });
1266
1292
  });
1267
1293
  };
@@ -1273,7 +1299,7 @@ const gitlabDefaultSignInResolver = async (info, ctx) => {
1273
1299
  id = profile.email.split("@")[0];
1274
1300
  }
1275
1301
  const token = await ctx.tokenIssuer.issueToken({
1276
- claims: { sub: id, ent: [`user:default/${id}`] }
1302
+ claims: { sub: `user:default/${id}`, ent: [`user:default/${id}`] }
1277
1303
  });
1278
1304
  return { id, token };
1279
1305
  };
@@ -1327,7 +1353,12 @@ class GitlabAuthProvider {
1327
1353
  };
1328
1354
  }
1329
1355
  async handleResult(result) {
1330
- const { profile } = await this.authHandler(result);
1356
+ const context = {
1357
+ logger: this.logger,
1358
+ catalogIdentityClient: this.catalogIdentityClient,
1359
+ tokenIssuer: this.tokenIssuer
1360
+ };
1361
+ const { profile } = await this.authHandler(result, context);
1331
1362
  const response = {
1332
1363
  providerInfo: {
1333
1364
  idToken: result.params.id_token,
@@ -1341,11 +1372,7 @@ class GitlabAuthProvider {
1341
1372
  response.backstageIdentity = await this.signInResolver({
1342
1373
  result,
1343
1374
  profile
1344
- }, {
1345
- tokenIssuer: this.tokenIssuer,
1346
- catalogIdentityClient: this.catalogIdentityClient,
1347
- logger: this.logger
1348
- });
1375
+ }, context);
1349
1376
  }
1350
1377
  return response;
1351
1378
  }
@@ -1356,6 +1383,7 @@ const createGitlabProvider = (options) => {
1356
1383
  globalConfig,
1357
1384
  config,
1358
1385
  tokenIssuer,
1386
+ tokenManager,
1359
1387
  catalogApi,
1360
1388
  logger
1361
1389
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1367,7 +1395,7 @@ const createGitlabProvider = (options) => {
1367
1395
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1368
1396
  const catalogIdentityClient = new CatalogIdentityClient({
1369
1397
  catalogApi,
1370
- tokenIssuer
1398
+ tokenManager
1371
1399
  });
1372
1400
  const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
1373
1401
  const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : gitlabDefaultSignInResolver;
@@ -1446,7 +1474,12 @@ class GoogleAuthProvider {
1446
1474
  };
1447
1475
  }
1448
1476
  async handleResult(result) {
1449
- const { profile } = await this.authHandler(result);
1477
+ const context = {
1478
+ logger: this.logger,
1479
+ catalogIdentityClient: this.catalogIdentityClient,
1480
+ tokenIssuer: this.tokenIssuer
1481
+ };
1482
+ const { profile } = await this.authHandler(result, context);
1450
1483
  const response = {
1451
1484
  providerInfo: {
1452
1485
  idToken: result.params.id_token,
@@ -1460,11 +1493,7 @@ class GoogleAuthProvider {
1460
1493
  response.backstageIdentity = await this.signInResolver({
1461
1494
  result,
1462
1495
  profile
1463
- }, {
1464
- tokenIssuer: this.tokenIssuer,
1465
- catalogIdentityClient: this.catalogIdentityClient,
1466
- logger: this.logger
1467
- });
1496
+ }, context);
1468
1497
  }
1469
1498
  return response;
1470
1499
  }
@@ -1501,7 +1530,7 @@ const googleDefaultSignInResolver = async (info, ctx) => {
1501
1530
  userId = profile.email.split("@")[0];
1502
1531
  }
1503
1532
  const token = await ctx.tokenIssuer.issueToken({
1504
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1533
+ claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
1505
1534
  });
1506
1535
  return { id: userId, token };
1507
1536
  };
@@ -1511,6 +1540,7 @@ const createGoogleProvider = (options) => {
1511
1540
  globalConfig,
1512
1541
  config,
1513
1542
  tokenIssuer,
1543
+ tokenManager,
1514
1544
  catalogApi,
1515
1545
  logger
1516
1546
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1520,7 +1550,7 @@ const createGoogleProvider = (options) => {
1520
1550
  const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1521
1551
  const catalogIdentityClient = new CatalogIdentityClient({
1522
1552
  catalogApi,
1523
- tokenIssuer
1553
+ tokenManager
1524
1554
  });
1525
1555
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1526
1556
  profile: makeProfileInfo(fullProfile, params.id_token)
@@ -1595,7 +1625,12 @@ class MicrosoftAuthProvider {
1595
1625
  async handleResult(result) {
1596
1626
  const photo = await this.getUserPhoto(result.accessToken);
1597
1627
  result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
1598
- const { profile } = await this.authHandler(result);
1628
+ const context = {
1629
+ logger: this.logger,
1630
+ catalogIdentityClient: this.catalogIdentityClient,
1631
+ tokenIssuer: this.tokenIssuer
1632
+ };
1633
+ const { profile } = await this.authHandler(result, context);
1599
1634
  const response = {
1600
1635
  providerInfo: {
1601
1636
  idToken: result.params.id_token,
@@ -1609,11 +1644,7 @@ class MicrosoftAuthProvider {
1609
1644
  response.backstageIdentity = await this.signInResolver({
1610
1645
  result,
1611
1646
  profile
1612
- }, {
1613
- tokenIssuer: this.tokenIssuer,
1614
- catalogIdentityClient: this.catalogIdentityClient,
1615
- logger: this.logger
1616
- });
1647
+ }, context);
1617
1648
  }
1618
1649
  return response;
1619
1650
  }
@@ -1654,7 +1685,10 @@ const microsoftDefaultSignInResolver = async (info, ctx) => {
1654
1685
  }
1655
1686
  const userId = profile.email.split("@")[0];
1656
1687
  const token = await ctx.tokenIssuer.issueToken({
1657
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1688
+ claims: {
1689
+ sub: `user:default/${userId}`,
1690
+ ent: [`user:default/${userId}`]
1691
+ }
1658
1692
  });
1659
1693
  return { id: userId, token };
1660
1694
  };
@@ -1664,6 +1698,7 @@ const createMicrosoftProvider = (options) => {
1664
1698
  globalConfig,
1665
1699
  config,
1666
1700
  tokenIssuer,
1701
+ tokenManager,
1667
1702
  catalogApi,
1668
1703
  logger
1669
1704
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1676,7 +1711,7 @@ const createMicrosoftProvider = (options) => {
1676
1711
  const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
1677
1712
  const catalogIdentityClient = new CatalogIdentityClient({
1678
1713
  catalogApi,
1679
- tokenIssuer
1714
+ tokenManager
1680
1715
  });
1681
1716
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1682
1717
  profile: makeProfileInfo(fullProfile, params.id_token)
@@ -1765,7 +1800,12 @@ class OAuth2AuthProvider {
1765
1800
  };
1766
1801
  }
1767
1802
  async handleResult(result) {
1768
- const { profile } = await this.authHandler(result);
1803
+ const context = {
1804
+ logger: this.logger,
1805
+ catalogIdentityClient: this.catalogIdentityClient,
1806
+ tokenIssuer: this.tokenIssuer
1807
+ };
1808
+ const { profile } = await this.authHandler(result, context);
1769
1809
  const response = {
1770
1810
  providerInfo: {
1771
1811
  idToken: result.params.id_token,
@@ -1779,11 +1819,7 @@ class OAuth2AuthProvider {
1779
1819
  response.backstageIdentity = await this.signInResolver({
1780
1820
  result,
1781
1821
  profile
1782
- }, {
1783
- tokenIssuer: this.tokenIssuer,
1784
- catalogIdentityClient: this.catalogIdentityClient,
1785
- logger: this.logger
1786
- });
1822
+ }, context);
1787
1823
  }
1788
1824
  return response;
1789
1825
  }
@@ -1798,7 +1834,7 @@ const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
1798
1834
  }
1799
1835
  const userId = profile.email.split("@")[0];
1800
1836
  const token = await ctx.tokenIssuer.issueToken({
1801
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1837
+ claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
1802
1838
  });
1803
1839
  return { id: userId, token };
1804
1840
  };
@@ -1808,6 +1844,7 @@ const createOAuth2Provider = (options) => {
1808
1844
  globalConfig,
1809
1845
  config,
1810
1846
  tokenIssuer,
1847
+ tokenManager,
1811
1848
  catalogApi,
1812
1849
  logger
1813
1850
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1822,7 +1859,7 @@ const createOAuth2Provider = (options) => {
1822
1859
  const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
1823
1860
  const catalogIdentityClient = new CatalogIdentityClient({
1824
1861
  catalogApi,
1825
- tokenIssuer
1862
+ tokenManager
1826
1863
  });
1827
1864
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1828
1865
  profile: makeProfileInfo(fullProfile, params.id_token)
@@ -1855,107 +1892,525 @@ const createOAuth2Provider = (options) => {
1855
1892
  });
1856
1893
  };
1857
1894
 
1858
- class OidcAuthProvider {
1895
+ function createOidcRouter(options) {
1896
+ const { baseUrl, tokenIssuer } = options;
1897
+ const router = Router__default["default"]();
1898
+ const config = {
1899
+ issuer: baseUrl,
1900
+ token_endpoint: `${baseUrl}/v1/token`,
1901
+ userinfo_endpoint: `${baseUrl}/v1/userinfo`,
1902
+ jwks_uri: `${baseUrl}/.well-known/jwks.json`,
1903
+ response_types_supported: ["id_token"],
1904
+ subject_types_supported: ["public"],
1905
+ id_token_signing_alg_values_supported: ["RS256"],
1906
+ scopes_supported: ["openid"],
1907
+ token_endpoint_auth_methods_supported: [],
1908
+ claims_supported: ["sub"],
1909
+ grant_types_supported: []
1910
+ };
1911
+ router.get("/.well-known/openid-configuration", (_req, res) => {
1912
+ res.json(config);
1913
+ });
1914
+ router.get("/.well-known/jwks.json", async (_req, res) => {
1915
+ const { keys } = await tokenIssuer.listPublicKeys();
1916
+ res.json({ keys });
1917
+ });
1918
+ router.get("/v1/token", (_req, res) => {
1919
+ res.status(501).send("Not Implemented");
1920
+ });
1921
+ router.get("/v1/userinfo", (_req, res) => {
1922
+ res.status(501).send("Not Implemented");
1923
+ });
1924
+ return router;
1925
+ }
1926
+
1927
+ const CLOCK_MARGIN_S = 10;
1928
+ class IdentityClient {
1859
1929
  constructor(options) {
1860
- this.implementation = this.setupStrategy(options);
1861
- this.scope = options.scope;
1862
- this.prompt = options.prompt;
1863
- this.signInResolver = options.signInResolver;
1864
- this.authHandler = options.authHandler;
1865
- this.tokenIssuer = options.tokenIssuer;
1866
- this.catalogIdentityClient = options.catalogIdentityClient;
1867
- this.logger = options.logger;
1930
+ this.discovery = options.discovery;
1931
+ this.issuer = options.issuer;
1932
+ this.keyStore = new jose.JWKS.KeyStore();
1933
+ this.keyStoreUpdated = 0;
1868
1934
  }
1869
- async start(req) {
1870
- const { strategy } = await this.implementation;
1871
- const options = {
1872
- scope: req.scope || this.scope || "openid profile email",
1873
- state: encodeState(req.state)
1874
- };
1875
- const prompt = this.prompt || "none";
1876
- if (prompt !== "auto") {
1877
- options.prompt = prompt;
1935
+ async authenticate(token) {
1936
+ var _a;
1937
+ if (!token) {
1938
+ throw new errors.AuthenticationError("No token specified");
1878
1939
  }
1879
- return await executeRedirectStrategy(req, strategy, options);
1880
- }
1881
- async handler(req) {
1882
- const { strategy } = await this.implementation;
1883
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
1884
- return {
1885
- response: await this.handleResult(result),
1886
- refreshToken: privateInfo.refreshToken
1940
+ const key = await this.getKey(token);
1941
+ if (!key) {
1942
+ throw new errors.AuthenticationError("No signing key matching token found");
1943
+ }
1944
+ const decoded = jose.JWT.IdToken.verify(token, key, {
1945
+ algorithms: ["ES256"],
1946
+ audience: "backstage",
1947
+ issuer: this.issuer
1948
+ });
1949
+ if (!decoded.sub) {
1950
+ throw new errors.AuthenticationError("No user sub found in token");
1951
+ }
1952
+ const user = {
1953
+ id: decoded.sub,
1954
+ token,
1955
+ identity: {
1956
+ type: "user",
1957
+ userEntityRef: decoded.sub,
1958
+ ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
1959
+ }
1887
1960
  };
1961
+ return user;
1888
1962
  }
1889
- async refresh(req) {
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");
1963
+ static getBearerToken(authorizationHeader) {
1964
+ if (typeof authorizationHeader !== "string") {
1965
+ return void 0;
1894
1966
  }
1895
- const userinfo = await client.userinfo(tokenset.access_token);
1896
- return {
1897
- response: await this.handleResult({ tokenset, userinfo }),
1898
- refreshToken: tokenset.refresh_token
1899
- };
1967
+ const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
1968
+ return matches == null ? void 0 : matches[1];
1900
1969
  }
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
- });
1970
+ async getKey(rawJwtToken) {
1971
+ const { header, payload } = jose.JWT.decode(rawJwtToken, {
1972
+ complete: true
1922
1973
  });
1923
- strategy.error = console.error;
1924
- return { strategy, client };
1974
+ const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
1975
+ const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
1976
+ if (!keyStoreHasKey && issuedAfterLastRefresh) {
1977
+ await this.refreshKeyStore();
1978
+ }
1979
+ return this.keyStore.get({ kid: header.kid });
1925
1980
  }
1926
- async handleResult(result) {
1927
- const { profile } = await this.authHandler(result);
1928
- const response = {
1929
- providerInfo: {
1930
- idToken: result.tokenset.id_token,
1931
- accessToken: result.tokenset.access_token,
1932
- scope: result.tokenset.scope,
1933
- expiresInSeconds: result.tokenset.expires_in
1934
- },
1935
- profile
1936
- };
1937
- if (this.signInResolver) {
1938
- response.backstageIdentity = await this.signInResolver({
1939
- result,
1940
- profile
1941
- }, {
1942
- tokenIssuer: this.tokenIssuer,
1943
- catalogIdentityClient: this.catalogIdentityClient,
1944
- logger: this.logger
1945
- });
1981
+ async listPublicKeys() {
1982
+ const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
1983
+ const response = await fetch__default["default"](url);
1984
+ if (!response.ok) {
1985
+ const payload = await response.text();
1986
+ const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
1987
+ throw new Error(message);
1946
1988
  }
1947
- return response;
1989
+ const publicKeys = await response.json();
1990
+ return publicKeys;
1948
1991
  }
1949
- }
1950
- const oAuth2DefaultSignInResolver = async (info, ctx) => {
1951
- const { profile } = info;
1952
- if (!profile.email) {
1953
- throw new Error("Profile contained no email");
1992
+ async refreshKeyStore() {
1993
+ const now = Date.now() / 1e3;
1994
+ const publicKeys = await this.listPublicKeys();
1995
+ this.keyStore = jose.JWKS.asKeyStore({
1996
+ keys: publicKeys.keys.map((key) => key)
1997
+ });
1998
+ this.keyStoreUpdated = now;
1954
1999
  }
1955
- const userId = profile.email.split("@")[0];
1956
- const token = await ctx.tokenIssuer.issueToken({
1957
- claims: { sub: userId, ent: [`user:default/${userId}`] }
1958
- });
2000
+ }
2001
+
2002
+ const MS_IN_S = 1e3;
2003
+ class TokenFactory {
2004
+ constructor(options) {
2005
+ this.issuer = options.issuer;
2006
+ this.logger = options.logger;
2007
+ this.keyStore = options.keyStore;
2008
+ this.keyDurationSeconds = options.keyDurationSeconds;
2009
+ }
2010
+ async issueToken(params) {
2011
+ const key = await this.getKey();
2012
+ const iss = this.issuer;
2013
+ const sub = params.claims.sub;
2014
+ const ent = params.claims.ent;
2015
+ const aud = "backstage";
2016
+ const iat = Math.floor(Date.now() / MS_IN_S);
2017
+ const exp = iat + this.keyDurationSeconds;
2018
+ this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2019
+ return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2020
+ alg: key.alg,
2021
+ kid: key.kid
2022
+ });
2023
+ }
2024
+ async listPublicKeys() {
2025
+ const { items: keys } = await this.keyStore.listKeys();
2026
+ const validKeys = [];
2027
+ const expiredKeys = [];
2028
+ for (const key of keys) {
2029
+ const expireAt = luxon.DateTime.fromJSDate(key.createdAt).plus({
2030
+ seconds: 3 * this.keyDurationSeconds
2031
+ });
2032
+ if (expireAt < luxon.DateTime.local()) {
2033
+ expiredKeys.push(key);
2034
+ } else {
2035
+ validKeys.push(key);
2036
+ }
2037
+ }
2038
+ if (expiredKeys.length > 0) {
2039
+ const kids = expiredKeys.map(({ key }) => key.kid);
2040
+ this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
2041
+ this.keyStore.removeKeys(kids).catch((error) => {
2042
+ this.logger.error(`Failed to remove expired keys, ${error}`);
2043
+ });
2044
+ }
2045
+ return { keys: validKeys.map(({ key }) => key) };
2046
+ }
2047
+ async getKey() {
2048
+ if (this.privateKeyPromise) {
2049
+ if (this.keyExpiry && luxon.DateTime.fromJSDate(this.keyExpiry) > luxon.DateTime.local()) {
2050
+ return this.privateKeyPromise;
2051
+ }
2052
+ this.logger.info(`Signing key has expired, generating new key`);
2053
+ delete this.privateKeyPromise;
2054
+ }
2055
+ this.keyExpiry = luxon.DateTime.utc().plus({
2056
+ seconds: this.keyDurationSeconds
2057
+ }).toJSDate();
2058
+ const promise = (async () => {
2059
+ const key = await jose.JWK.generate("EC", "P-256", {
2060
+ use: "sig",
2061
+ kid: uuid.v4(),
2062
+ alg: "ES256"
2063
+ });
2064
+ this.logger.info(`Created new signing key ${key.kid}`);
2065
+ await this.keyStore.addKey(key.toJWK(false));
2066
+ return key;
2067
+ })();
2068
+ this.privateKeyPromise = promise;
2069
+ try {
2070
+ await promise;
2071
+ } catch (error) {
2072
+ this.logger.error(`Failed to generate new signing key, ${error}`);
2073
+ delete this.keyExpiry;
2074
+ delete this.privateKeyPromise;
2075
+ }
2076
+ return promise;
2077
+ }
2078
+ }
2079
+
2080
+ const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
2081
+ const TABLE = "signing_keys";
2082
+ const parseDate = (date) => {
2083
+ const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
2084
+ if (!parsedDate.isValid) {
2085
+ throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
2086
+ }
2087
+ return parsedDate.toJSDate();
2088
+ };
2089
+ class DatabaseKeyStore {
2090
+ static async create(options) {
2091
+ const { database } = options;
2092
+ await database.migrate.latest({
2093
+ directory: migrationsDir
2094
+ });
2095
+ return new DatabaseKeyStore(options);
2096
+ }
2097
+ constructor(options) {
2098
+ this.database = options.database;
2099
+ }
2100
+ async addKey(key) {
2101
+ await this.database(TABLE).insert({
2102
+ kid: key.kid,
2103
+ key: JSON.stringify(key)
2104
+ });
2105
+ }
2106
+ async listKeys() {
2107
+ const rows = await this.database(TABLE).select();
2108
+ return {
2109
+ items: rows.map((row) => ({
2110
+ key: JSON.parse(row.key),
2111
+ createdAt: parseDate(row.created_at)
2112
+ }))
2113
+ };
2114
+ }
2115
+ async removeKeys(kids) {
2116
+ await this.database(TABLE).delete().whereIn("kid", kids);
2117
+ }
2118
+ }
2119
+
2120
+ class MemoryKeyStore {
2121
+ constructor() {
2122
+ this.keys = /* @__PURE__ */ new Map();
2123
+ }
2124
+ async addKey(key) {
2125
+ this.keys.set(key.kid, {
2126
+ createdAt: luxon.DateTime.utc().toJSDate(),
2127
+ key: JSON.stringify(key)
2128
+ });
2129
+ }
2130
+ async removeKeys(kids) {
2131
+ for (const kid of kids) {
2132
+ this.keys.delete(kid);
2133
+ }
2134
+ }
2135
+ async listKeys() {
2136
+ return {
2137
+ items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
2138
+ createdAt,
2139
+ key: JSON.parse(keyStr)
2140
+ }))
2141
+ };
2142
+ }
2143
+ }
2144
+
2145
+ const DEFAULT_TIMEOUT_MS = 1e4;
2146
+ const DEFAULT_DOCUMENT_PATH = "sessions";
2147
+ class FirestoreKeyStore {
2148
+ constructor(database, path, timeout) {
2149
+ this.database = database;
2150
+ this.path = path;
2151
+ this.timeout = timeout;
2152
+ }
2153
+ static async create(settings) {
2154
+ const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
2155
+ const database = new firestore.Firestore(firestoreSettings);
2156
+ return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
2157
+ }
2158
+ static async verifyConnection(keyStore, logger) {
2159
+ try {
2160
+ await keyStore.verify();
2161
+ } catch (error) {
2162
+ if (process.env.NODE_ENV !== "development") {
2163
+ throw new Error(`Failed to connect to database: ${error.message}`);
2164
+ }
2165
+ logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
2166
+ }
2167
+ }
2168
+ async addKey(key) {
2169
+ await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
2170
+ kid: key.kid,
2171
+ key: JSON.stringify(key)
2172
+ }));
2173
+ }
2174
+ async listKeys() {
2175
+ const keys = await this.withTimeout(this.database.collection(this.path).get());
2176
+ return {
2177
+ items: keys.docs.map((key) => ({
2178
+ key: key.data(),
2179
+ createdAt: key.createTime.toDate()
2180
+ }))
2181
+ };
2182
+ }
2183
+ async removeKeys(kids) {
2184
+ for (const kid of kids) {
2185
+ await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
2186
+ }
2187
+ }
2188
+ async withTimeout(operation) {
2189
+ const timer = new Promise((_, reject) => setTimeout(() => {
2190
+ reject(new Error(`Operation timed out after ${this.timeout}ms`));
2191
+ }, this.timeout));
2192
+ return Promise.race([operation, timer]);
2193
+ }
2194
+ async verify() {
2195
+ await this.withTimeout(this.database.collection(this.path).limit(1).get());
2196
+ }
2197
+ }
2198
+
2199
+ class KeyStores {
2200
+ static async fromConfig(config, options) {
2201
+ var _a;
2202
+ const { logger, database } = options != null ? options : {};
2203
+ const ks = config.getOptionalConfig("auth.keyStore");
2204
+ const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
2205
+ logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
2206
+ if (provider === "database") {
2207
+ if (!database) {
2208
+ throw new Error("This KeyStore provider requires a database");
2209
+ }
2210
+ return await DatabaseKeyStore.create({
2211
+ database: await database.getClient()
2212
+ });
2213
+ }
2214
+ if (provider === "memory") {
2215
+ return new MemoryKeyStore();
2216
+ }
2217
+ if (provider === "firestore") {
2218
+ const settings = ks == null ? void 0 : ks.getConfig(provider);
2219
+ const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2220
+ projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2221
+ keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2222
+ host: settings == null ? void 0 : settings.getOptionalString("host"),
2223
+ port: settings == null ? void 0 : settings.getOptionalNumber("port"),
2224
+ ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2225
+ path: settings == null ? void 0 : settings.getOptionalString("path"),
2226
+ timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2227
+ }, (value) => value !== void 0));
2228
+ await FirestoreKeyStore.verifyConnection(keyStore, logger);
2229
+ return keyStore;
2230
+ }
2231
+ throw new Error(`Unknown KeyStore provider: ${provider}`);
2232
+ }
2233
+ }
2234
+
2235
+ const OAUTH2_PROXY_JWT_HEADER = "X-OAUTH2-PROXY-ID-TOKEN";
2236
+ class Oauth2ProxyAuthProvider {
2237
+ constructor(options) {
2238
+ this.catalogIdentityClient = options.catalogIdentityClient;
2239
+ this.logger = options.logger;
2240
+ this.tokenIssuer = options.tokenIssuer;
2241
+ this.signInResolver = options.signInResolver;
2242
+ this.authHandler = options.authHandler;
2243
+ }
2244
+ frameHandler() {
2245
+ return Promise.resolve(void 0);
2246
+ }
2247
+ async refresh(req, res) {
2248
+ try {
2249
+ const result = this.getResult(req);
2250
+ const response = await this.handleResult(result);
2251
+ res.json(response);
2252
+ } catch (e) {
2253
+ this.logger.error(`Exception occurred during ${OAUTH2_PROXY_JWT_HEADER} refresh`, e);
2254
+ res.status(401);
2255
+ res.end();
2256
+ }
2257
+ }
2258
+ start() {
2259
+ return Promise.resolve(void 0);
2260
+ }
2261
+ async handleResult(result) {
2262
+ const ctx = {
2263
+ logger: this.logger,
2264
+ tokenIssuer: this.tokenIssuer,
2265
+ catalogIdentityClient: this.catalogIdentityClient
2266
+ };
2267
+ const { profile } = await this.authHandler(result, ctx);
2268
+ const backstageSignInResult = await this.signInResolver({
2269
+ result,
2270
+ profile
2271
+ }, ctx);
2272
+ return {
2273
+ providerInfo: {
2274
+ accessToken: result.accessToken
2275
+ },
2276
+ backstageIdentity: prepareBackstageIdentityResponse(backstageSignInResult),
2277
+ profile
2278
+ };
2279
+ }
2280
+ getResult(req) {
2281
+ const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
2282
+ const jwt = IdentityClient.getBearerToken(authHeader);
2283
+ if (!jwt) {
2284
+ throw new errors.AuthenticationError(`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`);
2285
+ }
2286
+ const decodedJWT = jose.JWT.decode(jwt);
2287
+ return {
2288
+ fullProfile: decodedJWT,
2289
+ accessToken: jwt
2290
+ };
2291
+ }
2292
+ }
2293
+ const createOauth2ProxyProvider = (options) => ({ catalogApi, logger, tokenIssuer, tokenManager }) => {
2294
+ const signInResolver = options.signIn.resolver;
2295
+ const authHandler = options.authHandler;
2296
+ const catalogIdentityClient = new CatalogIdentityClient({
2297
+ catalogApi,
2298
+ tokenManager
2299
+ });
2300
+ return new Oauth2ProxyAuthProvider({
2301
+ logger,
2302
+ signInResolver,
2303
+ authHandler,
2304
+ tokenIssuer,
2305
+ catalogIdentityClient
2306
+ });
2307
+ };
2308
+
2309
+ class OidcAuthProvider {
2310
+ constructor(options) {
2311
+ this.implementation = this.setupStrategy(options);
2312
+ this.scope = options.scope;
2313
+ this.prompt = options.prompt;
2314
+ this.signInResolver = options.signInResolver;
2315
+ this.authHandler = options.authHandler;
2316
+ this.tokenIssuer = options.tokenIssuer;
2317
+ this.catalogIdentityClient = options.catalogIdentityClient;
2318
+ this.logger = options.logger;
2319
+ }
2320
+ async start(req) {
2321
+ const { strategy } = await this.implementation;
2322
+ const options = {
2323
+ scope: req.scope || this.scope || "openid profile email",
2324
+ state: encodeState(req.state)
2325
+ };
2326
+ const prompt = this.prompt || "none";
2327
+ if (prompt !== "auto") {
2328
+ options.prompt = prompt;
2329
+ }
2330
+ return await executeRedirectStrategy(req, strategy, options);
2331
+ }
2332
+ async handler(req) {
2333
+ const { strategy } = await this.implementation;
2334
+ const { result, privateInfo } = await executeFrameHandlerStrategy(req, strategy);
2335
+ return {
2336
+ response: await this.handleResult(result),
2337
+ refreshToken: privateInfo.refreshToken
2338
+ };
2339
+ }
2340
+ async refresh(req) {
2341
+ const { client } = await this.implementation;
2342
+ const tokenset = await client.refresh(req.refreshToken);
2343
+ if (!tokenset.access_token) {
2344
+ throw new Error("Refresh failed");
2345
+ }
2346
+ const userinfo = await client.userinfo(tokenset.access_token);
2347
+ return {
2348
+ response: await this.handleResult({ tokenset, userinfo }),
2349
+ refreshToken: tokenset.refresh_token
2350
+ };
2351
+ }
2352
+ async setupStrategy(options) {
2353
+ const issuer = await openidClient.Issuer.discover(options.metadataUrl);
2354
+ const client = new issuer.Client({
2355
+ access_type: "offline",
2356
+ client_id: options.clientId,
2357
+ client_secret: options.clientSecret,
2358
+ redirect_uris: [options.callbackUrl],
2359
+ response_types: ["code"],
2360
+ id_token_signed_response_alg: options.tokenSignedResponseAlg || "RS256",
2361
+ scope: options.scope || ""
2362
+ });
2363
+ const strategy = new openidClient.Strategy({
2364
+ client,
2365
+ passReqToCallback: false
2366
+ }, (tokenset, userinfo, done) => {
2367
+ if (typeof done !== "function") {
2368
+ throw new Error("OIDC IdP must provide a userinfo_endpoint in the metadata response");
2369
+ }
2370
+ done(void 0, { tokenset, userinfo }, {
2371
+ refreshToken: tokenset.refresh_token
2372
+ });
2373
+ });
2374
+ strategy.error = console.error;
2375
+ return { strategy, client };
2376
+ }
2377
+ async handleResult(result) {
2378
+ const context = {
2379
+ logger: this.logger,
2380
+ catalogIdentityClient: this.catalogIdentityClient,
2381
+ tokenIssuer: this.tokenIssuer
2382
+ };
2383
+ const { profile } = await this.authHandler(result, context);
2384
+ const response = {
2385
+ providerInfo: {
2386
+ idToken: result.tokenset.id_token,
2387
+ accessToken: result.tokenset.access_token,
2388
+ scope: result.tokenset.scope,
2389
+ expiresInSeconds: result.tokenset.expires_in
2390
+ },
2391
+ profile
2392
+ };
2393
+ if (this.signInResolver) {
2394
+ response.backstageIdentity = await this.signInResolver({
2395
+ result,
2396
+ profile
2397
+ }, context);
2398
+ }
2399
+ return response;
2400
+ }
2401
+ }
2402
+ const oAuth2DefaultSignInResolver = async (info, ctx) => {
2403
+ const { profile } = info;
2404
+ if (!profile.email) {
2405
+ throw new Error("Profile contained no email");
2406
+ }
2407
+ const userId = profile.email.split("@")[0];
2408
+ const token = await ctx.tokenIssuer.issueToken({
2409
+ claims: {
2410
+ sub: `user:default/${userId}`,
2411
+ ent: [`user:default/${userId}`]
2412
+ }
2413
+ });
1959
2414
  return { id: userId, token };
1960
2415
  };
1961
2416
  const createOidcProvider = (options) => {
@@ -1964,6 +2419,7 @@ const createOidcProvider = (options) => {
1964
2419
  globalConfig,
1965
2420
  config,
1966
2421
  tokenIssuer,
2422
+ tokenManager,
1967
2423
  catalogApi,
1968
2424
  logger
1969
2425
  }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
@@ -1977,7 +2433,7 @@ const createOidcProvider = (options) => {
1977
2433
  const prompt = envConfig.getOptionalString("prompt");
1978
2434
  const catalogIdentityClient = new CatalogIdentityClient({
1979
2435
  catalogApi,
1980
- tokenIssuer
2436
+ tokenManager
1981
2437
  });
1982
2438
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ userinfo }) => ({
1983
2439
  profile: {
@@ -2076,7 +2532,12 @@ class OktaAuthProvider {
2076
2532
  };
2077
2533
  }
2078
2534
  async handleResult(result) {
2079
- const { profile } = await this._authHandler(result);
2535
+ const context = {
2536
+ logger: this._logger,
2537
+ catalogIdentityClient: this._catalogIdentityClient,
2538
+ tokenIssuer: this._tokenIssuer
2539
+ };
2540
+ const { profile } = await this._authHandler(result, context);
2080
2541
  const response = {
2081
2542
  providerInfo: {
2082
2543
  idToken: result.params.id_token,
@@ -2090,11 +2551,7 @@ class OktaAuthProvider {
2090
2551
  response.backstageIdentity = await this._signInResolver({
2091
2552
  result,
2092
2553
  profile
2093
- }, {
2094
- tokenIssuer: this._tokenIssuer,
2095
- catalogIdentityClient: this._catalogIdentityClient,
2096
- logger: this._logger
2097
- });
2554
+ }, context);
2098
2555
  }
2099
2556
  return response;
2100
2557
  }
@@ -2118,741 +2575,414 @@ const oktaDefaultSignInResolver = async (info, ctx) => {
2118
2575
  if (!profile.email) {
2119
2576
  throw new Error("Okta profile contained no email");
2120
2577
  }
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) => {
2128
- return ({
2129
- providerId,
2130
- globalConfig,
2131
- config,
2132
- tokenIssuer,
2133
- catalogApi,
2134
- logger
2135
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2136
- var _a, _b;
2137
- const clientId = envConfig.getString("clientId");
2138
- const clientSecret = envConfig.getString("clientSecret");
2139
- const audience = envConfig.getString("audience");
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
- }
2144
- const catalogIdentityClient = new CatalogIdentityClient({
2145
- catalogApi,
2146
- tokenIssuer
2147
- });
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,
2159
- clientId,
2160
- clientSecret,
2161
- callbackUrl,
2162
- authHandler,
2163
- signInResolver,
2164
- tokenIssuer,
2165
- catalogIdentityClient,
2166
- logger
2167
- });
2168
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2169
- disableRefresh: false,
2170
- providerId,
2171
- tokenIssuer
2172
- });
2173
- });
2174
- };
2175
-
2176
- class OneLoginProvider {
2177
- constructor(options) {
2178
- this.signInResolver = options.signInResolver;
2179
- this.authHandler = options.authHandler;
2180
- this.tokenIssuer = options.tokenIssuer;
2181
- this.catalogIdentityClient = options.catalogIdentityClient;
2182
- this.logger = options.logger;
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
- });
2199
- }
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({
2220
- fullProfile,
2221
- params,
2222
- accessToken
2223
- }),
2224
- refreshToken
2225
- };
2226
- }
2227
- async handleResult(result) {
2228
- const { profile } = await this.authHandler(result);
2229
- const response = {
2230
- providerInfo: {
2231
- idToken: result.params.id_token,
2232
- accessToken: result.accessToken,
2233
- scope: result.params.scope,
2234
- expiresInSeconds: result.params.expires_in
2235
- },
2236
- profile
2237
- };
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
- });
2247
- }
2248
- return response;
2249
- }
2250
- }
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`;
2273
- const catalogIdentityClient = new CatalogIdentityClient({
2274
- catalogApi,
2275
- tokenIssuer
2276
- });
2277
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2278
- profile: makeProfileInfo(fullProfile, params.id_token)
2279
- });
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,
2285
- issuer,
2286
- authHandler,
2287
- signInResolver,
2288
- tokenIssuer,
2289
- catalogIdentityClient,
2290
- logger
2291
- });
2292
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2293
- disableRefresh: false,
2294
- providerId,
2295
- tokenIssuer
2296
- });
2297
- });
2298
- };
2299
-
2300
- class SamlAuthProvider {
2301
- constructor(options) {
2302
- this.appUrl = options.appUrl;
2303
- this.signInResolver = options.signInResolver;
2304
- this.authHandler = options.authHandler;
2305
- this.tokenIssuer = options.tokenIssuer;
2306
- this.catalogIdentityClient = options.catalogIdentityClient;
2307
- this.logger = options.logger;
2308
- this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
2309
- done(void 0, { fullProfile });
2310
- });
2311
- }
2312
- async start(req, res) {
2313
- const { url } = await executeRedirectStrategy(req, this.strategy, {});
2314
- res.redirect(url);
2315
- }
2316
- async frameHandler(req, res) {
2317
- try {
2318
- const { result } = await executeFrameHandlerStrategy(req, this.strategy);
2319
- const { profile } = await this.authHandler(result);
2320
- const response = {
2321
- profile,
2322
- providerInfo: {}
2323
- };
2324
- if (this.signInResolver) {
2325
- const signInResponse = await this.signInResolver({
2326
- result,
2327
- profile
2328
- }, {
2329
- tokenIssuer: this.tokenIssuer,
2330
- catalogIdentityClient: this.catalogIdentityClient,
2331
- logger: this.logger
2332
- });
2333
- response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
2334
- }
2335
- return postMessageResponse(res, this.appUrl, {
2336
- type: "authorization_response",
2337
- response
2338
- });
2339
- } catch (error) {
2340
- const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
2341
- return postMessageResponse(res, this.appUrl, {
2342
- type: "authorization_response",
2343
- error: { name, message }
2344
- });
2345
- }
2346
- }
2347
- async logout(_req, res) {
2348
- res.end();
2349
- }
2350
- }
2351
- const samlDefaultSignInResolver = async (info, ctx) => {
2352
- const id = info.result.fullProfile.nameID;
2578
+ const userId = profile.email.split("@")[0];
2353
2579
  const token = await ctx.tokenIssuer.issueToken({
2354
- claims: { sub: id }
2580
+ claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
2355
2581
  });
2356
- return { id, token };
2582
+ return { id: userId, token };
2357
2583
  };
2358
- const createSamlProvider = (options) => {
2584
+ const createOktaProvider = (_options) => {
2359
2585
  return ({
2360
2586
  providerId,
2361
2587
  globalConfig,
2362
2588
  config,
2363
2589
  tokenIssuer,
2590
+ tokenManager,
2364
2591
  catalogApi,
2365
2592
  logger
2366
- }) => {
2593
+ }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2367
2594
  var _a, _b;
2595
+ const clientId = envConfig.getString("clientId");
2596
+ const clientSecret = envConfig.getString("clientSecret");
2597
+ const audience = envConfig.getString("audience");
2598
+ const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2599
+ if (!audience.startsWith("https://")) {
2600
+ throw new Error("URL for 'audience' must start with 'https://'.");
2601
+ }
2368
2602
  const catalogIdentityClient = new CatalogIdentityClient({
2369
2603
  catalogApi,
2370
- tokenIssuer
2604
+ tokenManager
2371
2605
  });
2372
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2373
- profile: {
2374
- email: fullProfile.email,
2375
- displayName: fullProfile.displayName
2376
- }
2606
+ const authHandler = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({ fullProfile, params }) => ({
2607
+ profile: makeProfileInfo(fullProfile, params.id_token)
2377
2608
  });
2378
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
2609
+ const signInResolverFn = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
2379
2610
  const signInResolver = (info) => signInResolverFn(info, {
2380
2611
  catalogIdentityClient,
2381
2612
  tokenIssuer,
2382
- logger
2383
- });
2384
- return new SamlAuthProvider({
2385
- callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2386
- entryPoint: config.getString("entryPoint"),
2387
- logoutUrl: config.getOptionalString("logoutUrl"),
2388
- audience: config.getOptionalString("audience"),
2389
- issuer: config.getString("issuer"),
2390
- cert: config.getString("cert"),
2391
- privateKey: config.getOptionalString("privateKey"),
2392
- authnContext: config.getOptionalStringArray("authnContext"),
2393
- identifierFormat: config.getOptionalString("identifierFormat"),
2394
- decryptionPvk: config.getOptionalString("decryptionPvk"),
2395
- signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2396
- digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2397
- acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2398
- tokenIssuer,
2399
- appUrl: globalConfig.appUrl,
2400
- authHandler,
2401
- signInResolver,
2402
- logger,
2403
- catalogIdentityClient
2404
- });
2405
- };
2406
- };
2407
-
2408
- const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
2409
-
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}`);
2425
- }
2426
- let payload;
2427
- try {
2428
- payload = await tokenValidator(jwtToken);
2429
- } catch (e) {
2430
- throw new errors.AuthenticationError(`Google IAP token verification failed, ${e}`);
2431
- }
2432
- if (!payload.sub || !payload.email) {
2433
- throw new errors.AuthenticationError("Google IAP token payload is missing sub and/or email claim");
2434
- }
2435
- return {
2436
- iapToken: {
2437
- ...payload,
2438
- sub: payload.sub,
2439
- email: payload.email
2440
- }
2441
- };
2442
- }
2443
- const defaultAuthHandler = async ({
2444
- iapToken
2445
- }) => ({ profile: { email: iapToken.email } });
2446
-
2447
- class GcpIapProvider {
2448
- constructor(options) {
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;
2455
- }
2456
- async start() {
2457
- }
2458
- async frameHandler() {
2459
- }
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
2467
- });
2468
- const response = {
2469
- providerInfo: { iapToken: result.iapToken },
2470
- profile,
2471
- backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity)
2472
- };
2473
- res.json(response);
2474
- }
2475
- }
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,
2485
- tokenIssuer
2486
- });
2487
- return new GcpIapProvider({
2488
- authHandler,
2489
- signInResolver,
2490
- tokenValidator,
2491
- tokenIssuer,
2492
- catalogIdentityClient,
2493
- logger
2494
- });
2495
- };
2496
- }
2497
-
2498
- const factories = {
2499
- google: createGoogleProvider(),
2500
- github: createGithubProvider(),
2501
- gitlab: createGitlabProvider(),
2502
- saml: createSamlProvider(),
2503
- okta: createOktaProvider(),
2504
- auth0: createAuth0Provider(),
2505
- microsoft: createMicrosoftProvider(),
2506
- oauth2: createOAuth2Provider(),
2507
- oidc: createOidcProvider(),
2508
- onelogin: createOneLoginProvider(),
2509
- awsalb: createAwsAlbProvider(),
2510
- bitbucket: createBitbucketProvider(),
2511
- atlassian: createAtlassianProvider()
2512
- };
2513
-
2514
- function createOidcRouter(options) {
2515
- const { baseUrl, tokenIssuer } = options;
2516
- const router = Router__default["default"]();
2517
- const config = {
2518
- issuer: baseUrl,
2519
- token_endpoint: `${baseUrl}/v1/token`,
2520
- userinfo_endpoint: `${baseUrl}/v1/userinfo`,
2521
- jwks_uri: `${baseUrl}/.well-known/jwks.json`,
2522
- response_types_supported: ["id_token"],
2523
- subject_types_supported: ["public"],
2524
- id_token_signing_alg_values_supported: ["RS256"],
2525
- scopes_supported: ["openid"],
2526
- token_endpoint_auth_methods_supported: [],
2527
- claims_supported: ["sub"],
2528
- grant_types_supported: []
2529
- };
2530
- router.get("/.well-known/openid-configuration", (_req, res) => {
2531
- res.json(config);
2532
- });
2533
- router.get("/.well-known/jwks.json", async (_req, res) => {
2534
- const { keys } = await tokenIssuer.listPublicKeys();
2535
- res.json({ keys });
2536
- });
2537
- router.get("/v1/token", (_req, res) => {
2538
- res.status(501).send("Not Implemented");
2539
- });
2540
- router.get("/v1/userinfo", (_req, res) => {
2541
- res.status(501).send("Not Implemented");
2542
- });
2543
- return router;
2544
- }
2545
-
2546
- const CLOCK_MARGIN_S = 10;
2547
- class IdentityClient {
2548
- constructor(options) {
2549
- this.discovery = options.discovery;
2550
- this.issuer = options.issuer;
2551
- this.keyStore = new jose.JWKS.KeyStore();
2552
- this.keyStoreUpdated = 0;
2553
- }
2554
- async authenticate(token) {
2555
- var _a;
2556
- if (!token) {
2557
- throw new errors.AuthenticationError("No token specified");
2558
- }
2559
- const key = await this.getKey(token);
2560
- if (!key) {
2561
- throw new errors.AuthenticationError("No signing key matching token found");
2562
- }
2563
- const decoded = jose.JWT.IdToken.verify(token, key, {
2564
- algorithms: ["ES256"],
2565
- audience: "backstage",
2566
- issuer: this.issuer
2567
- });
2568
- if (!decoded.sub) {
2569
- throw new errors.AuthenticationError("No user sub found in token");
2570
- }
2571
- const user = {
2572
- id: decoded.sub,
2573
- token,
2574
- identity: {
2575
- type: "user",
2576
- userEntityRef: decoded.sub,
2577
- ownershipEntityRefs: (_a = decoded.ent) != null ? _a : []
2578
- }
2579
- };
2580
- return user;
2581
- }
2582
- static getBearerToken(authorizationHeader) {
2583
- if (typeof authorizationHeader !== "string") {
2584
- return void 0;
2585
- }
2586
- const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
2587
- return matches == null ? void 0 : matches[1];
2588
- }
2589
- async getKey(rawJwtToken) {
2590
- const { header, payload } = jose.JWT.decode(rawJwtToken, {
2591
- complete: true
2592
- });
2593
- const keyStoreHasKey = !!this.keyStore.get({ kid: header.kid });
2594
- const issuedAfterLastRefresh = (payload == null ? void 0 : payload.iat) && payload.iat > this.keyStoreUpdated - CLOCK_MARGIN_S;
2595
- if (!keyStoreHasKey && issuedAfterLastRefresh) {
2596
- await this.refreshKeyStore();
2597
- }
2598
- return this.keyStore.get({ kid: header.kid });
2599
- }
2600
- async listPublicKeys() {
2601
- const url = `${await this.discovery.getBaseUrl("auth")}/.well-known/jwks.json`;
2602
- const response = await fetch__default["default"](url);
2603
- if (!response.ok) {
2604
- const payload = await response.text();
2605
- const message = `Request failed with ${response.status} ${response.statusText}, ${payload}`;
2606
- throw new Error(message);
2607
- }
2608
- const publicKeys = await response.json();
2609
- return publicKeys;
2610
- }
2611
- async refreshKeyStore() {
2612
- const now = Date.now() / 1e3;
2613
- const publicKeys = await this.listPublicKeys();
2614
- this.keyStore = jose.JWKS.asKeyStore({
2615
- keys: publicKeys.keys.map((key) => key)
2613
+ logger
2616
2614
  });
2617
- this.keyStoreUpdated = now;
2618
- }
2619
- }
2615
+ const provider = new OktaAuthProvider({
2616
+ audience,
2617
+ clientId,
2618
+ clientSecret,
2619
+ callbackUrl,
2620
+ authHandler,
2621
+ signInResolver,
2622
+ tokenIssuer,
2623
+ catalogIdentityClient,
2624
+ logger
2625
+ });
2626
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
2627
+ disableRefresh: false,
2628
+ providerId,
2629
+ tokenIssuer
2630
+ });
2631
+ });
2632
+ };
2620
2633
 
2621
- const MS_IN_S = 1e3;
2622
- class TokenFactory {
2634
+ class OneLoginProvider {
2623
2635
  constructor(options) {
2624
- this.issuer = options.issuer;
2636
+ this.signInResolver = options.signInResolver;
2637
+ this.authHandler = options.authHandler;
2638
+ this.tokenIssuer = options.tokenIssuer;
2639
+ this.catalogIdentityClient = options.catalogIdentityClient;
2625
2640
  this.logger = options.logger;
2626
- this.keyStore = options.keyStore;
2627
- this.keyDurationSeconds = options.keyDurationSeconds;
2641
+ this._strategy = new passportOneloginOauth.Strategy({
2642
+ issuer: options.issuer,
2643
+ clientID: options.clientId,
2644
+ clientSecret: options.clientSecret,
2645
+ callbackURL: options.callbackUrl,
2646
+ passReqToCallback: false
2647
+ }, (accessToken, refreshToken, params, fullProfile, done) => {
2648
+ done(void 0, {
2649
+ accessToken,
2650
+ refreshToken,
2651
+ params,
2652
+ fullProfile
2653
+ }, {
2654
+ refreshToken
2655
+ });
2656
+ });
2628
2657
  }
2629
- async issueToken(params) {
2630
- const key = await this.getKey();
2631
- const iss = this.issuer;
2632
- const sub = params.claims.sub;
2633
- const ent = params.claims.ent;
2634
- const aud = "backstage";
2635
- const iat = Math.floor(Date.now() / MS_IN_S);
2636
- const exp = iat + this.keyDurationSeconds;
2637
- this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2638
- return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2639
- alg: key.alg,
2640
- kid: key.kid
2658
+ async start(req) {
2659
+ return await executeRedirectStrategy(req, this._strategy, {
2660
+ accessType: "offline",
2661
+ prompt: "consent",
2662
+ scope: "openid",
2663
+ state: encodeState(req.state)
2641
2664
  });
2642
2665
  }
2643
- async listPublicKeys() {
2644
- const { items: keys } = await this.keyStore.listKeys();
2645
- const validKeys = [];
2646
- const expiredKeys = [];
2647
- for (const key of keys) {
2648
- const expireAt = luxon.DateTime.fromJSDate(key.createdAt).plus({
2649
- seconds: 3 * this.keyDurationSeconds
2650
- });
2651
- if (expireAt < luxon.DateTime.local()) {
2652
- expiredKeys.push(key);
2653
- } else {
2654
- validKeys.push(key);
2655
- }
2656
- }
2657
- if (expiredKeys.length > 0) {
2658
- const kids = expiredKeys.map(({ key }) => key.kid);
2659
- this.logger.info(`Removing expired signing keys, '${kids.join("', '")}'`);
2660
- this.keyStore.removeKeys(kids).catch((error) => {
2661
- this.logger.error(`Failed to remove expired keys, ${error}`);
2662
- });
2663
- }
2664
- return { keys: validKeys.map(({ key }) => key) };
2666
+ async handler(req) {
2667
+ const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
2668
+ return {
2669
+ response: await this.handleResult(result),
2670
+ refreshToken: privateInfo.refreshToken
2671
+ };
2665
2672
  }
2666
- async getKey() {
2667
- if (this.privateKeyPromise) {
2668
- if (this.keyExpiry && luxon.DateTime.fromJSDate(this.keyExpiry) > luxon.DateTime.local()) {
2669
- return this.privateKeyPromise;
2670
- }
2671
- this.logger.info(`Signing key has expired, generating new key`);
2672
- delete this.privateKeyPromise;
2673
- }
2674
- this.keyExpiry = luxon.DateTime.utc().plus({
2675
- seconds: this.keyDurationSeconds
2676
- }).toJSDate();
2677
- const promise = (async () => {
2678
- const key = await jose.JWK.generate("EC", "P-256", {
2679
- use: "sig",
2680
- kid: uuid.v4(),
2681
- alg: "ES256"
2682
- });
2683
- this.logger.info(`Created new signing key ${key.kid}`);
2684
- await this.keyStore.addKey(key.toJWK(false));
2685
- return key;
2686
- })();
2687
- this.privateKeyPromise = promise;
2688
- try {
2689
- await promise;
2690
- } catch (error) {
2691
- this.logger.error(`Failed to generate new signing key, ${error}`);
2692
- delete this.keyExpiry;
2693
- delete this.privateKeyPromise;
2673
+ async refresh(req) {
2674
+ const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
2675
+ const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
2676
+ return {
2677
+ response: await this.handleResult({
2678
+ fullProfile,
2679
+ params,
2680
+ accessToken
2681
+ }),
2682
+ refreshToken
2683
+ };
2684
+ }
2685
+ async handleResult(result) {
2686
+ const context = {
2687
+ logger: this.logger,
2688
+ catalogIdentityClient: this.catalogIdentityClient,
2689
+ tokenIssuer: this.tokenIssuer
2690
+ };
2691
+ const { profile } = await this.authHandler(result, context);
2692
+ const response = {
2693
+ providerInfo: {
2694
+ idToken: result.params.id_token,
2695
+ accessToken: result.accessToken,
2696
+ scope: result.params.scope,
2697
+ expiresInSeconds: result.params.expires_in
2698
+ },
2699
+ profile
2700
+ };
2701
+ if (this.signInResolver) {
2702
+ response.backstageIdentity = await this.signInResolver({
2703
+ result,
2704
+ profile
2705
+ }, context);
2694
2706
  }
2695
- return promise;
2707
+ return response;
2696
2708
  }
2697
2709
  }
2698
-
2699
- const migrationsDir = backendCommon.resolvePackagePath("@backstage/plugin-auth-backend", "migrations");
2700
- const TABLE = "signing_keys";
2701
- const parseDate = (date) => {
2702
- const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
2703
- if (!parsedDate.isValid) {
2704
- throw new Error(`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`);
2710
+ const defaultSignInResolver = async (info) => {
2711
+ const { profile } = info;
2712
+ if (!profile.email) {
2713
+ throw new Error("OIDC profile contained no email");
2705
2714
  }
2706
- return parsedDate.toJSDate();
2715
+ const id = profile.email.split("@")[0];
2716
+ return { id, token: "" };
2707
2717
  };
2708
- class DatabaseKeyStore {
2709
- static async create(options) {
2710
- const { database } = options;
2711
- await database.migrate.latest({
2712
- directory: migrationsDir
2718
+ const createOneLoginProvider = (options) => {
2719
+ return ({
2720
+ providerId,
2721
+ globalConfig,
2722
+ config,
2723
+ tokenIssuer,
2724
+ tokenManager,
2725
+ catalogApi,
2726
+ logger
2727
+ }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2728
+ var _a, _b;
2729
+ const clientId = envConfig.getString("clientId");
2730
+ const clientSecret = envConfig.getString("clientSecret");
2731
+ const issuer = envConfig.getString("issuer");
2732
+ const callbackUrl = `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2733
+ const catalogIdentityClient = new CatalogIdentityClient({
2734
+ catalogApi,
2735
+ tokenManager
2713
2736
  });
2714
- return new DatabaseKeyStore(options);
2715
- }
2737
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2738
+ profile: makeProfileInfo(fullProfile, params.id_token)
2739
+ });
2740
+ const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
2741
+ const provider = new OneLoginProvider({
2742
+ clientId,
2743
+ clientSecret,
2744
+ callbackUrl,
2745
+ issuer,
2746
+ authHandler,
2747
+ signInResolver,
2748
+ tokenIssuer,
2749
+ catalogIdentityClient,
2750
+ logger
2751
+ });
2752
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
2753
+ disableRefresh: false,
2754
+ providerId,
2755
+ tokenIssuer
2756
+ });
2757
+ });
2758
+ };
2759
+
2760
+ class SamlAuthProvider {
2716
2761
  constructor(options) {
2717
- this.database = options.database;
2718
- }
2719
- async addKey(key) {
2720
- await this.database(TABLE).insert({
2721
- kid: key.kid,
2722
- key: JSON.stringify(key)
2762
+ this.appUrl = options.appUrl;
2763
+ this.signInResolver = options.signInResolver;
2764
+ this.authHandler = options.authHandler;
2765
+ this.tokenIssuer = options.tokenIssuer;
2766
+ this.catalogIdentityClient = options.catalogIdentityClient;
2767
+ this.logger = options.logger;
2768
+ this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
2769
+ done(void 0, { fullProfile });
2723
2770
  });
2724
2771
  }
2725
- async listKeys() {
2726
- const rows = await this.database(TABLE).select();
2727
- return {
2728
- items: rows.map((row) => ({
2729
- key: JSON.parse(row.key),
2730
- createdAt: parseDate(row.created_at)
2731
- }))
2732
- };
2772
+ async start(req, res) {
2773
+ const { url } = await executeRedirectStrategy(req, this.strategy, {});
2774
+ res.redirect(url);
2775
+ }
2776
+ async frameHandler(req, res) {
2777
+ try {
2778
+ const context = {
2779
+ logger: this.logger,
2780
+ catalogIdentityClient: this.catalogIdentityClient,
2781
+ tokenIssuer: this.tokenIssuer
2782
+ };
2783
+ const { result } = await executeFrameHandlerStrategy(req, this.strategy);
2784
+ const { profile } = await this.authHandler(result, context);
2785
+ const response = {
2786
+ profile,
2787
+ providerInfo: {}
2788
+ };
2789
+ if (this.signInResolver) {
2790
+ const signInResponse = await this.signInResolver({
2791
+ result,
2792
+ profile
2793
+ }, context);
2794
+ response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
2795
+ }
2796
+ return postMessageResponse(res, this.appUrl, {
2797
+ type: "authorization_response",
2798
+ response
2799
+ });
2800
+ } catch (error) {
2801
+ const { name, message } = errors.isError(error) ? error : new Error("Encountered invalid error");
2802
+ return postMessageResponse(res, this.appUrl, {
2803
+ type: "authorization_response",
2804
+ error: { name, message }
2805
+ });
2806
+ }
2733
2807
  }
2734
- async removeKeys(kids) {
2735
- await this.database(TABLE).delete().whereIn("kid", kids);
2808
+ async logout(_req, res) {
2809
+ res.end();
2736
2810
  }
2737
2811
  }
2738
-
2739
- class MemoryKeyStore {
2740
- constructor() {
2741
- this.keys = /* @__PURE__ */ new Map();
2742
- }
2743
- async addKey(key) {
2744
- this.keys.set(key.kid, {
2745
- createdAt: luxon.DateTime.utc().toJSDate(),
2746
- key: JSON.stringify(key)
2812
+ const samlDefaultSignInResolver = async (info, ctx) => {
2813
+ const id = info.result.fullProfile.nameID;
2814
+ const token = await ctx.tokenIssuer.issueToken({
2815
+ claims: { sub: id }
2816
+ });
2817
+ return { id, token };
2818
+ };
2819
+ const createSamlProvider = (options) => {
2820
+ return ({
2821
+ providerId,
2822
+ globalConfig,
2823
+ config,
2824
+ tokenIssuer,
2825
+ tokenManager,
2826
+ catalogApi,
2827
+ logger
2828
+ }) => {
2829
+ var _a, _b;
2830
+ const catalogIdentityClient = new CatalogIdentityClient({
2831
+ catalogApi,
2832
+ tokenManager
2747
2833
  });
2748
- }
2749
- async removeKeys(kids) {
2750
- for (const kid of kids) {
2751
- this.keys.delete(kid);
2834
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2835
+ profile: {
2836
+ email: fullProfile.email,
2837
+ displayName: fullProfile.displayName
2838
+ }
2839
+ });
2840
+ const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
2841
+ const signInResolver = (info) => signInResolverFn(info, {
2842
+ catalogIdentityClient,
2843
+ tokenIssuer,
2844
+ logger
2845
+ });
2846
+ return new SamlAuthProvider({
2847
+ callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2848
+ entryPoint: config.getString("entryPoint"),
2849
+ logoutUrl: config.getOptionalString("logoutUrl"),
2850
+ audience: config.getOptionalString("audience"),
2851
+ issuer: config.getString("issuer"),
2852
+ cert: config.getString("cert"),
2853
+ privateKey: config.getOptionalString("privateKey"),
2854
+ authnContext: config.getOptionalStringArray("authnContext"),
2855
+ identifierFormat: config.getOptionalString("identifierFormat"),
2856
+ decryptionPvk: config.getOptionalString("decryptionPvk"),
2857
+ signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2858
+ digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2859
+ acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2860
+ tokenIssuer,
2861
+ appUrl: globalConfig.appUrl,
2862
+ authHandler,
2863
+ signInResolver,
2864
+ logger,
2865
+ catalogIdentityClient
2866
+ });
2867
+ };
2868
+ };
2869
+
2870
+ const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
2871
+
2872
+ function createTokenValidator(audience, mockClient) {
2873
+ const client = mockClient != null ? mockClient : new googleAuthLibrary.OAuth2Client();
2874
+ return async function tokenValidator(token) {
2875
+ const response = await client.getIapPublicKeys();
2876
+ const ticket = await client.verifySignedJwtWithCertsAsync(token, response.pubkeys, audience, ["https://cloud.google.com/iap"]);
2877
+ const payload = ticket.getPayload();
2878
+ if (!payload) {
2879
+ throw new TypeError("Token had no payload");
2752
2880
  }
2881
+ return payload;
2882
+ };
2883
+ }
2884
+ async function parseRequestToken(jwtToken, tokenValidator) {
2885
+ if (typeof jwtToken !== "string" || !jwtToken) {
2886
+ throw new errors.AuthenticationError(`Missing Google IAP header: ${IAP_JWT_HEADER}`);
2753
2887
  }
2754
- async listKeys() {
2755
- return {
2756
- items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
2757
- createdAt,
2758
- key: JSON.parse(keyStr)
2759
- }))
2760
- };
2888
+ let payload;
2889
+ try {
2890
+ payload = await tokenValidator(jwtToken);
2891
+ } catch (e) {
2892
+ throw new errors.AuthenticationError(`Google IAP token verification failed, ${e}`);
2893
+ }
2894
+ if (!payload.sub || !payload.email) {
2895
+ throw new errors.AuthenticationError("Google IAP token payload is missing sub and/or email claim");
2761
2896
  }
2897
+ return {
2898
+ iapToken: {
2899
+ ...payload,
2900
+ sub: payload.sub,
2901
+ email: payload.email
2902
+ }
2903
+ };
2762
2904
  }
2905
+ const defaultAuthHandler = async ({
2906
+ iapToken
2907
+ }) => ({ profile: { email: iapToken.email } });
2763
2908
 
2764
- const DEFAULT_TIMEOUT_MS = 1e4;
2765
- const DEFAULT_DOCUMENT_PATH = "sessions";
2766
- class FirestoreKeyStore {
2767
- constructor(database, path, timeout) {
2768
- this.database = database;
2769
- this.path = path;
2770
- this.timeout = timeout;
2771
- }
2772
- static async create(settings) {
2773
- const { path, timeout, ...firestoreSettings } = settings != null ? settings : {};
2774
- const database = new firestore.Firestore(firestoreSettings);
2775
- return new FirestoreKeyStore(database, path != null ? path : DEFAULT_DOCUMENT_PATH, timeout != null ? timeout : DEFAULT_TIMEOUT_MS);
2909
+ class GcpIapProvider {
2910
+ constructor(options) {
2911
+ this.authHandler = options.authHandler;
2912
+ this.signInResolver = options.signInResolver;
2913
+ this.tokenValidator = options.tokenValidator;
2914
+ this.tokenIssuer = options.tokenIssuer;
2915
+ this.catalogIdentityClient = options.catalogIdentityClient;
2916
+ this.logger = options.logger;
2776
2917
  }
2777
- static async verifyConnection(keyStore, logger) {
2778
- try {
2779
- await keyStore.verify();
2780
- } catch (error) {
2781
- if (process.env.NODE_ENV !== "development") {
2782
- throw new Error(`Failed to connect to database: ${error.message}`);
2783
- }
2784
- logger == null ? void 0 : logger.warn(`Failed to connect to database: ${error.message}`);
2785
- }
2918
+ async start() {
2786
2919
  }
2787
- async addKey(key) {
2788
- await this.withTimeout(this.database.collection(this.path).doc(key.kid).set({
2789
- kid: key.kid,
2790
- key: JSON.stringify(key)
2791
- }));
2920
+ async frameHandler() {
2792
2921
  }
2793
- async listKeys() {
2794
- const keys = await this.withTimeout(this.database.collection(this.path).get());
2795
- return {
2796
- items: keys.docs.map((key) => ({
2797
- key: key.data(),
2798
- createdAt: key.createTime.toDate()
2799
- }))
2922
+ async refresh(req, res) {
2923
+ const result = await parseRequestToken(req.header(IAP_JWT_HEADER), this.tokenValidator);
2924
+ const context = {
2925
+ logger: this.logger,
2926
+ catalogIdentityClient: this.catalogIdentityClient,
2927
+ tokenIssuer: this.tokenIssuer
2800
2928
  };
2801
- }
2802
- async removeKeys(kids) {
2803
- for (const kid of kids) {
2804
- await this.withTimeout(this.database.collection(this.path).doc(kid).delete());
2805
- }
2806
- }
2807
- async withTimeout(operation) {
2808
- const timer = new Promise((_, reject) => setTimeout(() => {
2809
- reject(new Error(`Operation timed out after ${this.timeout}ms`));
2810
- }, this.timeout));
2811
- return Promise.race([operation, timer]);
2812
- }
2813
- async verify() {
2814
- await this.withTimeout(this.database.collection(this.path).limit(1).get());
2929
+ const { profile } = await this.authHandler(result, context);
2930
+ const backstageIdentity = await this.signInResolver({ profile, result }, context);
2931
+ const response = {
2932
+ providerInfo: { iapToken: result.iapToken },
2933
+ profile,
2934
+ backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity)
2935
+ };
2936
+ res.json(response);
2815
2937
  }
2816
2938
  }
2817
-
2818
- class KeyStores {
2819
- static async fromConfig(config, options) {
2939
+ function createGcpIapProvider(options) {
2940
+ return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
2820
2941
  var _a;
2821
- const { logger, database } = options != null ? options : {};
2822
- const ks = config.getOptionalConfig("auth.keyStore");
2823
- const provider = (_a = ks == null ? void 0 : ks.getOptionalString("provider")) != null ? _a : "database";
2824
- logger == null ? void 0 : logger.info(`Configuring "${provider}" as KeyStore provider`);
2825
- if (provider === "database") {
2826
- if (!database) {
2827
- throw new Error("This KeyStore provider requires a database");
2828
- }
2829
- return await DatabaseKeyStore.create({
2830
- database: await database.getClient()
2831
- });
2832
- }
2833
- if (provider === "memory") {
2834
- return new MemoryKeyStore();
2835
- }
2836
- if (provider === "firestore") {
2837
- const settings = ks == null ? void 0 : ks.getConfig(provider);
2838
- const keyStore = await FirestoreKeyStore.create(lodash.pickBy({
2839
- projectId: settings == null ? void 0 : settings.getOptionalString("projectId"),
2840
- keyFilename: settings == null ? void 0 : settings.getOptionalString("keyFilename"),
2841
- host: settings == null ? void 0 : settings.getOptionalString("host"),
2842
- port: settings == null ? void 0 : settings.getOptionalNumber("port"),
2843
- ssl: settings == null ? void 0 : settings.getOptionalBoolean("ssl"),
2844
- path: settings == null ? void 0 : settings.getOptionalString("path"),
2845
- timeout: settings == null ? void 0 : settings.getOptionalNumber("timeout")
2846
- }, (value) => value !== void 0));
2847
- await FirestoreKeyStore.verifyConnection(keyStore, logger);
2848
- return keyStore;
2849
- }
2850
- throw new Error(`Unknown KeyStore provider: ${provider}`);
2851
- }
2942
+ const audience = config.getString("audience");
2943
+ const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
2944
+ const signInResolver = options.signIn.resolver;
2945
+ const tokenValidator = createTokenValidator(audience);
2946
+ const catalogIdentityClient = new CatalogIdentityClient({
2947
+ catalogApi,
2948
+ tokenManager
2949
+ });
2950
+ return new GcpIapProvider({
2951
+ authHandler,
2952
+ signInResolver,
2953
+ tokenValidator,
2954
+ tokenIssuer,
2955
+ catalogIdentityClient,
2956
+ logger
2957
+ });
2958
+ };
2852
2959
  }
2853
2960
 
2961
+ const factories = {
2962
+ google: createGoogleProvider(),
2963
+ github: createGithubProvider(),
2964
+ gitlab: createGitlabProvider(),
2965
+ saml: createSamlProvider(),
2966
+ okta: createOktaProvider(),
2967
+ auth0: createAuth0Provider(),
2968
+ microsoft: createMicrosoftProvider(),
2969
+ oauth2: createOAuth2Provider(),
2970
+ oidc: createOidcProvider(),
2971
+ onelogin: createOneLoginProvider(),
2972
+ awsalb: createAwsAlbProvider(),
2973
+ bitbucket: createBitbucketProvider(),
2974
+ atlassian: createAtlassianProvider()
2975
+ };
2976
+
2854
2977
  async function createRouter(options) {
2855
- const { logger, config, discovery, database, providerFactories } = options;
2978
+ const {
2979
+ logger,
2980
+ config,
2981
+ discovery,
2982
+ database,
2983
+ tokenManager,
2984
+ providerFactories
2985
+ } = options;
2856
2986
  const router = Router__default["default"]();
2857
2987
  const appUrl = config.getString("app.baseUrl");
2858
2988
  const authUrl = await discovery.getExternalBaseUrl("auth");
@@ -2898,6 +3028,7 @@ async function createRouter(options) {
2898
3028
  globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },
2899
3029
  config: providersConfig.getConfig(providerId),
2900
3030
  logger,
3031
+ tokenManager,
2901
3032
  tokenIssuer,
2902
3033
  discovery,
2903
3034
  catalogApi
@@ -2969,6 +3100,7 @@ exports.createGitlabProvider = createGitlabProvider;
2969
3100
  exports.createGoogleProvider = createGoogleProvider;
2970
3101
  exports.createMicrosoftProvider = createMicrosoftProvider;
2971
3102
  exports.createOAuth2Provider = createOAuth2Provider;
3103
+ exports.createOauth2ProxyProvider = createOauth2ProxyProvider;
2972
3104
  exports.createOidcProvider = createOidcProvider;
2973
3105
  exports.createOktaProvider = createOktaProvider;
2974
3106
  exports.createOneLoginProvider = createOneLoginProvider;