@backstage/plugin-auth-backend 0.13.0-next.1 → 0.13.1-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
@@ -11,7 +11,6 @@ var pickBy = require('lodash/pickBy');
11
11
  var crypto = require('crypto');
12
12
  var url = require('url');
13
13
  var jwtDecoder = require('jwt-decode');
14
- var catalogModel = require('@backstage/catalog-model');
15
14
  var fetch = require('node-fetch');
16
15
  var NodeCache = require('node-cache');
17
16
  var jose = require('jose');
@@ -29,6 +28,7 @@ var googleAuthLibrary = require('google-auth-library');
29
28
  var catalogClient = require('@backstage/catalog-client');
30
29
  var uuid = require('uuid');
31
30
  var luxon = require('luxon');
31
+ var catalogModel = require('@backstage/catalog-model');
32
32
  var backendCommon = require('@backstage/backend-common');
33
33
  var firestore = require('@google-cloud/firestore');
34
34
  var lodash = require('lodash');
@@ -542,75 +542,12 @@ const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) =>
542
542
  });
543
543
  };
544
544
 
545
- class CatalogIdentityClient {
546
- constructor(options) {
547
- this.catalogApi = options.catalogApi;
548
- this.tokenManager = options.tokenManager;
549
- }
550
- async findUser(query) {
551
- const filter = {
552
- kind: "user"
553
- };
554
- for (const [key, value] of Object.entries(query.annotations)) {
555
- filter[`metadata.annotations.${key}`] = value;
556
- }
557
- const { token } = await this.tokenManager.getToken();
558
- const { items } = await this.catalogApi.getEntities({ filter }, { token });
559
- if (items.length !== 1) {
560
- if (items.length > 1) {
561
- throw new errors.ConflictError("User lookup resulted in multiple matches");
562
- } else {
563
- throw new errors.NotFoundError("User not found");
564
- }
565
- }
566
- return items[0];
567
- }
568
- async resolveCatalogMembership(query) {
569
- const { entityRefs, logger } = query;
570
- const resolvedEntityRefs = entityRefs.map((ref) => {
571
- try {
572
- const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
573
- defaultKind: "user",
574
- defaultNamespace: "default"
575
- });
576
- return parsedRef;
577
- } catch {
578
- logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
579
- return null;
580
- }
581
- }).filter((ref) => ref !== null);
582
- const filter = resolvedEntityRefs.map((ref) => ({
583
- kind: ref.kind,
584
- "metadata.namespace": ref.namespace,
585
- "metadata.name": ref.name
586
- }));
587
- const { token } = await this.tokenManager.getToken();
588
- const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
589
- if (entityRefs.length !== entities.length) {
590
- const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
591
- const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
592
- logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
593
- }
594
- const memberOf = entities.flatMap((e) => {
595
- var _a, _b;
596
- return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.targetRef)) != null ? _b : [];
597
- });
598
- const newEntityRefs = [
599
- ...new Set(resolvedEntityRefs.map(catalogModel.stringifyEntityRef).concat(memberOf))
600
- ];
601
- logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
602
- return newEntityRefs;
603
- }
604
- }
605
-
606
- function getEntityClaims(entity) {
607
- var _a, _b;
608
- const userRef = catalogModel.stringifyEntityRef(entity);
609
- const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF && r.targetRef.startsWith("group:")).map((r) => r.targetRef)) != null ? _b : [];
610
- return {
611
- sub: userRef,
612
- ent: [userRef, ...membershipRefs]
613
- };
545
+ function createAuthProviderIntegration(config) {
546
+ var _a;
547
+ return Object.freeze({
548
+ ...config,
549
+ resolvers: Object.freeze((_a = config.resolvers) != null ? _a : {})
550
+ });
614
551
  }
615
552
 
616
553
  const atlassianDefaultAuthHandler = async ({
@@ -621,9 +558,7 @@ const atlassianDefaultAuthHandler = async ({
621
558
  });
622
559
  class AtlassianAuthProvider {
623
560
  constructor(options) {
624
- this.catalogIdentityClient = options.catalogIdentityClient;
625
- this.logger = options.logger;
626
- this.tokenIssuer = options.tokenIssuer;
561
+ this.resolverContext = options.resolverContext;
627
562
  this.authHandler = options.authHandler;
628
563
  this.signInResolver = options.signInResolver;
629
564
  this._strategy = new AtlassianStrategy({
@@ -653,12 +588,7 @@ class AtlassianAuthProvider {
653
588
  };
654
589
  }
655
590
  async handleResult(result) {
656
- const context = {
657
- logger: this.logger,
658
- catalogIdentityClient: this.catalogIdentityClient,
659
- tokenIssuer: this.tokenIssuer
660
- };
661
- const { profile } = await this.authHandler(result, context);
591
+ const { profile } = await this.authHandler(result, this.resolverContext);
662
592
  const response = {
663
593
  providerInfo: {
664
594
  idToken: result.params.id_token,
@@ -672,7 +602,7 @@ class AtlassianAuthProvider {
672
602
  response.backstageIdentity = await this.signInResolver({
673
603
  result,
674
604
  profile
675
- }, context);
605
+ }, this.resolverContext);
676
606
  }
677
607
  return response;
678
608
  }
@@ -689,45 +619,33 @@ class AtlassianAuthProvider {
689
619
  };
690
620
  }
691
621
  }
692
- const createAtlassianProvider = (options) => {
693
- return ({
694
- providerId,
695
- globalConfig,
696
- config,
697
- tokenIssuer,
698
- tokenManager,
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 customCallbackUrl = envConfig.getOptionalString("callbackUrl");
707
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
708
- const catalogIdentityClient = new CatalogIdentityClient({
709
- catalogApi,
710
- tokenManager
711
- });
712
- const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
713
- const provider = new AtlassianAuthProvider({
714
- clientId,
715
- clientSecret,
716
- scopes,
717
- callbackUrl,
718
- authHandler,
719
- signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
720
- catalogIdentityClient,
721
- logger,
722
- tokenIssuer
723
- });
724
- return OAuthAdapter.fromConfig(globalConfig, provider, {
725
- providerId,
726
- tokenIssuer,
727
- callbackUrl
622
+ const atlassian = createAuthProviderIntegration({
623
+ create(options) {
624
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
625
+ var _a, _b;
626
+ const clientId = envConfig.getString("clientId");
627
+ const clientSecret = envConfig.getString("clientSecret");
628
+ const scopes = envConfig.getString("scopes");
629
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
630
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
631
+ const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : atlassianDefaultAuthHandler;
632
+ const provider = new AtlassianAuthProvider({
633
+ clientId,
634
+ clientSecret,
635
+ scopes,
636
+ callbackUrl,
637
+ authHandler,
638
+ signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
639
+ resolverContext
640
+ });
641
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
642
+ providerId,
643
+ callbackUrl
644
+ });
728
645
  });
729
- });
730
- };
646
+ }
647
+ });
648
+ const createAtlassianProvider = atlassian.create;
731
649
 
732
650
  class Auth0Strategy extends OAuth2Strategy__default["default"] {
733
651
  constructor(options, verify) {
@@ -746,9 +664,7 @@ class Auth0AuthProvider {
746
664
  constructor(options) {
747
665
  this.signInResolver = options.signInResolver;
748
666
  this.authHandler = options.authHandler;
749
- this.tokenIssuer = options.tokenIssuer;
750
- this.catalogIdentityClient = options.catalogIdentityClient;
751
- this.logger = options.logger;
667
+ this.resolverContext = options.resolverContext;
752
668
  this._strategy = new Auth0Strategy({
753
669
  clientID: options.clientId,
754
670
  clientSecret: options.clientSecret,
@@ -794,12 +710,7 @@ class Auth0AuthProvider {
794
710
  };
795
711
  }
796
712
  async handleResult(result) {
797
- const context = {
798
- logger: this.logger,
799
- catalogIdentityClient: this.catalogIdentityClient,
800
- tokenIssuer: this.tokenIssuer
801
- };
802
- const { profile } = await this.authHandler(result, context);
713
+ const { profile } = await this.authHandler(result, this.resolverContext);
803
714
  const response = {
804
715
  providerInfo: {
805
716
  idToken: result.params.id_token,
@@ -813,78 +724,52 @@ class Auth0AuthProvider {
813
724
  response.backstageIdentity = await this.signInResolver({
814
725
  result,
815
726
  profile
816
- }, context);
727
+ }, this.resolverContext);
817
728
  }
818
729
  return response;
819
730
  }
820
731
  }
821
- const defaultSignInResolver$1 = async (info) => {
822
- const { profile } = info;
823
- if (!profile.email) {
824
- throw new Error("Profile does not contain an email");
825
- }
826
- const id = profile.email.split("@")[0];
827
- return { id, token: "" };
828
- };
829
- const createAuth0Provider = (options) => {
830
- return ({
831
- providerId,
832
- globalConfig,
833
- config,
834
- tokenIssuer,
835
- tokenManager,
836
- catalogApi,
837
- logger
838
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
839
- var _a, _b;
840
- const clientId = envConfig.getString("clientId");
841
- const clientSecret = envConfig.getString("clientSecret");
842
- const domain = envConfig.getString("domain");
843
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
844
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
845
- const catalogIdentityClient = new CatalogIdentityClient({
846
- catalogApi,
847
- tokenManager
848
- });
849
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
850
- profile: makeProfileInfo(fullProfile, params.id_token)
851
- });
852
- const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver$1;
853
- const provider = new Auth0AuthProvider({
854
- clientId,
855
- clientSecret,
856
- callbackUrl,
857
- domain,
858
- authHandler,
859
- signInResolver,
860
- tokenIssuer,
861
- catalogIdentityClient,
862
- logger
863
- });
864
- return OAuthAdapter.fromConfig(globalConfig, provider, {
865
- disableRefresh: true,
866
- providerId,
867
- tokenIssuer,
868
- callbackUrl
732
+ const auth0 = createAuthProviderIntegration({
733
+ create(options) {
734
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
735
+ var _a;
736
+ const clientId = envConfig.getString("clientId");
737
+ const clientSecret = envConfig.getString("clientSecret");
738
+ const domain = envConfig.getString("domain");
739
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
740
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
741
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
742
+ profile: makeProfileInfo(fullProfile, params.id_token)
743
+ });
744
+ const signInResolver = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver;
745
+ const provider = new Auth0AuthProvider({
746
+ clientId,
747
+ clientSecret,
748
+ callbackUrl,
749
+ domain,
750
+ authHandler,
751
+ signInResolver,
752
+ resolverContext
753
+ });
754
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
755
+ disableRefresh: true,
756
+ providerId,
757
+ callbackUrl
758
+ });
869
759
  });
870
- });
871
- };
760
+ }
761
+ });
762
+ const createAuth0Provider = auth0.create;
872
763
 
873
764
  const ALB_JWT_HEADER = "x-amzn-oidc-data";
874
765
  const ALB_ACCESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken";
875
- const getJWTHeaders = (input) => {
876
- const encoded = input.split(".")[0];
877
- return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
878
- };
879
766
  class AwsAlbAuthProvider {
880
767
  constructor(options) {
881
768
  this.region = options.region;
882
769
  this.issuer = options.issuer;
883
770
  this.authHandler = options.authHandler;
884
771
  this.signInResolver = options.signInResolver;
885
- this.tokenIssuer = options.tokenIssuer;
886
- this.catalogIdentityClient = options.catalogIdentityClient;
887
- this.logger = options.logger;
772
+ this.resolverContext = options.resolverContext;
888
773
  this.keyCache = new NodeCache__default["default"]({ stdTTL: 3600 });
889
774
  }
890
775
  frameHandler() {
@@ -896,9 +781,7 @@ class AwsAlbAuthProvider {
896
781
  const response = await this.handleResult(result);
897
782
  res.json(response);
898
783
  } catch (e) {
899
- this.logger.error("Exception occurred during AWS ALB token refresh", e);
900
- res.status(401);
901
- res.end();
784
+ throw new errors.AuthenticationError("Exception occurred during AWS ALB token refresh", e);
902
785
  }
903
786
  }
904
787
  start() {
@@ -914,9 +797,8 @@ class AwsAlbAuthProvider {
914
797
  throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESS_TOKEN_HEADER}`);
915
798
  }
916
799
  try {
917
- const headers = getJWTHeaders(jwt);
918
- const key = await this.getKey(headers.kid);
919
- const claims = jose.JWT.verify(jwt, key);
800
+ const verifyResult = await jose.jwtVerify(jwt, this.getKey);
801
+ const claims = verifyResult.payload;
920
802
  if (this.issuer && claims.iss !== this.issuer) {
921
803
  throw new errors.AuthenticationError("Issuer mismatch on JWT token");
922
804
  }
@@ -942,16 +824,11 @@ class AwsAlbAuthProvider {
942
824
  }
943
825
  }
944
826
  async handleResult(result) {
945
- const context = {
946
- tokenIssuer: this.tokenIssuer,
947
- catalogIdentityClient: this.catalogIdentityClient,
948
- logger: this.logger
949
- };
950
- const { profile } = await this.authHandler(result, context);
827
+ const { profile } = await this.authHandler(result, this.resolverContext);
951
828
  const backstageIdentity = await this.signInResolver({
952
829
  result,
953
830
  profile
954
- }, context);
831
+ }, this.resolverContext);
955
832
  return {
956
833
  providerInfo: {
957
834
  accessToken: result.accessToken,
@@ -961,51 +838,48 @@ class AwsAlbAuthProvider {
961
838
  profile
962
839
  };
963
840
  }
964
- async getKey(keyId) {
965
- const optionalCacheKey = this.keyCache.get(keyId);
841
+ async getKey(header) {
842
+ if (!header.kid) {
843
+ throw new errors.AuthenticationError("No key id was specified in header");
844
+ }
845
+ const optionalCacheKey = this.keyCache.get(header.kid);
966
846
  if (optionalCacheKey) {
967
847
  return crypto__namespace.createPublicKey(optionalCacheKey);
968
848
  }
969
- const keyText = await fetch__default["default"](`https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`).then((response) => response.text());
849
+ const keyText = await fetch__default["default"](`https://public-keys.auth.elb.${encodeURIComponent(this.region)}.amazonaws.com/${encodeURIComponent(header.kid)}`).then((response) => response.text());
970
850
  const keyValue = crypto__namespace.createPublicKey(keyText);
971
- this.keyCache.set(keyId, keyValue.export({ format: "pem", type: "spki" }));
851
+ this.keyCache.set(header.kid, keyValue.export({ format: "pem", type: "spki" }));
972
852
  return keyValue;
973
853
  }
974
854
  }
975
- const createAwsAlbProvider = (options) => {
976
- return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
977
- const region = config.getString("region");
978
- const issuer = config.getOptionalString("iss");
979
- if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
980
- throw new Error("SignInResolver is required to use this authentication provider");
981
- }
982
- const catalogIdentityClient = new CatalogIdentityClient({
983
- catalogApi,
984
- tokenManager
985
- });
986
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
987
- profile: makeProfileInfo(fullProfile)
988
- });
989
- const signInResolver = options == null ? void 0 : options.signIn.resolver;
990
- return new AwsAlbAuthProvider({
991
- region,
992
- issuer,
993
- signInResolver,
994
- authHandler,
995
- tokenIssuer,
996
- catalogIdentityClient,
997
- logger
998
- });
999
- };
1000
- };
855
+ const awsAlb = createAuthProviderIntegration({
856
+ create(options) {
857
+ return ({ config, resolverContext }) => {
858
+ const region = config.getString("region");
859
+ const issuer = config.getOptionalString("iss");
860
+ if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
861
+ throw new Error("SignInResolver is required to use this authentication provider");
862
+ }
863
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
864
+ profile: makeProfileInfo(fullProfile)
865
+ });
866
+ return new AwsAlbAuthProvider({
867
+ region,
868
+ issuer,
869
+ signInResolver: options == null ? void 0 : options.signIn.resolver,
870
+ authHandler,
871
+ resolverContext
872
+ });
873
+ };
874
+ }
875
+ });
876
+ const createAwsAlbProvider = awsAlb.create;
1001
877
 
1002
878
  class BitbucketAuthProvider {
1003
879
  constructor(options) {
1004
880
  this.signInResolver = options.signInResolver;
1005
881
  this.authHandler = options.authHandler;
1006
- this.tokenIssuer = options.tokenIssuer;
1007
- this.catalogIdentityClient = options.catalogIdentityClient;
1008
- this.logger = options.logger;
882
+ this.resolverContext = options.resolverContext;
1009
883
  this._strategy = new passportBitbucketOauth2.Strategy({
1010
884
  clientID: options.clientId,
1011
885
  clientSecret: options.clientSecret,
@@ -1051,12 +925,7 @@ class BitbucketAuthProvider {
1051
925
  }
1052
926
  async handleResult(result) {
1053
927
  result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
1054
- const context = {
1055
- logger: this.logger,
1056
- catalogIdentityClient: this.catalogIdentityClient,
1057
- tokenIssuer: this.tokenIssuer
1058
- };
1059
- const { profile } = await this.authHandler(result, context);
928
+ const { profile } = await this.authHandler(result, this.resolverContext);
1060
929
  const response = {
1061
930
  providerInfo: {
1062
931
  idToken: result.params.id_token,
@@ -1070,79 +939,69 @@ class BitbucketAuthProvider {
1070
939
  response.backstageIdentity = await this.signInResolver({
1071
940
  result,
1072
941
  profile
1073
- }, context);
942
+ }, this.resolverContext);
1074
943
  }
1075
944
  return response;
1076
945
  }
1077
946
  }
1078
- const bitbucketUsernameSignInResolver = async (info, ctx) => {
1079
- const { result } = info;
1080
- if (!result.fullProfile.username) {
1081
- throw new Error("Bitbucket profile contained no Username");
1082
- }
1083
- const entity = await ctx.catalogIdentityClient.findUser({
1084
- annotations: {
1085
- "bitbucket.org/username": result.fullProfile.username
947
+ const bitbucket = createAuthProviderIntegration({
948
+ create(options) {
949
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
950
+ var _a;
951
+ const clientId = envConfig.getString("clientId");
952
+ const clientSecret = envConfig.getString("clientSecret");
953
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
954
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
955
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
956
+ profile: makeProfileInfo(fullProfile, params.id_token)
957
+ });
958
+ const provider = new BitbucketAuthProvider({
959
+ clientId,
960
+ clientSecret,
961
+ callbackUrl,
962
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
963
+ authHandler,
964
+ resolverContext
965
+ });
966
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
967
+ disableRefresh: false,
968
+ providerId,
969
+ callbackUrl
970
+ });
971
+ });
972
+ },
973
+ resolvers: {
974
+ usernameMatchingUserEntityAnnotation() {
975
+ return async (info, ctx) => {
976
+ const { result } = info;
977
+ if (!result.fullProfile.username) {
978
+ throw new Error("Bitbucket profile contained no Username");
979
+ }
980
+ return ctx.signInWithCatalogUser({
981
+ annotations: {
982
+ "bitbucket.org/username": result.fullProfile.username
983
+ }
984
+ });
985
+ };
986
+ },
987
+ userIdMatchingUserEntityAnnotation() {
988
+ return async (info, ctx) => {
989
+ const { result } = info;
990
+ if (!result.fullProfile.id) {
991
+ throw new Error("Bitbucket profile contained no User ID");
992
+ }
993
+ return ctx.signInWithCatalogUser({
994
+ annotations: {
995
+ "bitbucket.org/user-id": result.fullProfile.id
996
+ }
997
+ });
998
+ };
1086
999
  }
1087
- });
1088
- const claims = getEntityClaims(entity);
1089
- const token = await ctx.tokenIssuer.issueToken({ claims });
1090
- return { id: entity.metadata.name, entity, token };
1091
- };
1092
- const bitbucketUserIdSignInResolver = async (info, ctx) => {
1093
- const { result } = info;
1094
- if (!result.fullProfile.id) {
1095
- throw new Error("Bitbucket profile contained no User ID");
1096
1000
  }
1097
- const entity = await ctx.catalogIdentityClient.findUser({
1098
- annotations: {
1099
- "bitbucket.org/user-id": result.fullProfile.id
1100
- }
1101
- });
1102
- const claims = getEntityClaims(entity);
1103
- const token = await ctx.tokenIssuer.issueToken({ claims });
1104
- return { id: entity.metadata.name, entity, token };
1105
- };
1106
- const createBitbucketProvider = (options) => {
1107
- return ({
1108
- providerId,
1109
- globalConfig,
1110
- config,
1111
- tokenIssuer,
1112
- tokenManager,
1113
- catalogApi,
1114
- logger
1115
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1116
- var _a;
1117
- const clientId = envConfig.getString("clientId");
1118
- const clientSecret = envConfig.getString("clientSecret");
1119
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1120
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1121
- const catalogIdentityClient = new CatalogIdentityClient({
1122
- catalogApi,
1123
- tokenManager
1124
- });
1125
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1126
- profile: makeProfileInfo(fullProfile, params.id_token)
1127
- });
1128
- const provider = new BitbucketAuthProvider({
1129
- clientId,
1130
- clientSecret,
1131
- callbackUrl,
1132
- signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
1133
- authHandler,
1134
- tokenIssuer,
1135
- catalogIdentityClient,
1136
- logger
1137
- });
1138
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1139
- disableRefresh: false,
1140
- providerId,
1141
- tokenIssuer,
1142
- callbackUrl
1143
- });
1144
- });
1145
- };
1001
+ });
1002
+ const createBitbucketProvider = bitbucket.create;
1003
+ const bitbucketUsernameSignInResolver = bitbucket.resolvers.usernameMatchingUserEntityAnnotation();
1004
+ const bitbucketUserIdSignInResolver = bitbucket.resolvers.userIdMatchingUserEntityAnnotation();
1146
1005
 
1147
1006
  const ACCESS_TOKEN_PREFIX = "access-token.";
1148
1007
  const BACKSTAGE_SESSION_EXPIRATION = 3600;
@@ -1151,9 +1010,7 @@ class GithubAuthProvider {
1151
1010
  this.signInResolver = options.signInResolver;
1152
1011
  this.authHandler = options.authHandler;
1153
1012
  this.stateEncoder = options.stateEncoder;
1154
- this.tokenIssuer = options.tokenIssuer;
1155
- this.catalogIdentityClient = options.catalogIdentityClient;
1156
- this.logger = options.logger;
1013
+ this.resolverContext = options.resolverContext;
1157
1014
  this._strategy = new passportGithub2.Strategy({
1158
1015
  clientID: options.clientId,
1159
1016
  clientSecret: options.clientSecret,
@@ -1213,12 +1070,7 @@ class GithubAuthProvider {
1213
1070
  };
1214
1071
  }
1215
1072
  async handleResult(result) {
1216
- const context = {
1217
- logger: this.logger,
1218
- catalogIdentityClient: this.catalogIdentityClient,
1219
- tokenIssuer: this.tokenIssuer
1220
- };
1221
- const { profile } = await this.authHandler(result, context);
1073
+ const { profile } = await this.authHandler(result, this.resolverContext);
1222
1074
  const expiresInStr = result.params.expires_in;
1223
1075
  let expiresInSeconds = expiresInStr === void 0 ? void 0 : Number(expiresInStr);
1224
1076
  let backstageIdentity = void 0;
@@ -1226,7 +1078,7 @@ class GithubAuthProvider {
1226
1078
  backstageIdentity = await this.signInResolver({
1227
1079
  result,
1228
1080
  profile
1229
- }, context);
1081
+ }, this.resolverContext);
1230
1082
  if (expiresInSeconds) {
1231
1083
  expiresInSeconds = Math.min(expiresInSeconds, BACKSTAGE_SESSION_EXPIRATION);
1232
1084
  } else {
@@ -1244,99 +1096,58 @@ class GithubAuthProvider {
1244
1096
  };
1245
1097
  }
1246
1098
  }
1247
- const githubDefaultSignInResolver = async (info, ctx) => {
1248
- const { fullProfile } = info.result;
1249
- const userId = fullProfile.username || fullProfile.id;
1250
- const entityRef = catalogModel.stringifyEntityRef({
1251
- kind: "User",
1252
- namespace: catalogModel.DEFAULT_NAMESPACE,
1253
- name: userId
1254
- });
1255
- const token = await ctx.tokenIssuer.issueToken({
1256
- claims: {
1257
- sub: entityRef,
1258
- ent: [entityRef]
1259
- }
1260
- });
1261
- return { id: userId, token };
1262
- };
1263
- const createGithubProvider = (options) => {
1264
- return ({
1265
- providerId,
1266
- globalConfig,
1267
- config,
1268
- tokenIssuer,
1269
- tokenManager,
1270
- catalogApi,
1271
- logger
1272
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1273
- var _a, _b, _c, _d;
1274
- const clientId = envConfig.getString("clientId");
1275
- const clientSecret = envConfig.getString("clientSecret");
1276
- const enterpriseInstanceUrl = (_a = envConfig.getOptionalString("enterpriseInstanceUrl")) == null ? void 0 : _a.replace(/\/$/, "");
1277
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1278
- const authorizationUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/authorize` : void 0;
1279
- const tokenUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/access_token` : void 0;
1280
- const userProfileUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/api/v3/user` : void 0;
1281
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1282
- const catalogIdentityClient = new CatalogIdentityClient({
1283
- catalogApi,
1284
- tokenManager
1285
- });
1286
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
1287
- profile: makeProfileInfo(fullProfile)
1288
- });
1289
- const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : githubDefaultSignInResolver;
1290
- const signInResolver = (info) => signInResolverFn(info, {
1291
- catalogIdentityClient,
1292
- tokenIssuer,
1293
- logger
1294
- });
1295
- const stateEncoder = (_d = options == null ? void 0 : options.stateEncoder) != null ? _d : async (req) => {
1296
- return { encodedState: encodeState(req.state) };
1297
- };
1298
- const provider = new GithubAuthProvider({
1299
- clientId,
1300
- clientSecret,
1301
- callbackUrl,
1302
- tokenUrl,
1303
- userProfileUrl,
1304
- authorizationUrl,
1305
- signInResolver,
1306
- authHandler,
1307
- tokenIssuer,
1308
- catalogIdentityClient,
1309
- stateEncoder,
1310
- logger
1311
- });
1312
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1313
- persistScopes: true,
1314
- providerId,
1315
- tokenIssuer,
1316
- callbackUrl
1099
+ const github = createAuthProviderIntegration({
1100
+ create(options) {
1101
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1102
+ var _a, _b, _c;
1103
+ const clientId = envConfig.getString("clientId");
1104
+ const clientSecret = envConfig.getString("clientSecret");
1105
+ const enterpriseInstanceUrl = (_a = envConfig.getOptionalString("enterpriseInstanceUrl")) == null ? void 0 : _a.replace(/\/$/, "");
1106
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1107
+ const authorizationUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/authorize` : void 0;
1108
+ const tokenUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/login/oauth/access_token` : void 0;
1109
+ const userProfileUrl = enterpriseInstanceUrl ? `${enterpriseInstanceUrl}/api/v3/user` : void 0;
1110
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1111
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
1112
+ profile: makeProfileInfo(fullProfile)
1113
+ });
1114
+ const stateEncoder = (_b = options == null ? void 0 : options.stateEncoder) != null ? _b : async (req) => {
1115
+ return { encodedState: encodeState(req.state) };
1116
+ };
1117
+ const provider = new GithubAuthProvider({
1118
+ clientId,
1119
+ clientSecret,
1120
+ callbackUrl,
1121
+ tokenUrl,
1122
+ userProfileUrl,
1123
+ authorizationUrl,
1124
+ signInResolver: (_c = options == null ? void 0 : options.signIn) == null ? void 0 : _c.resolver,
1125
+ authHandler,
1126
+ stateEncoder,
1127
+ resolverContext
1128
+ });
1129
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1130
+ persistScopes: true,
1131
+ providerId,
1132
+ callbackUrl
1133
+ });
1317
1134
  });
1318
- });
1319
- };
1320
-
1321
- const gitlabDefaultSignInResolver = async (info, ctx) => {
1322
- const { profile, result } = info;
1323
- let id = result.fullProfile.id;
1324
- if (profile.email) {
1325
- id = profile.email.split("@")[0];
1326
- }
1327
- const entityRef = catalogModel.stringifyEntityRef({
1328
- kind: "User",
1329
- namespace: catalogModel.DEFAULT_NAMESPACE,
1330
- name: id
1331
- });
1332
- const token = await ctx.tokenIssuer.issueToken({
1333
- claims: {
1334
- sub: entityRef,
1335
- ent: [entityRef]
1135
+ },
1136
+ resolvers: {
1137
+ usernameMatchingUserEntityName: () => {
1138
+ return async (info, ctx) => {
1139
+ const { fullProfile } = info.result;
1140
+ const userId = fullProfile.username;
1141
+ if (!userId) {
1142
+ throw new Error(`GitHub user profile does not contain a username`);
1143
+ }
1144
+ return ctx.signInWithCatalogUser({ entityRef: { name: userId } });
1145
+ };
1336
1146
  }
1337
- });
1338
- return { id, token };
1339
- };
1147
+ }
1148
+ });
1149
+ const createGithubProvider = github.create;
1150
+
1340
1151
  const gitlabDefaultAuthHandler = async ({
1341
1152
  fullProfile,
1342
1153
  params
@@ -1345,9 +1156,7 @@ const gitlabDefaultAuthHandler = async ({
1345
1156
  });
1346
1157
  class GitlabAuthProvider {
1347
1158
  constructor(options) {
1348
- this.catalogIdentityClient = options.catalogIdentityClient;
1349
- this.logger = options.logger;
1350
- this.tokenIssuer = options.tokenIssuer;
1159
+ this.resolverContext = options.resolverContext;
1351
1160
  this.authHandler = options.authHandler;
1352
1161
  this.signInResolver = options.signInResolver;
1353
1162
  this._strategy = new passportGitlab2.Strategy({
@@ -1387,12 +1196,7 @@ class GitlabAuthProvider {
1387
1196
  };
1388
1197
  }
1389
1198
  async handleResult(result) {
1390
- const context = {
1391
- logger: this.logger,
1392
- catalogIdentityClient: this.catalogIdentityClient,
1393
- tokenIssuer: this.tokenIssuer
1394
- };
1395
- const { profile } = await this.authHandler(result, context);
1199
+ const { profile } = await this.authHandler(result, this.resolverContext);
1396
1200
  const response = {
1397
1201
  providerInfo: {
1398
1202
  idToken: result.params.id_token,
@@ -1406,67 +1210,69 @@ class GitlabAuthProvider {
1406
1210
  response.backstageIdentity = await this.signInResolver({
1407
1211
  result,
1408
1212
  profile
1409
- }, context);
1213
+ }, this.resolverContext);
1410
1214
  }
1411
1215
  return response;
1412
1216
  }
1413
1217
  }
1414
- const createGitlabProvider = (options) => {
1415
- return ({
1416
- providerId,
1417
- globalConfig,
1418
- config,
1419
- tokenIssuer,
1420
- tokenManager,
1421
- catalogApi,
1422
- logger
1423
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1424
- var _a, _b, _c;
1425
- const clientId = envConfig.getString("clientId");
1426
- const clientSecret = envConfig.getString("clientSecret");
1427
- const audience = envConfig.getOptionalString("audience");
1428
- const baseUrl = audience || "https://gitlab.com";
1429
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1430
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1431
- const catalogIdentityClient = new CatalogIdentityClient({
1432
- catalogApi,
1433
- tokenManager
1434
- });
1435
- const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
1436
- const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : gitlabDefaultSignInResolver;
1437
- const signInResolver = (info) => signInResolverFn(info, {
1438
- catalogIdentityClient,
1439
- tokenIssuer,
1440
- logger
1441
- });
1442
- const provider = new GitlabAuthProvider({
1443
- clientId,
1444
- clientSecret,
1445
- callbackUrl,
1446
- baseUrl,
1447
- authHandler,
1448
- signInResolver,
1449
- catalogIdentityClient,
1450
- logger,
1451
- tokenIssuer
1452
- });
1453
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1454
- disableRefresh: false,
1455
- providerId,
1456
- tokenIssuer,
1457
- callbackUrl
1218
+ const gitlab = createAuthProviderIntegration({
1219
+ create(options) {
1220
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1221
+ var _a, _b;
1222
+ const clientId = envConfig.getString("clientId");
1223
+ const clientSecret = envConfig.getString("clientSecret");
1224
+ const audience = envConfig.getOptionalString("audience");
1225
+ const baseUrl = audience || "https://gitlab.com";
1226
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1227
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1228
+ const authHandler = (_a = options == null ? void 0 : options.authHandler) != null ? _a : gitlabDefaultAuthHandler;
1229
+ const provider = new GitlabAuthProvider({
1230
+ clientId,
1231
+ clientSecret,
1232
+ callbackUrl,
1233
+ baseUrl,
1234
+ authHandler,
1235
+ signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
1236
+ resolverContext
1237
+ });
1238
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1239
+ disableRefresh: false,
1240
+ providerId,
1241
+ callbackUrl
1242
+ });
1458
1243
  });
1244
+ }
1245
+ });
1246
+ const createGitlabProvider = gitlab.create;
1247
+
1248
+ const commonByEmailLocalPartResolver = async (info, ctx) => {
1249
+ const { profile } = info;
1250
+ if (!profile.email) {
1251
+ throw new Error("Login failed, user profile does not contain an email");
1252
+ }
1253
+ const [localPart] = profile.email.split("@");
1254
+ return ctx.signInWithCatalogUser({
1255
+ entityRef: { name: localPart }
1256
+ });
1257
+ };
1258
+ const commonByEmailResolver = async (info, ctx) => {
1259
+ const { profile } = info;
1260
+ if (!profile.email) {
1261
+ throw new Error("Login failed, user profile does not contain an email");
1262
+ }
1263
+ return ctx.signInWithCatalogUser({
1264
+ filter: {
1265
+ "spec.profile.email": profile.email
1266
+ }
1459
1267
  });
1460
1268
  };
1461
1269
 
1462
1270
  class GoogleAuthProvider {
1463
1271
  constructor(options) {
1464
- this.signInResolver = options.signInResolver;
1465
1272
  this.authHandler = options.authHandler;
1466
- this.tokenIssuer = options.tokenIssuer;
1467
- this.catalogIdentityClient = options.catalogIdentityClient;
1468
- this.logger = options.logger;
1469
- this._strategy = new passportGoogleOauth20.Strategy({
1273
+ this.signInResolver = options.signInResolver;
1274
+ this.resolverContext = options.resolverContext;
1275
+ this.strategy = new passportGoogleOauth20.Strategy({
1470
1276
  clientID: options.clientId,
1471
1277
  clientSecret: options.clientSecret,
1472
1278
  callbackURL: options.callbackUrl,
@@ -1483,7 +1289,7 @@ class GoogleAuthProvider {
1483
1289
  });
1484
1290
  }
1485
1291
  async start(req) {
1486
- return await executeRedirectStrategy(req, this._strategy, {
1292
+ return await executeRedirectStrategy(req, this.strategy, {
1487
1293
  accessType: "offline",
1488
1294
  prompt: "consent",
1489
1295
  scope: req.scope,
@@ -1491,15 +1297,15 @@ class GoogleAuthProvider {
1491
1297
  });
1492
1298
  }
1493
1299
  async handler(req) {
1494
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
1300
+ const { result, privateInfo } = await executeFrameHandlerStrategy(req, this.strategy);
1495
1301
  return {
1496
1302
  response: await this.handleResult(result),
1497
1303
  refreshToken: privateInfo.refreshToken
1498
1304
  };
1499
1305
  }
1500
1306
  async refresh(req) {
1501
- const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
1502
- const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
1307
+ const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this.strategy, req.refreshToken, req.scope);
1308
+ const fullProfile = await executeFetchUserProfileStrategy(this.strategy, accessToken);
1503
1309
  return {
1504
1310
  response: await this.handleResult({
1505
1311
  fullProfile,
@@ -1510,12 +1316,7 @@ class GoogleAuthProvider {
1510
1316
  };
1511
1317
  }
1512
1318
  async handleResult(result) {
1513
- const context = {
1514
- logger: this.logger,
1515
- catalogIdentityClient: this.catalogIdentityClient,
1516
- tokenIssuer: this.tokenIssuer
1517
- };
1518
- const { profile } = await this.authHandler(result, context);
1319
+ const { profile } = await this.authHandler(result, this.resolverContext);
1519
1320
  const response = {
1520
1321
  providerInfo: {
1521
1322
  idToken: result.params.id_token,
@@ -1529,109 +1330,64 @@ class GoogleAuthProvider {
1529
1330
  response.backstageIdentity = await this.signInResolver({
1530
1331
  result,
1531
1332
  profile
1532
- }, context);
1333
+ }, this.resolverContext);
1533
1334
  }
1534
1335
  return response;
1535
1336
  }
1536
1337
  }
1537
- const googleEmailSignInResolver = async (info, ctx) => {
1538
- const { profile } = info;
1539
- if (!profile.email) {
1540
- throw new Error("Google profile contained no email");
1541
- }
1542
- const entity = await ctx.catalogIdentityClient.findUser({
1543
- annotations: {
1544
- "google.com/email": profile.email
1545
- }
1546
- });
1547
- const claims = getEntityClaims(entity);
1548
- const token = await ctx.tokenIssuer.issueToken({ claims });
1549
- return { id: entity.metadata.name, entity, token };
1550
- };
1551
- const googleDefaultSignInResolver = async (info, ctx) => {
1552
- const { profile } = info;
1553
- if (!profile.email) {
1554
- throw new Error("Google profile contained no email");
1555
- }
1556
- let userId;
1557
- try {
1558
- const entity = await ctx.catalogIdentityClient.findUser({
1559
- annotations: {
1560
- "google.com/email": profile.email
1561
- }
1338
+ const google = createAuthProviderIntegration({
1339
+ create(options) {
1340
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1341
+ var _a;
1342
+ const clientId = envConfig.getString("clientId");
1343
+ const clientSecret = envConfig.getString("clientSecret");
1344
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1345
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1346
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1347
+ profile: makeProfileInfo(fullProfile, params.id_token)
1348
+ });
1349
+ const provider = new GoogleAuthProvider({
1350
+ clientId,
1351
+ clientSecret,
1352
+ callbackUrl,
1353
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
1354
+ authHandler,
1355
+ resolverContext
1356
+ });
1357
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1358
+ disableRefresh: false,
1359
+ providerId,
1360
+ callbackUrl
1361
+ });
1562
1362
  });
1563
- userId = entity.metadata.name;
1564
- } catch (error) {
1565
- 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`);
1566
- userId = profile.email.split("@")[0];
1567
- }
1568
- const entityRef = catalogModel.stringifyEntityRef({
1569
- kind: "User",
1570
- namespace: catalogModel.DEFAULT_NAMESPACE,
1571
- name: userId
1572
- });
1573
- const token = await ctx.tokenIssuer.issueToken({
1574
- claims: {
1575
- sub: entityRef,
1576
- ent: [entityRef]
1363
+ },
1364
+ resolvers: {
1365
+ emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1366
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1367
+ emailMatchingUserEntityAnnotation() {
1368
+ return async (info, ctx) => {
1369
+ const { profile } = info;
1370
+ if (!profile.email) {
1371
+ throw new Error("Google profile contained no email");
1372
+ }
1373
+ return ctx.signInWithCatalogUser({
1374
+ annotations: {
1375
+ "google.com/email": profile.email
1376
+ }
1377
+ });
1378
+ };
1577
1379
  }
1578
- });
1579
- return { id: userId, token };
1580
- };
1581
- const createGoogleProvider = (options) => {
1582
- return ({
1583
- providerId,
1584
- globalConfig,
1585
- config,
1586
- tokenIssuer,
1587
- tokenManager,
1588
- catalogApi,
1589
- logger
1590
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1591
- var _a, _b;
1592
- const clientId = envConfig.getString("clientId");
1593
- const clientSecret = envConfig.getString("clientSecret");
1594
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1595
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1596
- const catalogIdentityClient = new CatalogIdentityClient({
1597
- catalogApi,
1598
- tokenManager
1599
- });
1600
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1601
- profile: makeProfileInfo(fullProfile, params.id_token)
1602
- });
1603
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : googleDefaultSignInResolver;
1604
- const signInResolver = (info) => signInResolverFn(info, {
1605
- catalogIdentityClient,
1606
- tokenIssuer,
1607
- logger
1608
- });
1609
- const provider = new GoogleAuthProvider({
1610
- clientId,
1611
- clientSecret,
1612
- callbackUrl,
1613
- signInResolver,
1614
- authHandler,
1615
- tokenIssuer,
1616
- catalogIdentityClient,
1617
- logger
1618
- });
1619
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1620
- disableRefresh: false,
1621
- providerId,
1622
- tokenIssuer,
1623
- callbackUrl
1624
- });
1625
- });
1626
- };
1380
+ }
1381
+ });
1382
+ const createGoogleProvider = google.create;
1383
+ const googleEmailSignInResolver = google.resolvers.emailMatchingUserEntityAnnotation();
1627
1384
 
1628
1385
  class MicrosoftAuthProvider {
1629
1386
  constructor(options) {
1630
1387
  this.signInResolver = options.signInResolver;
1631
1388
  this.authHandler = options.authHandler;
1632
- this.tokenIssuer = options.tokenIssuer;
1633
1389
  this.logger = options.logger;
1634
- this.catalogIdentityClient = options.catalogIdentityClient;
1390
+ this.resolverContext = options.resolverContext;
1635
1391
  this._strategy = new passportMicrosoft.Strategy({
1636
1392
  clientID: options.clientId,
1637
1393
  clientSecret: options.clientSecret,
@@ -1671,12 +1427,7 @@ class MicrosoftAuthProvider {
1671
1427
  async handleResult(result) {
1672
1428
  const photo = await this.getUserPhoto(result.accessToken);
1673
1429
  result.fullProfile.photos = photo ? [{ value: photo }] : void 0;
1674
- const context = {
1675
- logger: this.logger,
1676
- catalogIdentityClient: this.catalogIdentityClient,
1677
- tokenIssuer: this.tokenIssuer
1678
- };
1679
- const { profile } = await this.authHandler(result, context);
1430
+ const { profile } = await this.authHandler(result, this.resolverContext);
1680
1431
  const response = {
1681
1432
  providerInfo: {
1682
1433
  idToken: result.params.id_token,
@@ -1690,118 +1441,83 @@ class MicrosoftAuthProvider {
1690
1441
  response.backstageIdentity = await this.signInResolver({
1691
1442
  result,
1692
1443
  profile
1693
- }, context);
1444
+ }, this.resolverContext);
1694
1445
  }
1695
1446
  return response;
1696
1447
  }
1697
- getUserPhoto(accessToken) {
1698
- return new Promise((resolve) => {
1699
- fetch__default["default"]("https://graph.microsoft.com/v1.0/me/photos/48x48/$value", {
1448
+ async getUserPhoto(accessToken) {
1449
+ try {
1450
+ const res = await fetch__default["default"]("https://graph.microsoft.com/v1.0/me/photos/48x48/$value", {
1700
1451
  headers: {
1701
1452
  Authorization: `Bearer ${accessToken}`
1702
1453
  }
1703
- }).then((response) => response.arrayBuffer()).then((arrayBuffer) => {
1704
- const imageUrl = `data:image/jpeg;base64,${Buffer.from(arrayBuffer).toString("base64")}`;
1705
- resolve(imageUrl);
1706
- }).catch((error) => {
1707
- this.logger.warn(`Could not retrieve user profile photo from Microsoft Graph API: ${error}`);
1708
- resolve(void 0);
1709
1454
  });
1710
- });
1455
+ const data = await res.buffer();
1456
+ return `data:image/jpeg;base64,${data.toString("base64")}`;
1457
+ } catch (error) {
1458
+ this.logger.warn(`Could not retrieve user profile photo from Microsoft Graph API: ${error}`);
1459
+ return void 0;
1460
+ }
1711
1461
  }
1712
1462
  }
1713
- const microsoftEmailSignInResolver = async (info, ctx) => {
1714
- const { profile } = info;
1715
- if (!profile.email) {
1716
- throw new Error("Microsoft profile contained no email");
1717
- }
1718
- const entity = await ctx.catalogIdentityClient.findUser({
1719
- annotations: {
1720
- "microsoft.com/email": profile.email
1463
+ const microsoft = createAuthProviderIntegration({
1464
+ create(options) {
1465
+ return ({ providerId, globalConfig, config, logger, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1466
+ var _a;
1467
+ const clientId = envConfig.getString("clientId");
1468
+ const clientSecret = envConfig.getString("clientSecret");
1469
+ const tenantId = envConfig.getString("tenantId");
1470
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1471
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1472
+ const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
1473
+ const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
1474
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1475
+ profile: makeProfileInfo(fullProfile, params.id_token)
1476
+ });
1477
+ const provider = new MicrosoftAuthProvider({
1478
+ clientId,
1479
+ clientSecret,
1480
+ callbackUrl,
1481
+ authorizationUrl,
1482
+ tokenUrl,
1483
+ authHandler,
1484
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
1485
+ logger,
1486
+ resolverContext
1487
+ });
1488
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1489
+ disableRefresh: false,
1490
+ providerId,
1491
+ callbackUrl
1492
+ });
1493
+ });
1494
+ },
1495
+ resolvers: {
1496
+ emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1497
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1498
+ emailMatchingUserEntityAnnotation() {
1499
+ return async (info, ctx) => {
1500
+ const { profile } = info;
1501
+ if (!profile.email) {
1502
+ throw new Error("Microsoft profile contained no email");
1503
+ }
1504
+ return ctx.signInWithCatalogUser({
1505
+ annotations: {
1506
+ "microsoft.com/email": profile.email
1507
+ }
1508
+ });
1509
+ };
1721
1510
  }
1722
- });
1723
- const claims = getEntityClaims(entity);
1724
- const token = await ctx.tokenIssuer.issueToken({ claims });
1725
- return { id: entity.metadata.name, entity, token };
1726
- };
1727
- const microsoftDefaultSignInResolver = async (info, ctx) => {
1728
- const { profile } = info;
1729
- if (!profile.email) {
1730
- throw new Error("Profile contained no email");
1731
1511
  }
1732
- const userId = profile.email.split("@")[0];
1733
- const entityRef = catalogModel.stringifyEntityRef({
1734
- kind: "User",
1735
- namespace: catalogModel.DEFAULT_NAMESPACE,
1736
- name: userId
1737
- });
1738
- const token = await ctx.tokenIssuer.issueToken({
1739
- claims: {
1740
- sub: entityRef,
1741
- ent: [entityRef]
1742
- }
1743
- });
1744
- return { id: userId, token };
1745
- };
1746
- const createMicrosoftProvider = (options) => {
1747
- return ({
1748
- providerId,
1749
- globalConfig,
1750
- config,
1751
- tokenIssuer,
1752
- tokenManager,
1753
- catalogApi,
1754
- logger
1755
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1756
- var _a, _b;
1757
- const clientId = envConfig.getString("clientId");
1758
- const clientSecret = envConfig.getString("clientSecret");
1759
- const tenantId = envConfig.getString("tenantId");
1760
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1761
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1762
- const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`;
1763
- const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
1764
- const catalogIdentityClient = new CatalogIdentityClient({
1765
- catalogApi,
1766
- tokenManager
1767
- });
1768
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1769
- profile: makeProfileInfo(fullProfile, params.id_token)
1770
- });
1771
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : microsoftDefaultSignInResolver;
1772
- const signInResolver = (info) => signInResolverFn(info, {
1773
- catalogIdentityClient,
1774
- tokenIssuer,
1775
- logger
1776
- });
1777
- const provider = new MicrosoftAuthProvider({
1778
- clientId,
1779
- clientSecret,
1780
- callbackUrl,
1781
- authorizationUrl,
1782
- tokenUrl,
1783
- authHandler,
1784
- signInResolver,
1785
- catalogIdentityClient,
1786
- logger,
1787
- tokenIssuer
1788
- });
1789
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1790
- disableRefresh: false,
1791
- providerId,
1792
- tokenIssuer,
1793
- callbackUrl
1794
- });
1795
- });
1796
- };
1512
+ });
1513
+ const createMicrosoftProvider = microsoft.create;
1514
+ const microsoftEmailSignInResolver = microsoft.resolvers.emailMatchingUserEntityAnnotation();
1797
1515
 
1798
1516
  class OAuth2AuthProvider {
1799
1517
  constructor(options) {
1800
1518
  this.signInResolver = options.signInResolver;
1801
1519
  this.authHandler = options.authHandler;
1802
- this.tokenIssuer = options.tokenIssuer;
1803
- this.catalogIdentityClient = options.catalogIdentityClient;
1804
- this.logger = options.logger;
1520
+ this.resolverContext = options.resolverContext;
1805
1521
  this._strategy = new OAuth2Strategy.Strategy({
1806
1522
  clientID: options.clientId,
1807
1523
  clientSecret: options.clientSecret,
@@ -1853,12 +1569,7 @@ class OAuth2AuthProvider {
1853
1569
  };
1854
1570
  }
1855
1571
  async handleResult(result) {
1856
- const context = {
1857
- logger: this.logger,
1858
- catalogIdentityClient: this.catalogIdentityClient,
1859
- tokenIssuer: this.tokenIssuer
1860
- };
1861
- const { profile } = await this.authHandler(result, context);
1572
+ const { profile } = await this.authHandler(result, this.resolverContext);
1862
1573
  const response = {
1863
1574
  providerInfo: {
1864
1575
  idToken: result.params.id_token,
@@ -1872,7 +1583,7 @@ class OAuth2AuthProvider {
1872
1583
  response.backstageIdentity = await this.signInResolver({
1873
1584
  result,
1874
1585
  profile
1875
- }, context);
1586
+ }, this.resolverContext);
1876
1587
  }
1877
1588
  return response;
1878
1589
  }
@@ -1880,87 +1591,48 @@ class OAuth2AuthProvider {
1880
1591
  return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
1881
1592
  }
1882
1593
  }
1883
- const oAuth2DefaultSignInResolver = async (info, ctx) => {
1884
- const { profile } = info;
1885
- if (!profile.email) {
1886
- throw new Error("Profile contained no email");
1887
- }
1888
- const userId = profile.email.split("@")[0];
1889
- const entityRef = catalogModel.stringifyEntityRef({
1890
- kind: "User",
1891
- namespace: catalogModel.DEFAULT_NAMESPACE,
1892
- name: userId
1893
- });
1894
- const token = await ctx.tokenIssuer.issueToken({
1895
- claims: {
1896
- sub: entityRef,
1897
- ent: [entityRef]
1898
- }
1899
- });
1900
- return { id: userId, token };
1901
- };
1902
- const createOAuth2Provider = (options) => {
1903
- return ({
1904
- providerId,
1905
- globalConfig,
1906
- config,
1907
- tokenIssuer,
1908
- tokenManager,
1909
- catalogApi,
1910
- logger
1911
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1912
- var _a, _b, _c;
1913
- const clientId = envConfig.getString("clientId");
1914
- const clientSecret = envConfig.getString("clientSecret");
1915
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1916
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1917
- const authorizationUrl = envConfig.getString("authorizationUrl");
1918
- const tokenUrl = envConfig.getString("tokenUrl");
1919
- const scope = envConfig.getOptionalString("scope");
1920
- const includeBasicAuth = envConfig.getOptionalBoolean("includeBasicAuth");
1921
- const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
1922
- const catalogIdentityClient = new CatalogIdentityClient({
1923
- catalogApi,
1924
- tokenManager
1925
- });
1926
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1927
- profile: makeProfileInfo(fullProfile, params.id_token)
1928
- });
1929
- const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver;
1930
- const signInResolver = (info) => signInResolverFn(info, {
1931
- catalogIdentityClient,
1932
- tokenIssuer,
1933
- logger
1934
- });
1935
- const provider = new OAuth2AuthProvider({
1936
- clientId,
1937
- clientSecret,
1938
- tokenIssuer,
1939
- catalogIdentityClient,
1940
- callbackUrl,
1941
- signInResolver,
1942
- authHandler,
1943
- authorizationUrl,
1944
- tokenUrl,
1945
- scope,
1946
- logger,
1947
- includeBasicAuth
1948
- });
1949
- return OAuthAdapter.fromConfig(globalConfig, provider, {
1950
- disableRefresh,
1951
- providerId,
1952
- tokenIssuer,
1953
- callbackUrl
1594
+ const oauth2 = createAuthProviderIntegration({
1595
+ create(options) {
1596
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1597
+ var _a, _b;
1598
+ const clientId = envConfig.getString("clientId");
1599
+ const clientSecret = envConfig.getString("clientSecret");
1600
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1601
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1602
+ const authorizationUrl = envConfig.getString("authorizationUrl");
1603
+ const tokenUrl = envConfig.getString("tokenUrl");
1604
+ const scope = envConfig.getOptionalString("scope");
1605
+ const includeBasicAuth = envConfig.getOptionalBoolean("includeBasicAuth");
1606
+ const disableRefresh = (_a = envConfig.getOptionalBoolean("disableRefresh")) != null ? _a : false;
1607
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1608
+ profile: makeProfileInfo(fullProfile, params.id_token)
1609
+ });
1610
+ const provider = new OAuth2AuthProvider({
1611
+ clientId,
1612
+ clientSecret,
1613
+ callbackUrl,
1614
+ signInResolver: (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver,
1615
+ authHandler,
1616
+ authorizationUrl,
1617
+ tokenUrl,
1618
+ scope,
1619
+ includeBasicAuth,
1620
+ resolverContext
1621
+ });
1622
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1623
+ disableRefresh,
1624
+ providerId,
1625
+ callbackUrl
1626
+ });
1954
1627
  });
1955
- });
1956
- };
1628
+ }
1629
+ });
1630
+ const createOAuth2Provider = oauth2.create;
1957
1631
 
1958
1632
  const OAUTH2_PROXY_JWT_HEADER = "X-OAUTH2-PROXY-ID-TOKEN";
1959
1633
  class Oauth2ProxyAuthProvider {
1960
1634
  constructor(options) {
1961
- this.catalogIdentityClient = options.catalogIdentityClient;
1962
- this.logger = options.logger;
1963
- this.tokenIssuer = options.tokenIssuer;
1635
+ this.resolverContext = options.resolverContext;
1964
1636
  this.signInResolver = options.signInResolver;
1965
1637
  this.authHandler = options.authHandler;
1966
1638
  }
@@ -1973,25 +1645,18 @@ class Oauth2ProxyAuthProvider {
1973
1645
  const response = await this.handleResult(result);
1974
1646
  res.json(response);
1975
1647
  } catch (e) {
1976
- this.logger.error(`Exception occurred during ${OAUTH2_PROXY_JWT_HEADER} refresh`, e);
1977
- res.status(401);
1978
- res.end();
1648
+ throw new errors.AuthenticationError("Refresh failed", e);
1979
1649
  }
1980
1650
  }
1981
1651
  start() {
1982
1652
  return Promise.resolve(void 0);
1983
1653
  }
1984
1654
  async handleResult(result) {
1985
- const ctx = {
1986
- logger: this.logger,
1987
- tokenIssuer: this.tokenIssuer,
1988
- catalogIdentityClient: this.catalogIdentityClient
1989
- };
1990
- const { profile } = await this.authHandler(result, ctx);
1655
+ const { profile } = await this.authHandler(result, this.resolverContext);
1991
1656
  const backstageSignInResult = await this.signInResolver({
1992
1657
  result,
1993
1658
  profile
1994
- }, ctx);
1659
+ }, this.resolverContext);
1995
1660
  return {
1996
1661
  providerInfo: {
1997
1662
  accessToken: result.accessToken
@@ -2006,28 +1671,27 @@ class Oauth2ProxyAuthProvider {
2006
1671
  if (!jwt) {
2007
1672
  throw new errors.AuthenticationError(`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`);
2008
1673
  }
2009
- const decodedJWT = jose.JWT.decode(jwt);
1674
+ const decodedJWT = jose.decodeJwt(jwt);
2010
1675
  return {
2011
1676
  fullProfile: decodedJWT,
2012
1677
  accessToken: jwt
2013
1678
  };
2014
1679
  }
2015
1680
  }
2016
- const createOauth2ProxyProvider = (options) => ({ catalogApi, logger, tokenIssuer, tokenManager }) => {
2017
- const signInResolver = options.signIn.resolver;
2018
- const authHandler = options.authHandler;
2019
- const catalogIdentityClient = new CatalogIdentityClient({
2020
- catalogApi,
2021
- tokenManager
2022
- });
2023
- return new Oauth2ProxyAuthProvider({
2024
- logger,
2025
- signInResolver,
2026
- authHandler,
2027
- tokenIssuer,
2028
- catalogIdentityClient
2029
- });
2030
- };
1681
+ const oauth2Proxy = createAuthProviderIntegration({
1682
+ create(options) {
1683
+ return ({ resolverContext }) => {
1684
+ const signInResolver = options.signIn.resolver;
1685
+ const authHandler = options.authHandler;
1686
+ return new Oauth2ProxyAuthProvider({
1687
+ resolverContext,
1688
+ signInResolver,
1689
+ authHandler
1690
+ });
1691
+ };
1692
+ }
1693
+ });
1694
+ const createOauth2ProxyProvider = oauth2Proxy.create;
2031
1695
 
2032
1696
  class OidcAuthProvider {
2033
1697
  constructor(options) {
@@ -2036,9 +1700,7 @@ class OidcAuthProvider {
2036
1700
  this.prompt = options.prompt;
2037
1701
  this.signInResolver = options.signInResolver;
2038
1702
  this.authHandler = options.authHandler;
2039
- this.tokenIssuer = options.tokenIssuer;
2040
- this.catalogIdentityClient = options.catalogIdentityClient;
2041
- this.logger = options.logger;
1703
+ this.resolverContext = options.resolverContext;
2042
1704
  }
2043
1705
  async start(req) {
2044
1706
  const { strategy } = await this.implementation;
@@ -2098,12 +1760,7 @@ class OidcAuthProvider {
2098
1760
  return { strategy, client };
2099
1761
  }
2100
1762
  async handleResult(result) {
2101
- const context = {
2102
- logger: this.logger,
2103
- catalogIdentityClient: this.catalogIdentityClient,
2104
- tokenIssuer: this.tokenIssuer
2105
- };
2106
- const { profile } = await this.authHandler(result, context);
1763
+ const { profile } = await this.authHandler(result, this.resolverContext);
2107
1764
  const response = {
2108
1765
  providerInfo: {
2109
1766
  idToken: result.tokenset.id_token,
@@ -2117,92 +1774,55 @@ class OidcAuthProvider {
2117
1774
  response.backstageIdentity = await this.signInResolver({
2118
1775
  result,
2119
1776
  profile
2120
- }, context);
1777
+ }, this.resolverContext);
2121
1778
  }
2122
1779
  return response;
2123
1780
  }
2124
1781
  }
2125
- const oidcDefaultSignInResolver = async (info, ctx) => {
2126
- const { profile } = info;
2127
- if (!profile.email) {
2128
- throw new Error("Profile contained no email");
2129
- }
2130
- const userId = profile.email.split("@")[0];
2131
- const entityRef = catalogModel.stringifyEntityRef({
2132
- kind: "User",
2133
- namespace: catalogModel.DEFAULT_NAMESPACE,
2134
- name: userId
2135
- });
2136
- const token = await ctx.tokenIssuer.issueToken({
2137
- claims: {
2138
- sub: entityRef,
2139
- ent: [entityRef]
2140
- }
2141
- });
2142
- return { id: userId, token };
2143
- };
2144
- const createOidcProvider = (options) => {
2145
- return ({
2146
- providerId,
2147
- globalConfig,
2148
- config,
2149
- tokenIssuer,
2150
- tokenManager,
2151
- catalogApi,
2152
- logger
2153
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2154
- var _a, _b;
2155
- const clientId = envConfig.getString("clientId");
2156
- const clientSecret = envConfig.getString("clientSecret");
2157
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
2158
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2159
- const metadataUrl = envConfig.getString("metadataUrl");
2160
- const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
2161
- const scope = envConfig.getOptionalString("scope");
2162
- const prompt = envConfig.getOptionalString("prompt");
2163
- const catalogIdentityClient = new CatalogIdentityClient({
2164
- catalogApi,
2165
- tokenManager
2166
- });
2167
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ userinfo }) => ({
2168
- profile: {
2169
- displayName: userinfo.name,
2170
- email: userinfo.email,
2171
- picture: userinfo.picture
2172
- }
2173
- });
2174
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oidcDefaultSignInResolver;
2175
- const signInResolver = (info) => signInResolverFn(info, {
2176
- catalogIdentityClient,
2177
- tokenIssuer,
2178
- logger
2179
- });
2180
- const provider = new OidcAuthProvider({
2181
- clientId,
2182
- clientSecret,
2183
- callbackUrl,
2184
- tokenSignedResponseAlg,
2185
- metadataUrl,
2186
- scope,
2187
- prompt,
2188
- signInResolver,
2189
- authHandler,
2190
- logger,
2191
- tokenIssuer,
2192
- catalogIdentityClient
2193
- });
2194
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2195
- disableRefresh: false,
2196
- providerId,
2197
- tokenIssuer,
2198
- callbackUrl
1782
+ const oidc = createAuthProviderIntegration({
1783
+ create(options) {
1784
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1785
+ var _a;
1786
+ const clientId = envConfig.getString("clientId");
1787
+ const clientSecret = envConfig.getString("clientSecret");
1788
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1789
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1790
+ const metadataUrl = envConfig.getString("metadataUrl");
1791
+ const tokenSignedResponseAlg = envConfig.getOptionalString("tokenSignedResponseAlg");
1792
+ const scope = envConfig.getOptionalString("scope");
1793
+ const prompt = envConfig.getOptionalString("prompt");
1794
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ userinfo }) => ({
1795
+ profile: {
1796
+ displayName: userinfo.name,
1797
+ email: userinfo.email,
1798
+ picture: userinfo.picture
1799
+ }
1800
+ });
1801
+ const provider = new OidcAuthProvider({
1802
+ clientId,
1803
+ clientSecret,
1804
+ callbackUrl,
1805
+ tokenSignedResponseAlg,
1806
+ metadataUrl,
1807
+ scope,
1808
+ prompt,
1809
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
1810
+ authHandler,
1811
+ resolverContext
1812
+ });
1813
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1814
+ disableRefresh: false,
1815
+ providerId,
1816
+ callbackUrl
1817
+ });
2199
1818
  });
2200
- });
2201
- };
1819
+ }
1820
+ });
1821
+ const createOidcProvider = oidc.create;
2202
1822
 
2203
1823
  class OktaAuthProvider {
2204
1824
  constructor(options) {
2205
- this._store = {
1825
+ this.store = {
2206
1826
  store(_req, cb) {
2207
1827
  cb(null, null);
2208
1828
  },
@@ -2210,18 +1830,16 @@ class OktaAuthProvider {
2210
1830
  cb(null, true);
2211
1831
  }
2212
1832
  };
2213
- this._signInResolver = options.signInResolver;
2214
- this._authHandler = options.authHandler;
2215
- this._tokenIssuer = options.tokenIssuer;
2216
- this._catalogIdentityClient = options.catalogIdentityClient;
2217
- this._logger = options.logger;
2218
- this._strategy = new passportOktaOauth.Strategy({
1833
+ this.signInResolver = options.signInResolver;
1834
+ this.authHandler = options.authHandler;
1835
+ this.resolverContext = options.resolverContext;
1836
+ this.strategy = new passportOktaOauth.Strategy({
2219
1837
  clientID: options.clientId,
2220
1838
  clientSecret: options.clientSecret,
2221
1839
  callbackURL: options.callbackUrl,
2222
1840
  audience: options.audience,
2223
1841
  passReqToCallback: false,
2224
- store: this._store,
1842
+ store: this.store,
2225
1843
  response_type: "code"
2226
1844
  }, (accessToken, refreshToken, params, fullProfile, done) => {
2227
1845
  done(void 0, {
@@ -2235,7 +1853,7 @@ class OktaAuthProvider {
2235
1853
  });
2236
1854
  }
2237
1855
  async start(req) {
2238
- return await executeRedirectStrategy(req, this._strategy, {
1856
+ return await executeRedirectStrategy(req, this.strategy, {
2239
1857
  accessType: "offline",
2240
1858
  prompt: "consent",
2241
1859
  scope: req.scope,
@@ -2243,15 +1861,15 @@ class OktaAuthProvider {
2243
1861
  });
2244
1862
  }
2245
1863
  async handler(req) {
2246
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
1864
+ const { result, privateInfo } = await executeFrameHandlerStrategy(req, this.strategy);
2247
1865
  return {
2248
1866
  response: await this.handleResult(result),
2249
1867
  refreshToken: privateInfo.refreshToken
2250
1868
  };
2251
1869
  }
2252
1870
  async refresh(req) {
2253
- const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this._strategy, req.refreshToken, req.scope);
2254
- const fullProfile = await executeFetchUserProfileStrategy(this._strategy, accessToken);
1871
+ const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(this.strategy, req.refreshToken, req.scope);
1872
+ const fullProfile = await executeFetchUserProfileStrategy(this.strategy, accessToken);
2255
1873
  return {
2256
1874
  response: await this.handleResult({
2257
1875
  fullProfile,
@@ -2262,12 +1880,7 @@ class OktaAuthProvider {
2262
1880
  };
2263
1881
  }
2264
1882
  async handleResult(result) {
2265
- const context = {
2266
- logger: this._logger,
2267
- catalogIdentityClient: this._catalogIdentityClient,
2268
- tokenIssuer: this._tokenIssuer
2269
- };
2270
- const { profile } = await this._authHandler(result, context);
1883
+ const { profile } = await this.authHandler(result, this.resolverContext);
2271
1884
  const response = {
2272
1885
  providerInfo: {
2273
1886
  idToken: result.params.id_token,
@@ -2277,107 +1890,72 @@ class OktaAuthProvider {
2277
1890
  },
2278
1891
  profile
2279
1892
  };
2280
- if (this._signInResolver) {
2281
- response.backstageIdentity = await this._signInResolver({
1893
+ if (this.signInResolver) {
1894
+ response.backstageIdentity = await this.signInResolver({
2282
1895
  result,
2283
1896
  profile
2284
- }, context);
1897
+ }, this.resolverContext);
2285
1898
  }
2286
1899
  return response;
2287
1900
  }
2288
1901
  }
2289
- const oktaEmailSignInResolver = async (info, ctx) => {
2290
- const { profile } = info;
2291
- if (!profile.email) {
2292
- throw new Error("Okta profile contained no email");
2293
- }
2294
- const entity = await ctx.catalogIdentityClient.findUser({
2295
- annotations: {
2296
- "okta.com/email": profile.email
1902
+ const okta = createAuthProviderIntegration({
1903
+ create(options) {
1904
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
1905
+ var _a;
1906
+ const clientId = envConfig.getString("clientId");
1907
+ const clientSecret = envConfig.getString("clientSecret");
1908
+ const audience = envConfig.getString("audience");
1909
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1910
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1911
+ if (!audience.startsWith("https://")) {
1912
+ throw new Error("URL for 'audience' must start with 'https://'.");
1913
+ }
1914
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1915
+ profile: makeProfileInfo(fullProfile, params.id_token)
1916
+ });
1917
+ const provider = new OktaAuthProvider({
1918
+ audience,
1919
+ clientId,
1920
+ clientSecret,
1921
+ callbackUrl,
1922
+ authHandler,
1923
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
1924
+ resolverContext
1925
+ });
1926
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
1927
+ disableRefresh: false,
1928
+ providerId,
1929
+ callbackUrl
1930
+ });
1931
+ });
1932
+ },
1933
+ resolvers: {
1934
+ emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1935
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1936
+ emailMatchingUserEntityAnnotation() {
1937
+ return async (info, ctx) => {
1938
+ const { profile } = info;
1939
+ if (!profile.email) {
1940
+ throw new Error("Okta profile contained no email");
1941
+ }
1942
+ return ctx.signInWithCatalogUser({
1943
+ annotations: {
1944
+ "okta.com/email": profile.email
1945
+ }
1946
+ });
1947
+ };
2297
1948
  }
2298
- });
2299
- const claims = getEntityClaims(entity);
2300
- const token = await ctx.tokenIssuer.issueToken({ claims });
2301
- return { id: entity.metadata.name, entity, token };
2302
- };
2303
- const oktaDefaultSignInResolver = async (info, ctx) => {
2304
- const { profile } = info;
2305
- if (!profile.email) {
2306
- throw new Error("Okta profile contained no email");
2307
1949
  }
2308
- const userId = profile.email.split("@")[0];
2309
- const entityRef = catalogModel.stringifyEntityRef({
2310
- kind: "User",
2311
- namespace: catalogModel.DEFAULT_NAMESPACE,
2312
- name: userId
2313
- });
2314
- const token = await ctx.tokenIssuer.issueToken({
2315
- claims: {
2316
- sub: entityRef,
2317
- ent: [entityRef]
2318
- }
2319
- });
2320
- return { id: userId, token };
2321
- };
2322
- const createOktaProvider = (_options) => {
2323
- return ({
2324
- providerId,
2325
- globalConfig,
2326
- config,
2327
- tokenIssuer,
2328
- tokenManager,
2329
- catalogApi,
2330
- logger
2331
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2332
- var _a, _b;
2333
- const clientId = envConfig.getString("clientId");
2334
- const clientSecret = envConfig.getString("clientSecret");
2335
- const audience = envConfig.getString("audience");
2336
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
2337
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2338
- if (!audience.startsWith("https://")) {
2339
- throw new Error("URL for 'audience' must start with 'https://'.");
2340
- }
2341
- const catalogIdentityClient = new CatalogIdentityClient({
2342
- catalogApi,
2343
- tokenManager
2344
- });
2345
- const authHandler = (_options == null ? void 0 : _options.authHandler) ? _options.authHandler : async ({ fullProfile, params }) => ({
2346
- profile: makeProfileInfo(fullProfile, params.id_token)
2347
- });
2348
- const signInResolverFn = (_b = (_a = _options == null ? void 0 : _options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oktaDefaultSignInResolver;
2349
- const signInResolver = (info) => signInResolverFn(info, {
2350
- catalogIdentityClient,
2351
- tokenIssuer,
2352
- logger
2353
- });
2354
- const provider = new OktaAuthProvider({
2355
- audience,
2356
- clientId,
2357
- clientSecret,
2358
- callbackUrl,
2359
- authHandler,
2360
- signInResolver,
2361
- tokenIssuer,
2362
- catalogIdentityClient,
2363
- logger
2364
- });
2365
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2366
- disableRefresh: false,
2367
- providerId,
2368
- tokenIssuer,
2369
- callbackUrl
2370
- });
2371
- });
2372
- };
1950
+ });
1951
+ const createOktaProvider = okta.create;
1952
+ const oktaEmailSignInResolver = okta.resolvers.emailMatchingUserEntityAnnotation();
2373
1953
 
2374
1954
  class OneLoginProvider {
2375
1955
  constructor(options) {
2376
1956
  this.signInResolver = options.signInResolver;
2377
1957
  this.authHandler = options.authHandler;
2378
- this.tokenIssuer = options.tokenIssuer;
2379
- this.catalogIdentityClient = options.catalogIdentityClient;
2380
- this.logger = options.logger;
1958
+ this.resolverContext = options.resolverContext;
2381
1959
  this._strategy = new passportOneloginOauth.Strategy({
2382
1960
  issuer: options.issuer,
2383
1961
  clientID: options.clientId,
@@ -2423,12 +2001,7 @@ class OneLoginProvider {
2423
2001
  };
2424
2002
  }
2425
2003
  async handleResult(result) {
2426
- const context = {
2427
- logger: this.logger,
2428
- catalogIdentityClient: this.catalogIdentityClient,
2429
- tokenIssuer: this.tokenIssuer
2430
- };
2431
- const { profile } = await this.authHandler(result, context);
2004
+ const { profile } = await this.authHandler(result, this.resolverContext);
2432
2005
  const response = {
2433
2006
  providerInfo: {
2434
2007
  idToken: result.params.id_token,
@@ -2442,71 +2015,48 @@ class OneLoginProvider {
2442
2015
  response.backstageIdentity = await this.signInResolver({
2443
2016
  result,
2444
2017
  profile
2445
- }, context);
2018
+ }, this.resolverContext);
2446
2019
  }
2447
2020
  return response;
2448
2021
  }
2449
2022
  }
2450
- const defaultSignInResolver = async (info) => {
2451
- const { profile } = info;
2452
- if (!profile.email) {
2453
- throw new Error("OIDC profile contained no email");
2454
- }
2455
- const id = profile.email.split("@")[0];
2456
- return { id, token: "" };
2457
- };
2458
- const createOneLoginProvider = (options) => {
2459
- return ({
2460
- providerId,
2461
- globalConfig,
2462
- config,
2463
- tokenIssuer,
2464
- tokenManager,
2465
- catalogApi,
2466
- logger
2467
- }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2468
- var _a, _b;
2469
- const clientId = envConfig.getString("clientId");
2470
- const clientSecret = envConfig.getString("clientSecret");
2471
- const issuer = envConfig.getString("issuer");
2472
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
2473
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2474
- const catalogIdentityClient = new CatalogIdentityClient({
2475
- catalogApi,
2476
- tokenManager
2477
- });
2478
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2479
- profile: makeProfileInfo(fullProfile, params.id_token)
2480
- });
2481
- const signInResolver = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : defaultSignInResolver;
2482
- const provider = new OneLoginProvider({
2483
- clientId,
2484
- clientSecret,
2485
- callbackUrl,
2486
- issuer,
2487
- authHandler,
2488
- signInResolver,
2489
- tokenIssuer,
2490
- catalogIdentityClient,
2491
- logger
2492
- });
2493
- return OAuthAdapter.fromConfig(globalConfig, provider, {
2494
- disableRefresh: false,
2495
- providerId,
2496
- tokenIssuer,
2497
- callbackUrl
2023
+ const onelogin = createAuthProviderIntegration({
2024
+ create(options) {
2025
+ return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
2026
+ var _a;
2027
+ const clientId = envConfig.getString("clientId");
2028
+ const clientSecret = envConfig.getString("clientSecret");
2029
+ const issuer = envConfig.getString("issuer");
2030
+ const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
2031
+ const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
2032
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
2033
+ profile: makeProfileInfo(fullProfile, params.id_token)
2034
+ });
2035
+ const provider = new OneLoginProvider({
2036
+ clientId,
2037
+ clientSecret,
2038
+ callbackUrl,
2039
+ issuer,
2040
+ authHandler,
2041
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
2042
+ resolverContext
2043
+ });
2044
+ return OAuthAdapter.fromConfig(globalConfig, provider, {
2045
+ disableRefresh: false,
2046
+ providerId,
2047
+ callbackUrl
2048
+ });
2498
2049
  });
2499
- });
2500
- };
2050
+ }
2051
+ });
2052
+ const createOneLoginProvider = onelogin.create;
2501
2053
 
2502
2054
  class SamlAuthProvider {
2503
2055
  constructor(options) {
2504
2056
  this.appUrl = options.appUrl;
2505
2057
  this.signInResolver = options.signInResolver;
2506
2058
  this.authHandler = options.authHandler;
2507
- this.tokenIssuer = options.tokenIssuer;
2508
- this.catalogIdentityClient = options.catalogIdentityClient;
2509
- this.logger = options.logger;
2059
+ this.resolverContext = options.resolverContext;
2510
2060
  this.strategy = new passportSaml.Strategy({ ...options }, (fullProfile, done) => {
2511
2061
  done(void 0, { fullProfile });
2512
2062
  });
@@ -2517,13 +2067,8 @@ class SamlAuthProvider {
2517
2067
  }
2518
2068
  async frameHandler(req, res) {
2519
2069
  try {
2520
- const context = {
2521
- logger: this.logger,
2522
- catalogIdentityClient: this.catalogIdentityClient,
2523
- tokenIssuer: this.tokenIssuer
2524
- };
2525
2070
  const { result } = await executeFrameHandlerStrategy(req, this.strategy);
2526
- const { profile } = await this.authHandler(result, context);
2071
+ const { profile } = await this.authHandler(result, this.resolverContext);
2527
2072
  const response = {
2528
2073
  profile,
2529
2074
  providerInfo: {}
@@ -2532,7 +2077,7 @@ class SamlAuthProvider {
2532
2077
  const signInResponse = await this.signInResolver({
2533
2078
  result,
2534
2079
  profile
2535
- }, context);
2080
+ }, this.resolverContext);
2536
2081
  response.backstageIdentity = prepareBackstageIdentityResponse(signInResponse);
2537
2082
  }
2538
2083
  return postMessageResponse(res, this.appUrl, {
@@ -2551,71 +2096,53 @@ class SamlAuthProvider {
2551
2096
  res.end();
2552
2097
  }
2553
2098
  }
2554
- const samlDefaultSignInResolver = async (info, ctx) => {
2555
- const id = info.result.fullProfile.nameID;
2556
- const entityRef = catalogModel.stringifyEntityRef({
2557
- kind: "User",
2558
- namespace: catalogModel.DEFAULT_NAMESPACE,
2559
- name: id
2560
- });
2561
- const token = await ctx.tokenIssuer.issueToken({
2562
- claims: {
2563
- sub: entityRef,
2564
- ent: [entityRef]
2099
+ const saml = createAuthProviderIntegration({
2100
+ create(options) {
2101
+ return ({ providerId, globalConfig, config, resolverContext }) => {
2102
+ var _a;
2103
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2104
+ profile: {
2105
+ email: fullProfile.email,
2106
+ displayName: fullProfile.displayName
2107
+ }
2108
+ });
2109
+ return new SamlAuthProvider({
2110
+ callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2111
+ entryPoint: config.getString("entryPoint"),
2112
+ logoutUrl: config.getOptionalString("logoutUrl"),
2113
+ audience: config.getOptionalString("audience"),
2114
+ issuer: config.getString("issuer"),
2115
+ cert: config.getString("cert"),
2116
+ privateKey: config.getOptionalString("privateKey"),
2117
+ authnContext: config.getOptionalStringArray("authnContext"),
2118
+ identifierFormat: config.getOptionalString("identifierFormat"),
2119
+ decryptionPvk: config.getOptionalString("decryptionPvk"),
2120
+ signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2121
+ digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2122
+ acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2123
+ appUrl: globalConfig.appUrl,
2124
+ authHandler,
2125
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
2126
+ resolverContext
2127
+ });
2128
+ };
2129
+ },
2130
+ resolvers: {
2131
+ nameIdMatchingUserEntityName() {
2132
+ return async (info, ctx) => {
2133
+ const id = info.result.fullProfile.nameID;
2134
+ if (!id) {
2135
+ throw new errors.AuthenticationError("No nameID found in SAML response");
2136
+ }
2137
+ return ctx.signInWithCatalogUser({
2138
+ entityRef: { name: id }
2139
+ });
2140
+ };
2565
2141
  }
2566
- });
2567
- return { id, token };
2568
- };
2569
- const createSamlProvider = (options) => {
2570
- return ({
2571
- providerId,
2572
- globalConfig,
2573
- config,
2574
- tokenIssuer,
2575
- tokenManager,
2576
- catalogApi,
2577
- logger
2578
- }) => {
2579
- var _a, _b;
2580
- const catalogIdentityClient = new CatalogIdentityClient({
2581
- catalogApi,
2582
- tokenManager
2583
- });
2584
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile }) => ({
2585
- profile: {
2586
- email: fullProfile.email,
2587
- displayName: fullProfile.displayName
2588
- }
2589
- });
2590
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : samlDefaultSignInResolver;
2591
- const signInResolver = (info) => signInResolverFn(info, {
2592
- catalogIdentityClient,
2593
- tokenIssuer,
2594
- logger
2595
- });
2596
- return new SamlAuthProvider({
2597
- callbackUrl: `${globalConfig.baseUrl}/${providerId}/handler/frame`,
2598
- entryPoint: config.getString("entryPoint"),
2599
- logoutUrl: config.getOptionalString("logoutUrl"),
2600
- audience: config.getOptionalString("audience"),
2601
- issuer: config.getString("issuer"),
2602
- cert: config.getString("cert"),
2603
- privateKey: config.getOptionalString("privateKey"),
2604
- authnContext: config.getOptionalStringArray("authnContext"),
2605
- identifierFormat: config.getOptionalString("identifierFormat"),
2606
- decryptionPvk: config.getOptionalString("decryptionPvk"),
2607
- signatureAlgorithm: config.getOptionalString("signatureAlgorithm"),
2608
- digestAlgorithm: config.getOptionalString("digestAlgorithm"),
2609
- acceptedClockSkewMs: config.getOptionalNumber("acceptedClockSkewMs"),
2610
- tokenIssuer,
2611
- appUrl: globalConfig.appUrl,
2612
- authHandler,
2613
- signInResolver,
2614
- logger,
2615
- catalogIdentityClient
2616
- });
2617
- };
2618
- };
2142
+ }
2143
+ });
2144
+ const createSamlProvider = saml.create;
2145
+ const samlNameIdEntityNameSignInResolver = saml.resolvers.nameIdMatchingUserEntityName();
2619
2146
 
2620
2147
  const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
2621
2148
 
@@ -2661,9 +2188,7 @@ class GcpIapProvider {
2661
2188
  this.authHandler = options.authHandler;
2662
2189
  this.signInResolver = options.signInResolver;
2663
2190
  this.tokenValidator = options.tokenValidator;
2664
- this.tokenIssuer = options.tokenIssuer;
2665
- this.catalogIdentityClient = options.catalogIdentityClient;
2666
- this.logger = options.logger;
2191
+ this.resolverContext = options.resolverContext;
2667
2192
  }
2668
2193
  async start() {
2669
2194
  }
@@ -2671,13 +2196,8 @@ class GcpIapProvider {
2671
2196
  }
2672
2197
  async refresh(req, res) {
2673
2198
  const result = await parseRequestToken(req.header(IAP_JWT_HEADER), this.tokenValidator);
2674
- const context = {
2675
- logger: this.logger,
2676
- catalogIdentityClient: this.catalogIdentityClient,
2677
- tokenIssuer: this.tokenIssuer
2678
- };
2679
- const { profile } = await this.authHandler(result, context);
2680
- const backstageIdentity = await this.signInResolver({ profile, result }, context);
2199
+ const { profile } = await this.authHandler(result, this.resolverContext);
2200
+ const backstageIdentity = await this.signInResolver({ profile, result }, this.resolverContext);
2681
2201
  const response = {
2682
2202
  providerInfo: { iapToken: result.iapToken },
2683
2203
  profile,
@@ -2686,27 +2206,42 @@ class GcpIapProvider {
2686
2206
  res.json(response);
2687
2207
  }
2688
2208
  }
2689
- function createGcpIapProvider(options) {
2690
- return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
2691
- var _a;
2692
- const audience = config.getString("audience");
2693
- const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
2694
- const signInResolver = options.signIn.resolver;
2695
- const tokenValidator = createTokenValidator(audience);
2696
- const catalogIdentityClient = new CatalogIdentityClient({
2697
- catalogApi,
2698
- tokenManager
2699
- });
2700
- return new GcpIapProvider({
2701
- authHandler,
2702
- signInResolver,
2703
- tokenValidator,
2704
- tokenIssuer,
2705
- catalogIdentityClient,
2706
- logger
2707
- });
2708
- };
2709
- }
2209
+ const gcpIap = createAuthProviderIntegration({
2210
+ create(options) {
2211
+ return ({ config, resolverContext }) => {
2212
+ var _a;
2213
+ const audience = config.getString("audience");
2214
+ const authHandler = (_a = options.authHandler) != null ? _a : defaultAuthHandler;
2215
+ const signInResolver = options.signIn.resolver;
2216
+ const tokenValidator = createTokenValidator(audience);
2217
+ return new GcpIapProvider({
2218
+ authHandler,
2219
+ signInResolver,
2220
+ tokenValidator,
2221
+ resolverContext
2222
+ });
2223
+ };
2224
+ }
2225
+ });
2226
+ const createGcpIapProvider = gcpIap.create;
2227
+
2228
+ const providers = Object.freeze({
2229
+ atlassian,
2230
+ auth0,
2231
+ awsAlb,
2232
+ bitbucket,
2233
+ gcpIap,
2234
+ github,
2235
+ gitlab,
2236
+ google,
2237
+ microsoft,
2238
+ oauth2,
2239
+ oauth2Proxy,
2240
+ oidc,
2241
+ okta,
2242
+ onelogin,
2243
+ saml
2244
+ });
2710
2245
 
2711
2246
  const factories = {
2712
2247
  google: createGoogleProvider(),
@@ -2778,10 +2313,10 @@ class TokenFactory {
2778
2313
  throw new Error('"sub" claim provided by the auth resolver is not a valid EntityRef.');
2779
2314
  }
2780
2315
  this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2781
- return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2782
- alg: key.alg,
2783
- kid: key.kid
2784
- });
2316
+ if (!key.alg) {
2317
+ throw new errors.AuthenticationError("No algorithm was provided in the key");
2318
+ }
2319
+ return new jose.SignJWT({ iss, sub, aud, iat, exp }).setProtectedHeader({ alg: key.alg, kid: key.kid }).setIssuer(iss).setAudience(aud).setSubject(sub).setIssuedAt(iat).setExpirationTime(exp).sign(await jose.importJWK(key));
2785
2320
  }
2786
2321
  async listPublicKeys() {
2787
2322
  const { items: keys } = await this.keyStore.listKeys();
@@ -2818,14 +2353,14 @@ class TokenFactory {
2818
2353
  seconds: this.keyDurationSeconds
2819
2354
  }).toJSDate();
2820
2355
  const promise = (async () => {
2821
- const key = await jose.JWK.generate("EC", "P-256", {
2822
- use: "sig",
2823
- kid: uuid.v4(),
2824
- alg: "ES256"
2825
- });
2826
- this.logger.info(`Created new signing key ${key.kid}`);
2827
- await this.keyStore.addKey(key.toJWK(false));
2828
- return key;
2356
+ const key = await jose.generateKeyPair("ES256");
2357
+ const publicKey = await jose.exportJWK(key.publicKey);
2358
+ const privateKey = await jose.exportJWK(key.privateKey);
2359
+ publicKey.kid = privateKey.kid = uuid.v4();
2360
+ publicKey.alg = privateKey.alg = "ES256";
2361
+ this.logger.info(`Created new signing key ${publicKey.kid}`);
2362
+ await this.keyStore.addKey(publicKey);
2363
+ return privateKey;
2829
2364
  })();
2830
2365
  this.privateKeyPromise = promise;
2831
2366
  try {
@@ -2994,6 +2529,149 @@ class KeyStores {
2994
2529
  }
2995
2530
  }
2996
2531
 
2532
+ class CatalogIdentityClient {
2533
+ constructor(options) {
2534
+ this.catalogApi = options.catalogApi;
2535
+ this.tokenManager = options.tokenManager;
2536
+ }
2537
+ async findUser(query) {
2538
+ const filter = {
2539
+ kind: "user"
2540
+ };
2541
+ for (const [key, value] of Object.entries(query.annotations)) {
2542
+ filter[`metadata.annotations.${key}`] = value;
2543
+ }
2544
+ const { token } = await this.tokenManager.getToken();
2545
+ const { items } = await this.catalogApi.getEntities({ filter }, { token });
2546
+ if (items.length !== 1) {
2547
+ if (items.length > 1) {
2548
+ throw new errors.ConflictError("User lookup resulted in multiple matches");
2549
+ } else {
2550
+ throw new errors.NotFoundError("User not found");
2551
+ }
2552
+ }
2553
+ return items[0];
2554
+ }
2555
+ async resolveCatalogMembership(query) {
2556
+ const { entityRefs, logger } = query;
2557
+ const resolvedEntityRefs = entityRefs.map((ref) => {
2558
+ try {
2559
+ const parsedRef = catalogModel.parseEntityRef(ref.toLocaleLowerCase("en-US"), {
2560
+ defaultKind: "user",
2561
+ defaultNamespace: "default"
2562
+ });
2563
+ return parsedRef;
2564
+ } catch {
2565
+ logger == null ? void 0 : logger.warn(`Failed to parse entityRef from ${ref}, ignoring`);
2566
+ return null;
2567
+ }
2568
+ }).filter((ref) => ref !== null);
2569
+ const filter = resolvedEntityRefs.map((ref) => ({
2570
+ kind: ref.kind,
2571
+ "metadata.namespace": ref.namespace,
2572
+ "metadata.name": ref.name
2573
+ }));
2574
+ const { token } = await this.tokenManager.getToken();
2575
+ const entities = await this.catalogApi.getEntities({ filter }, { token }).then((r) => r.items);
2576
+ if (entityRefs.length !== entities.length) {
2577
+ const foundEntityNames = entities.map(catalogModel.stringifyEntityRef);
2578
+ const missingEntityNames = resolvedEntityRefs.map(catalogModel.stringifyEntityRef).filter((s) => !foundEntityNames.includes(s));
2579
+ logger == null ? void 0 : logger.debug(`Entities not found for refs ${missingEntityNames.join()}`);
2580
+ }
2581
+ const memberOf = entities.flatMap((e) => {
2582
+ var _a, _b;
2583
+ return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.targetRef)) != null ? _b : [];
2584
+ });
2585
+ const newEntityRefs = [
2586
+ ...new Set(resolvedEntityRefs.map(catalogModel.stringifyEntityRef).concat(memberOf))
2587
+ ];
2588
+ logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
2589
+ return newEntityRefs;
2590
+ }
2591
+ }
2592
+
2593
+ function getEntityClaims(entity) {
2594
+ var _a, _b;
2595
+ const userRef = catalogModel.stringifyEntityRef(entity);
2596
+ const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF && r.targetRef.startsWith("group:")).map((r) => r.targetRef)) != null ? _b : [];
2597
+ return {
2598
+ sub: userRef,
2599
+ ent: [userRef, ...membershipRefs]
2600
+ };
2601
+ }
2602
+
2603
+ function getDefaultOwnershipEntityRefs(entity) {
2604
+ var _a, _b;
2605
+ const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF && r.targetRef.startsWith("group:")).map((r) => r.targetRef)) != null ? _b : [];
2606
+ return Array.from(/* @__PURE__ */ new Set([catalogModel.stringifyEntityRef(entity), ...membershipRefs]));
2607
+ }
2608
+ class CatalogAuthResolverContext {
2609
+ constructor(logger, tokenIssuer, catalogIdentityClient, catalogApi, tokenManager) {
2610
+ this.logger = logger;
2611
+ this.tokenIssuer = tokenIssuer;
2612
+ this.catalogIdentityClient = catalogIdentityClient;
2613
+ this.catalogApi = catalogApi;
2614
+ this.tokenManager = tokenManager;
2615
+ }
2616
+ static create(options) {
2617
+ const catalogIdentityClient = new CatalogIdentityClient({
2618
+ catalogApi: options.catalogApi,
2619
+ tokenManager: options.tokenManager
2620
+ });
2621
+ return new CatalogAuthResolverContext(options.logger, options.tokenIssuer, catalogIdentityClient, options.catalogApi, options.tokenManager);
2622
+ }
2623
+ async issueToken(params) {
2624
+ const token = await this.tokenIssuer.issueToken(params);
2625
+ return { token };
2626
+ }
2627
+ async findCatalogUser(query) {
2628
+ let result = void 0;
2629
+ const { token } = await this.tokenManager.getToken();
2630
+ if ("entityRef" in query) {
2631
+ const entityRef = catalogModel.parseEntityRef(query.entityRef, {
2632
+ defaultKind: "User",
2633
+ defaultNamespace: catalogModel.DEFAULT_NAMESPACE
2634
+ });
2635
+ result = await this.catalogApi.getEntityByRef(entityRef, { token });
2636
+ } else if ("annotations" in query) {
2637
+ const filter = {
2638
+ kind: "user"
2639
+ };
2640
+ for (const [key, value] of Object.entries(query.annotations)) {
2641
+ filter[`metadata.annotations.${key}`] = value;
2642
+ }
2643
+ const res = await this.catalogApi.getEntities({ filter }, { token });
2644
+ result = res.items;
2645
+ } else if ("filter" in query) {
2646
+ const res = await this.catalogApi.getEntities({ filter: query.filter }, { token });
2647
+ result = res.items;
2648
+ } else {
2649
+ throw new errors.InputError("Invalid user lookup query");
2650
+ }
2651
+ if (Array.isArray(result)) {
2652
+ if (result.length > 1) {
2653
+ throw new errors.ConflictError("User lookup resulted in multiple matches");
2654
+ }
2655
+ result = result[0];
2656
+ }
2657
+ if (!result) {
2658
+ throw new errors.NotFoundError("User not found");
2659
+ }
2660
+ return { entity: result };
2661
+ }
2662
+ async signInWithCatalogUser(query) {
2663
+ const { entity } = await this.findCatalogUser(query);
2664
+ const ownershipRefs = getDefaultOwnershipEntityRefs(entity);
2665
+ const token = await this.tokenIssuer.issueToken({
2666
+ claims: {
2667
+ sub: catalogModel.stringifyEntityRef(entity),
2668
+ ent: ownershipRefs
2669
+ }
2670
+ });
2671
+ return { token };
2672
+ }
2673
+ }
2674
+
2997
2675
  async function createRouter(options) {
2998
2676
  const {
2999
2677
  logger,
@@ -3055,7 +2733,13 @@ async function createRouter(options) {
3055
2733
  tokenManager,
3056
2734
  tokenIssuer,
3057
2735
  discovery,
3058
- catalogApi
2736
+ catalogApi,
2737
+ resolverContext: CatalogAuthResolverContext.create({
2738
+ logger,
2739
+ catalogApi,
2740
+ tokenIssuer,
2741
+ tokenManager
2742
+ })
3059
2743
  });
3060
2744
  const r = Router__default["default"]();
3061
2745
  r.get("/start", provider.start.bind(provider));
@@ -3133,12 +2817,15 @@ exports.createSamlProvider = createSamlProvider;
3133
2817
  exports.defaultAuthProviderFactories = factories;
3134
2818
  exports.encodeState = encodeState;
3135
2819
  exports.ensuresXRequestedWith = ensuresXRequestedWith;
2820
+ exports.getDefaultOwnershipEntityRefs = getDefaultOwnershipEntityRefs;
3136
2821
  exports.getEntityClaims = getEntityClaims;
3137
2822
  exports.googleEmailSignInResolver = googleEmailSignInResolver;
3138
2823
  exports.microsoftEmailSignInResolver = microsoftEmailSignInResolver;
3139
2824
  exports.oktaEmailSignInResolver = oktaEmailSignInResolver;
3140
2825
  exports.postMessageResponse = postMessageResponse;
3141
2826
  exports.prepareBackstageIdentityResponse = prepareBackstageIdentityResponse;
2827
+ exports.providers = providers;
3142
2828
  exports.readState = readState;
2829
+ exports.samlNameIdEntityNameSignInResolver = samlNameIdEntityNameSignInResolver;
3143
2830
  exports.verifyNonce = verifyNonce;
3144
2831
  //# sourceMappingURL=index.cjs.js.map