@backstage/plugin-auth-backend 0.22.4-next.0 → 0.22.4

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
@@ -15,8 +15,8 @@ var url = require('url');
15
15
  var errors = require('@backstage/errors');
16
16
  var jose = require('jose');
17
17
  var pluginAuthBackendModuleAwsAlbProvider = require('@backstage/plugin-auth-backend-module-aws-alb-provider');
18
- var passportBitbucketOauth2 = require('passport-bitbucket-oauth2');
19
- var fetch = require('node-fetch');
18
+ var pluginAuthBackendModuleBitbucketProvider = require('@backstage/plugin-auth-backend-module-bitbucket-provider');
19
+ var pluginAuthBackendModuleCloudflareAccessProvider = require('@backstage/plugin-auth-backend-module-cloudflare-access-provider');
20
20
  var pluginAuthBackendModuleGcpIapProvider = require('@backstage/plugin-auth-backend-module-gcp-iap-provider');
21
21
  var pluginAuthBackendModuleGithubProvider = require('@backstage/plugin-auth-backend-module-github-provider');
22
22
  var pluginAuthBackendModuleGitlabProvider = require('@backstage/plugin-auth-backend-module-gitlab-provider');
@@ -29,6 +29,8 @@ var pluginAuthBackendModuleOktaProvider = require('@backstage/plugin-auth-backen
29
29
  var passportOneloginOauth = require('passport-onelogin-oauth');
30
30
  var passportSaml = require('@node-saml/passport-saml');
31
31
  var passportOauth2 = require('passport-oauth2');
32
+ var fetch = require('node-fetch');
33
+ var pluginAuthBackendModuleAzureEasyauthProvider = require('@backstage/plugin-auth-backend-module-azure-easyauth-provider');
32
34
  var catalogClient = require('@backstage/catalog-client');
33
35
  var minimatch = require('minimatch');
34
36
  var catalogModel = require('@backstage/catalog-model');
@@ -219,10 +221,10 @@ const ensuresXRequestedWith = (req) => {
219
221
 
220
222
  const prepareBackstageIdentityResponse = pluginAuthNode.prepareBackstageIdentityResponse;
221
223
 
222
- var __defProp$c = Object.defineProperty;
223
- var __defNormalProp$c = (obj, key, value) => key in obj ? __defProp$c(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
224
- var __publicField$c = (obj, key, value) => {
225
- __defNormalProp$c(obj, typeof key !== "symbol" ? key + "" : key, value);
224
+ var __defProp$9 = Object.defineProperty;
225
+ var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
226
+ var __publicField$9 = (obj, key, value) => {
227
+ __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value);
226
228
  return value;
227
229
  };
228
230
  const THOUSAND_DAYS_MS = 1e3 * 24 * 60 * 60 * 1e3;
@@ -231,8 +233,8 @@ class OAuthAdapter {
231
233
  constructor(handlers, options) {
232
234
  this.handlers = handlers;
233
235
  this.options = options;
234
- __publicField$c(this, "baseCookieOptions");
235
- __publicField$c(this, "setNonceCookie", (res, nonce, cookieConfig) => {
236
+ __publicField$9(this, "baseCookieOptions");
237
+ __publicField$9(this, "setNonceCookie", (res, nonce, cookieConfig) => {
236
238
  res.cookie(`${this.options.providerId}-nonce`, nonce, {
237
239
  maxAge: TEN_MINUTES_MS,
238
240
  ...this.baseCookieOptions,
@@ -240,34 +242,34 @@ class OAuthAdapter {
240
242
  path: `${cookieConfig.path}/handler`
241
243
  });
242
244
  });
243
- __publicField$c(this, "setGrantedScopeCookie", (res, scope, cookieConfig) => {
245
+ __publicField$9(this, "setGrantedScopeCookie", (res, scope, cookieConfig) => {
244
246
  res.cookie(`${this.options.providerId}-granted-scope`, scope, {
245
247
  maxAge: THOUSAND_DAYS_MS,
246
248
  ...this.baseCookieOptions,
247
249
  ...cookieConfig
248
250
  });
249
251
  });
250
- __publicField$c(this, "getRefreshTokenFromCookie", (req) => {
252
+ __publicField$9(this, "getRefreshTokenFromCookie", (req) => {
251
253
  return req.cookies[`${this.options.providerId}-refresh-token`];
252
254
  });
253
- __publicField$c(this, "getGrantedScopeFromCookie", (req) => {
255
+ __publicField$9(this, "getGrantedScopeFromCookie", (req) => {
254
256
  return req.cookies[`${this.options.providerId}-granted-scope`];
255
257
  });
256
- __publicField$c(this, "setRefreshTokenCookie", (res, refreshToken, cookieConfig) => {
258
+ __publicField$9(this, "setRefreshTokenCookie", (res, refreshToken, cookieConfig) => {
257
259
  res.cookie(`${this.options.providerId}-refresh-token`, refreshToken, {
258
260
  maxAge: THOUSAND_DAYS_MS,
259
261
  ...this.baseCookieOptions,
260
262
  ...cookieConfig
261
263
  });
262
264
  });
263
- __publicField$c(this, "removeRefreshTokenCookie", (res, cookieConfig) => {
265
+ __publicField$9(this, "removeRefreshTokenCookie", (res, cookieConfig) => {
264
266
  res.cookie(`${this.options.providerId}-refresh-token`, "", {
265
267
  maxAge: 0,
266
268
  ...this.baseCookieOptions,
267
269
  ...cookieConfig
268
270
  });
269
271
  });
270
- __publicField$c(this, "getCookieConfig", (origin) => {
272
+ __publicField$9(this, "getCookieConfig", (origin) => {
271
273
  return this.options.cookieConfigurer({
272
274
  providerId: this.options.providerId,
273
275
  baseUrl: this.options.baseUrl,
@@ -565,21 +567,21 @@ const executeFetchUserProfileStrategy = async (providerStrategy, accessToken) =>
565
567
  });
566
568
  };
567
569
 
568
- var __defProp$b = Object.defineProperty;
569
- var __defNormalProp$b = (obj, key, value) => key in obj ? __defProp$b(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
570
- var __publicField$b = (obj, key, value) => {
571
- __defNormalProp$b(obj, typeof key !== "symbol" ? key + "" : key, value);
570
+ var __defProp$8 = Object.defineProperty;
571
+ var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
572
+ var __publicField$8 = (obj, key, value) => {
573
+ __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
572
574
  return value;
573
575
  };
574
576
  class Auth0AuthProvider {
575
577
  constructor(options) {
576
- __publicField$b(this, "_strategy");
577
- __publicField$b(this, "signInResolver");
578
- __publicField$b(this, "authHandler");
579
- __publicField$b(this, "resolverContext");
580
- __publicField$b(this, "audience");
581
- __publicField$b(this, "connection");
582
- __publicField$b(this, "connectionScope");
578
+ __publicField$8(this, "_strategy");
579
+ __publicField$8(this, "signInResolver");
580
+ __publicField$8(this, "authHandler");
581
+ __publicField$8(this, "resolverContext");
582
+ __publicField$8(this, "audience");
583
+ __publicField$8(this, "connection");
584
+ __publicField$8(this, "connectionScope");
583
585
  /**
584
586
  * Due to passport-auth0 forcing options.state = true,
585
587
  * passport-oauth2 requires express-session to be installed
@@ -588,7 +590,7 @@ class Auth0AuthProvider {
588
590
  * passport-oauth2, which is the StateStore implementation used when options.state = false,
589
591
  * allowing us to avoid using express-session in order to integrate with auth0.
590
592
  */
591
- __publicField$b(this, "store", {
593
+ __publicField$8(this, "store", {
592
594
  store(_req, cb) {
593
595
  cb(null, null);
594
596
  },
@@ -740,370 +742,34 @@ const awsAlb = createAuthProviderIntegration({
740
742
  }
741
743
  });
742
744
 
743
- var __defProp$a = Object.defineProperty;
744
- var __defNormalProp$a = (obj, key, value) => key in obj ? __defProp$a(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
745
- var __publicField$a = (obj, key, value) => {
746
- __defNormalProp$a(obj, typeof key !== "symbol" ? key + "" : key, value);
747
- return value;
748
- };
749
- class BitbucketAuthProvider {
750
- constructor(options) {
751
- __publicField$a(this, "_strategy");
752
- __publicField$a(this, "signInResolver");
753
- __publicField$a(this, "authHandler");
754
- __publicField$a(this, "resolverContext");
755
- this.signInResolver = options.signInResolver;
756
- this.authHandler = options.authHandler;
757
- this.resolverContext = options.resolverContext;
758
- this._strategy = new passportBitbucketOauth2.Strategy(
759
- {
760
- clientID: options.clientId,
761
- clientSecret: options.clientSecret,
762
- callbackURL: options.callbackUrl,
763
- passReqToCallback: false
764
- },
765
- (accessToken, refreshToken, params, fullProfile, done) => {
766
- done(
767
- void 0,
768
- {
769
- fullProfile,
770
- params,
771
- accessToken,
772
- refreshToken
773
- },
774
- {
775
- refreshToken
776
- }
777
- );
778
- }
779
- );
780
- }
781
- async start(req) {
782
- return await executeRedirectStrategy(req, this._strategy, {
783
- accessType: "offline",
784
- prompt: "consent",
785
- scope: req.scope,
786
- state: encodeState(req.state)
787
- });
788
- }
789
- async handler(req) {
790
- const { result, privateInfo } = await executeFrameHandlerStrategy(req, this._strategy);
791
- return {
792
- response: await this.handleResult(result),
793
- refreshToken: privateInfo.refreshToken
794
- };
795
- }
796
- async refresh(req) {
797
- const { accessToken, refreshToken, params } = await executeRefreshTokenStrategy(
798
- this._strategy,
799
- req.refreshToken,
800
- req.scope
801
- );
802
- const fullProfile = await executeFetchUserProfileStrategy(
803
- this._strategy,
804
- accessToken
805
- );
806
- return {
807
- response: await this.handleResult({
808
- fullProfile,
809
- params,
810
- accessToken
811
- }),
812
- refreshToken
813
- };
814
- }
815
- async handleResult(result) {
816
- result.fullProfile.avatarUrl = result.fullProfile._json.links.avatar.href;
817
- const { profile } = await this.authHandler(result, this.resolverContext);
818
- const response = {
819
- providerInfo: {
820
- idToken: result.params.id_token,
821
- accessToken: result.accessToken,
822
- scope: result.params.scope,
823
- expiresInSeconds: result.params.expires_in
824
- },
825
- profile
826
- };
827
- if (this.signInResolver) {
828
- response.backstageIdentity = await this.signInResolver(
829
- {
830
- result,
831
- profile
832
- },
833
- this.resolverContext
834
- );
835
- }
836
- return response;
837
- }
838
- }
839
745
  const bitbucket = createAuthProviderIntegration({
840
746
  create(options) {
841
- return ({ providerId, globalConfig, config, resolverContext }) => OAuthEnvironmentHandler.mapConfig(config, (envConfig) => {
842
- var _a;
843
- const clientId = envConfig.getString("clientId");
844
- const clientSecret = envConfig.getString("clientSecret");
845
- const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
846
- const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
847
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
848
- profile: makeProfileInfo(fullProfile, params.id_token)
849
- });
850
- const provider = new BitbucketAuthProvider({
851
- clientId,
852
- clientSecret,
853
- callbackUrl,
854
- signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
855
- authHandler,
856
- resolverContext
857
- });
858
- return OAuthAdapter.fromConfig(globalConfig, provider, {
859
- providerId,
860
- callbackUrl
861
- });
747
+ var _a;
748
+ return pluginAuthNode.createOAuthProviderFactory({
749
+ authenticator: pluginAuthBackendModuleBitbucketProvider.bitbucketAuthenticator,
750
+ profileTransform: adaptLegacyOAuthHandler(options == null ? void 0 : options.authHandler),
751
+ signInResolver: adaptLegacyOAuthSignInResolver((_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver)
862
752
  });
863
753
  },
864
- resolvers: {
865
- /**
866
- * Looks up the user by matching their username to the `bitbucket.org/username` annotation.
867
- */
868
- usernameMatchingUserEntityAnnotation() {
869
- return async (info, ctx) => {
870
- const { result } = info;
871
- if (!result.fullProfile.username) {
872
- throw new Error("Bitbucket profile contained no Username");
873
- }
874
- return ctx.signInWithCatalogUser({
875
- annotations: {
876
- "bitbucket.org/username": result.fullProfile.username
877
- }
878
- });
879
- };
880
- },
881
- /**
882
- * Looks up the user by matching their user ID to the `bitbucket.org/user-id` annotation.
883
- */
884
- userIdMatchingUserEntityAnnotation() {
885
- return async (info, ctx) => {
886
- const { result } = info;
887
- if (!result.fullProfile.id) {
888
- throw new Error("Bitbucket profile contained no User ID");
889
- }
890
- return ctx.signInWithCatalogUser({
891
- annotations: {
892
- "bitbucket.org/user-id": result.fullProfile.id
893
- }
894
- });
895
- };
896
- }
897
- }
754
+ resolvers: adaptOAuthSignInResolverToLegacy({
755
+ userIdMatchingUserEntityAnnotation: pluginAuthBackendModuleBitbucketProvider.bitbucketSignInResolvers.userIdMatchingUserEntityAnnotation(),
756
+ usernameMatchingUserEntityAnnotation: pluginAuthBackendModuleBitbucketProvider.bitbucketSignInResolvers.usernameMatchingUserEntityAnnotation()
757
+ })
898
758
  });
899
759
 
900
- const commonByEmailLocalPartResolver = async (info, ctx) => {
901
- const { profile } = info;
902
- if (!profile.email) {
903
- throw new Error("Login failed, user profile does not contain an email");
904
- }
905
- const [localPart] = profile.email.split("@");
906
- return ctx.signInWithCatalogUser({
907
- entityRef: { name: localPart }
908
- });
909
- };
910
- const commonByEmailResolver = async (info, ctx) => {
911
- const { profile } = info;
912
- if (!profile.email) {
913
- throw new Error("Login failed, user profile does not contain an email");
914
- }
915
- return ctx.signInWithCatalogUser({
916
- filter: {
917
- "spec.profile.email": profile.email
918
- }
919
- });
920
- };
921
-
922
- var __defProp$9 = Object.defineProperty;
923
- var __defNormalProp$9 = (obj, key, value) => key in obj ? __defProp$9(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
924
- var __publicField$9 = (obj, key, value) => {
925
- __defNormalProp$9(obj, typeof key !== "symbol" ? key + "" : key, value);
926
- return value;
927
- };
928
- const CF_JWT_HEADER = "cf-access-jwt-assertion";
929
- const COOKIE_AUTH_NAME = "CF_Authorization";
930
- const CACHE_PREFIX = "providers/cloudflare-access/profile-v1";
931
- class CloudflareAccessAuthProvider {
932
- constructor(options) {
933
- __publicField$9(this, "teamName");
934
- __publicField$9(this, "serviceTokens");
935
- __publicField$9(this, "resolverContext");
936
- __publicField$9(this, "authHandler");
937
- __publicField$9(this, "signInResolver");
938
- __publicField$9(this, "jwtKeySet");
939
- __publicField$9(this, "cache");
940
- this.teamName = options.teamName;
941
- this.serviceTokens = options.serviceTokens;
942
- this.authHandler = options.authHandler;
943
- this.signInResolver = options.signInResolver;
944
- this.resolverContext = options.resolverContext;
945
- this.jwtKeySet = jose.createRemoteJWKSet(
946
- new URL(
947
- `https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/certs`
948
- )
949
- );
950
- this.cache = options.cache;
951
- }
952
- frameHandler() {
953
- return Promise.resolve();
954
- }
955
- async refresh(req, res) {
956
- const result = await this.getResult(req);
957
- const response = await this.handleResult(result);
958
- res.json(response);
959
- }
960
- start() {
961
- return Promise.resolve();
962
- }
963
- async getIdentityProfile(jwt) {
964
- const headers = new fetch.Headers();
965
- headers.set(CF_JWT_HEADER, jwt);
966
- headers.set("cookie", `${COOKIE_AUTH_NAME}=${jwt}`);
967
- try {
968
- const res = await fetch__default.default(
969
- `https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/get-identity`,
970
- { headers }
971
- );
972
- if (!res.ok) {
973
- throw await errors.ResponseError.fromResponse(res);
974
- }
975
- const cfIdentity = await res.json();
976
- return cfIdentity;
977
- } catch (err) {
978
- throw new errors.ForwardedError("getIdentityProfile failed", err);
979
- }
980
- }
981
- async getResult(req) {
982
- var _a, _b;
983
- let jwt = req.header(CF_JWT_HEADER);
984
- if (!jwt) {
985
- jwt = req.cookies.CF_Authorization;
986
- }
987
- if (!jwt) {
988
- throw new errors.AuthenticationError(
989
- `Missing ${CF_JWT_HEADER} from Cloudflare Access`
990
- );
991
- }
992
- const verifyResult = await jose.jwtVerify(jwt, this.jwtKeySet, {
993
- issuer: `https://${this.teamName}.cloudflareaccess.com`
994
- });
995
- const isServiceToken = !verifyResult.payload.sub;
996
- const subject = isServiceToken ? verifyResult.payload.common_name : verifyResult.payload.sub;
997
- if (!subject) {
998
- throw new errors.AuthenticationError(
999
- `Missing both sub and common_name from Cloudflare Access JWT`
1000
- );
1001
- }
1002
- const serviceToken = this.serviceTokens.find((st) => st.token === subject);
1003
- if (isServiceToken && !serviceToken) {
1004
- throw new errors.AuthenticationError(
1005
- `${subject} is not a permitted Service Token.`
1006
- );
1007
- }
1008
- const cacheKey = `${CACHE_PREFIX}/${subject}`;
1009
- const cfAccessResultStr = await ((_a = this.cache) == null ? void 0 : _a.get(cacheKey));
1010
- if (typeof cfAccessResultStr === "string") {
1011
- const result = JSON.parse(cfAccessResultStr);
1012
- return {
1013
- ...result,
1014
- token: jwt
1015
- };
1016
- }
1017
- const claims = verifyResult.payload;
1018
- try {
1019
- let cfIdentity;
1020
- if (serviceToken) {
1021
- cfIdentity = {
1022
- id: subject,
1023
- name: "Bot",
1024
- email: serviceToken.subject,
1025
- groups: []
1026
- };
1027
- } else {
1028
- cfIdentity = await this.getIdentityProfile(jwt);
1029
- }
1030
- const cfAccessResult = {
1031
- claims,
1032
- cfIdentity,
1033
- expiresInSeconds: claims.exp - claims.iat
1034
- };
1035
- (_b = this.cache) == null ? void 0 : _b.set(cacheKey, JSON.stringify(cfAccessResult));
1036
- return {
1037
- ...cfAccessResult,
1038
- token: jwt
1039
- };
1040
- } catch (err) {
1041
- throw new errors.ForwardedError(
1042
- "Failed to populate access identity information",
1043
- err
1044
- );
1045
- }
1046
- }
1047
- async handleResult(result) {
1048
- const { profile } = await this.authHandler(result, this.resolverContext);
1049
- const backstageIdentity = await this.signInResolver(
1050
- {
1051
- result,
1052
- profile
1053
- },
1054
- this.resolverContext
1055
- );
1056
- return {
1057
- providerInfo: {
1058
- expiresInSeconds: result.expiresInSeconds,
1059
- claims: result.claims,
1060
- cfAccessIdentityProfile: result.cfIdentity
1061
- },
1062
- backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
1063
- profile
1064
- };
1065
- }
1066
- }
1067
760
  const cfAccess = createAuthProviderIntegration({
1068
761
  create(options) {
1069
- return ({ config, resolverContext }) => {
1070
- const teamName = config.getString("teamName");
1071
- const serviceTokensConfig = config.getOptionalConfigArray("serviceTokens");
1072
- const serviceTokens = (serviceTokensConfig == null ? void 0 : serviceTokensConfig.map((cfg) => {
1073
- return {
1074
- token: cfg.getString("token"),
1075
- subject: cfg.getString("subject")
1076
- };
1077
- })) || [];
1078
- if (!options.signIn.resolver) {
1079
- throw new Error(
1080
- "SignInResolver is required to use this authentication provider"
1081
- );
1082
- }
1083
- const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ claims, cfIdentity }) => {
1084
- return {
1085
- profile: {
1086
- email: claims.email,
1087
- displayName: cfIdentity.name
1088
- }
1089
- };
1090
- };
1091
- return new CloudflareAccessAuthProvider({
1092
- teamName,
1093
- serviceTokens,
1094
- signInResolver: options == null ? void 0 : options.signIn.resolver,
1095
- authHandler,
1096
- resolverContext,
1097
- ...options.cache && { cache: options.cache }
1098
- });
1099
- };
762
+ var _a;
763
+ return pluginAuthNode.createProxyAuthProviderFactory({
764
+ authenticator: pluginAuthBackendModuleCloudflareAccessProvider.createCloudflareAccessAuthenticator({
765
+ cache: options.cache
766
+ }),
767
+ profileTransform: options == null ? void 0 : options.authHandler,
768
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver,
769
+ signInResolverFactories: pluginAuthBackendModuleCloudflareAccessProvider.cloudflareAccessSignInResolvers
770
+ });
1100
771
  },
1101
- resolvers: {
1102
- /**
1103
- * Looks up the user by matching their email to the entity email.
1104
- */
1105
- emailMatchingUserEntityProfileEmail: () => commonByEmailResolver
1106
- }
772
+ resolvers: pluginAuthBackendModuleCloudflareAccessProvider.cloudflareAccessSignInResolvers
1107
773
  });
1108
774
 
1109
775
  const gcpIap = createAuthProviderIntegration({
@@ -1236,6 +902,28 @@ const oauth2Proxy = createAuthProviderIntegration({
1236
902
  }
1237
903
  });
1238
904
 
905
+ const commonByEmailLocalPartResolver = async (info, ctx) => {
906
+ const { profile } = info;
907
+ if (!profile.email) {
908
+ throw new Error("Login failed, user profile does not contain an email");
909
+ }
910
+ const [localPart] = profile.email.split("@");
911
+ return ctx.signInWithCatalogUser({
912
+ entityRef: { name: localPart }
913
+ });
914
+ };
915
+ const commonByEmailResolver = async (info, ctx) => {
916
+ const { profile } = info;
917
+ if (!profile.email) {
918
+ throw new Error("Login failed, user profile does not contain an email");
919
+ }
920
+ return ctx.signInWithCatalogUser({
921
+ filter: {
922
+ "spec.profile.email": profile.email
923
+ }
924
+ });
925
+ };
926
+
1239
927
  const oidc = createAuthProviderIntegration({
1240
928
  create(options) {
1241
929
  var _a;
@@ -1302,18 +990,18 @@ const okta = createAuthProviderIntegration({
1302
990
  }
1303
991
  });
1304
992
 
1305
- var __defProp$8 = Object.defineProperty;
1306
- var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1307
- var __publicField$8 = (obj, key, value) => {
1308
- __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
993
+ var __defProp$7 = Object.defineProperty;
994
+ var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
995
+ var __publicField$7 = (obj, key, value) => {
996
+ __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
1309
997
  return value;
1310
998
  };
1311
999
  class OneLoginProvider {
1312
1000
  constructor(options) {
1313
- __publicField$8(this, "_strategy");
1314
- __publicField$8(this, "signInResolver");
1315
- __publicField$8(this, "authHandler");
1316
- __publicField$8(this, "resolverContext");
1001
+ __publicField$7(this, "_strategy");
1002
+ __publicField$7(this, "signInResolver");
1003
+ __publicField$7(this, "authHandler");
1004
+ __publicField$7(this, "resolverContext");
1317
1005
  this.signInResolver = options.signInResolver;
1318
1006
  this.authHandler = options.authHandler;
1319
1007
  this.resolverContext = options.resolverContext;
@@ -1427,19 +1115,19 @@ const onelogin = createAuthProviderIntegration({
1427
1115
  }
1428
1116
  });
1429
1117
 
1430
- var __defProp$7 = Object.defineProperty;
1431
- var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1432
- var __publicField$7 = (obj, key, value) => {
1433
- __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
1118
+ var __defProp$6 = Object.defineProperty;
1119
+ var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1120
+ var __publicField$6 = (obj, key, value) => {
1121
+ __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
1434
1122
  return value;
1435
1123
  };
1436
1124
  class SamlAuthProvider {
1437
1125
  constructor(options) {
1438
- __publicField$7(this, "strategy");
1439
- __publicField$7(this, "signInResolver");
1440
- __publicField$7(this, "authHandler");
1441
- __publicField$7(this, "resolverContext");
1442
- __publicField$7(this, "appUrl");
1126
+ __publicField$6(this, "strategy");
1127
+ __publicField$6(this, "signInResolver");
1128
+ __publicField$6(this, "authHandler");
1129
+ __publicField$6(this, "resolverContext");
1130
+ __publicField$6(this, "appUrl");
1443
1131
  this.appUrl = options.appUrl;
1444
1132
  this.signInResolver = options.signInResolver;
1445
1133
  this.authHandler = options.authHandler;
@@ -1543,19 +1231,19 @@ const saml = createAuthProviderIntegration({
1543
1231
  }
1544
1232
  });
1545
1233
 
1546
- var __defProp$6 = Object.defineProperty;
1547
- var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1548
- var __publicField$6 = (obj, key, value) => {
1549
- __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
1234
+ var __defProp$5 = Object.defineProperty;
1235
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1236
+ var __publicField$5 = (obj, key, value) => {
1237
+ __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
1550
1238
  return value;
1551
1239
  };
1552
1240
  class BitbucketServerAuthProvider {
1553
1241
  constructor(options) {
1554
- __publicField$6(this, "signInResolver");
1555
- __publicField$6(this, "authHandler");
1556
- __publicField$6(this, "resolverContext");
1557
- __publicField$6(this, "strategy");
1558
- __publicField$6(this, "host");
1242
+ __publicField$5(this, "signInResolver");
1243
+ __publicField$5(this, "authHandler");
1244
+ __publicField$5(this, "resolverContext");
1245
+ __publicField$5(this, "strategy");
1246
+ __publicField$5(this, "host");
1559
1247
  this.signInResolver = options.signInResolver;
1560
1248
  this.authHandler = options.authHandler;
1561
1249
  this.resolverContext = options.resolverContext;
@@ -1720,112 +1408,16 @@ const bitbucketServer = createAuthProviderIntegration({
1720
1408
  }
1721
1409
  });
1722
1410
 
1723
- var __defProp$5 = Object.defineProperty;
1724
- var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1725
- var __publicField$5 = (obj, key, value) => {
1726
- __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
1727
- return value;
1728
- };
1729
- const ID_TOKEN_HEADER = "x-ms-token-aad-id-token";
1730
- const ACCESS_TOKEN_HEADER = "x-ms-token-aad-access-token";
1731
- class EasyAuthAuthProvider {
1732
- constructor(options) {
1733
- __publicField$5(this, "resolverContext");
1734
- __publicField$5(this, "authHandler");
1735
- __publicField$5(this, "signInResolver");
1736
- this.authHandler = options.authHandler;
1737
- this.signInResolver = options.signInResolver;
1738
- this.resolverContext = options.resolverContext;
1739
- }
1740
- frameHandler() {
1741
- return Promise.resolve(void 0);
1742
- }
1743
- async refresh(req, res) {
1744
- const result = await this.getResult(req);
1745
- const response = await this.handleResult(result);
1746
- res.json(response);
1747
- }
1748
- start() {
1749
- return Promise.resolve(void 0);
1750
- }
1751
- async getResult(req) {
1752
- const idToken = req.header(ID_TOKEN_HEADER);
1753
- const accessToken = req.header(ACCESS_TOKEN_HEADER);
1754
- if (idToken === void 0) {
1755
- throw new errors.AuthenticationError(`Missing ${ID_TOKEN_HEADER} header`);
1756
- }
1757
- return {
1758
- fullProfile: this.idTokenToProfile(idToken),
1759
- accessToken
1760
- };
1761
- }
1762
- idTokenToProfile(idToken) {
1763
- const claims = jose.decodeJwt(idToken);
1764
- if (claims.ver !== "2.0") {
1765
- throw new Error("id_token is not version 2.0 ");
1766
- }
1767
- return {
1768
- id: claims.oid,
1769
- displayName: claims.name,
1770
- provider: "easyauth",
1771
- emails: [{ value: claims.email }],
1772
- username: claims.preferred_username
1773
- };
1774
- }
1775
- async handleResult(result) {
1776
- const { profile } = await this.authHandler(result, this.resolverContext);
1777
- const backstageIdentity = await this.signInResolver(
1778
- {
1779
- result,
1780
- profile
1781
- },
1782
- this.resolverContext
1783
- );
1784
- return {
1785
- providerInfo: {
1786
- accessToken: result.accessToken
1787
- },
1788
- backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
1789
- profile
1790
- };
1791
- }
1792
- }
1793
1411
  const easyAuth = createAuthProviderIntegration({
1794
1412
  create(options) {
1795
- return ({ resolverContext }) => {
1796
- var _a;
1797
- validateAppServiceConfiguration(process.env);
1798
- if ((options == null ? void 0 : options.signIn.resolver) === void 0) {
1799
- throw new Error(
1800
- "SignInResolver is required to use this authentication provider"
1801
- );
1802
- }
1803
- const authHandler = (_a = options.authHandler) != null ? _a : async ({ fullProfile }) => ({
1804
- profile: makeProfileInfo(fullProfile)
1805
- });
1806
- return new EasyAuthAuthProvider({
1807
- signInResolver: options.signIn.resolver,
1808
- authHandler,
1809
- resolverContext
1810
- });
1811
- };
1413
+ var _a;
1414
+ return pluginAuthNode.createProxyAuthProviderFactory({
1415
+ authenticator: pluginAuthBackendModuleAzureEasyauthProvider.azureEasyAuthAuthenticator,
1416
+ profileTransform: options == null ? void 0 : options.authHandler,
1417
+ signInResolver: (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver
1418
+ });
1812
1419
  }
1813
1420
  });
1814
- function validateAppServiceConfiguration(env) {
1815
- var _a, _b, _c;
1816
- if (env.WEBSITE_SKU === void 0) {
1817
- throw new Error("Backstage is not running on Azure App Services");
1818
- }
1819
- if (((_a = env.WEBSITE_AUTH_ENABLED) == null ? void 0 : _a.toLowerCase()) !== "true") {
1820
- throw new Error("Azure App Services does not have authentication enabled");
1821
- }
1822
- if (((_b = env.WEBSITE_AUTH_DEFAULT_PROVIDER) == null ? void 0 : _b.toLowerCase()) !== "azureactivedirectory") {
1823
- throw new Error("Authentication provider is not Entra ID");
1824
- }
1825
- if (((_c = process.env.WEBSITE_AUTH_TOKEN_STORE) == null ? void 0 : _c.toLowerCase()) !== "true") {
1826
- throw new Error("Token Store is not enabled");
1827
- }
1828
- }
1829
1421
 
1830
1422
  const providers = Object.freeze({
1831
1423
  atlassian,
@@ -2156,7 +1748,7 @@ function createOriginFilter(config) {
2156
1748
  }
2157
1749
 
2158
1750
  function bindOidcRouter(targetRouter, options) {
2159
- const { baseUrl, tokenIssuer } = options;
1751
+ const { baseUrl, auth, tokenIssuer } = options;
2160
1752
  const router = Router__default.default();
2161
1753
  targetRouter.use(router);
2162
1754
  const config = {
@@ -2180,7 +1772,7 @@ function bindOidcRouter(targetRouter, options) {
2180
1772
  ],
2181
1773
  scopes_supported: ["openid"],
2182
1774
  token_endpoint_auth_methods_supported: [],
2183
- claims_supported: ["sub"],
1775
+ claims_supported: ["sub", "ent"],
2184
1776
  grant_types_supported: []
2185
1777
  };
2186
1778
  router.get("/.well-known/openid-configuration", (_req, res) => {
@@ -2193,8 +1785,31 @@ function bindOidcRouter(targetRouter, options) {
2193
1785
  router.get("/v1/token", (_req, res) => {
2194
1786
  res.status(501).send("Not Implemented");
2195
1787
  });
2196
- router.get("/v1/userinfo", (_req, res) => {
2197
- res.status(501).send("Not Implemented");
1788
+ router.get("/v1/userinfo", async (req, res) => {
1789
+ var _a;
1790
+ const matches = (_a = req.headers.authorization) == null ? void 0 : _a.match(/^Bearer[ ]+(\S+)$/i);
1791
+ const token = matches == null ? void 0 : matches[1];
1792
+ if (!token) {
1793
+ throw new errors.AuthenticationError("No token provided");
1794
+ }
1795
+ const credentials = await auth.authenticate(token, {
1796
+ allowLimitedAccess: true
1797
+ });
1798
+ if (!auth.isPrincipal(credentials, "user")) {
1799
+ throw new errors.InputError(
1800
+ "Userinfo endpoint must be called with a token that represents a user principal"
1801
+ );
1802
+ }
1803
+ const { sub: userEntityRef, ent: ownershipEntityRefs = [] } = jose.decodeJwt(token);
1804
+ if (typeof userEntityRef !== "string") {
1805
+ throw new Error("Invalid user token, user entity ref must be a string");
1806
+ }
1807
+ if (!Array.isArray(ownershipEntityRefs) || ownershipEntityRefs.some((ref) => typeof ref !== "string")) {
1808
+ throw new Error(
1809
+ "Invalid user token, ownership entity refs must be an array of strings"
1810
+ );
1811
+ }
1812
+ res.json({ sub: userEntityRef, ent: ownershipEntityRefs });
2198
1813
  });
2199
1814
  }
2200
1815
 
@@ -2225,8 +1840,8 @@ class TokenFactory {
2225
1840
  async issueToken(params) {
2226
1841
  const key = await this.getKey();
2227
1842
  const iss = this.issuer;
2228
- const { sub, ent, ...additionalClaims } = params.claims;
2229
- const aud = "backstage";
1843
+ const { sub, ent = [sub], ...additionalClaims } = params.claims;
1844
+ const aud = pluginAuthNode.tokenTypes.user.audClaim;
2230
1845
  const iat = Math.floor(Date.now() / MS_IN_S$1);
2231
1846
  const exp = iat + this.keyDurationSeconds;
2232
1847
  try {
@@ -2236,12 +1851,35 @@ class TokenFactory {
2236
1851
  '"sub" claim provided by the auth resolver is not a valid EntityRef.'
2237
1852
  );
2238
1853
  }
2239
- this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2240
1854
  if (!key.alg) {
2241
1855
  throw new errors.AuthenticationError("No algorithm was provided in the key");
2242
1856
  }
2243
- const claims = { ...additionalClaims, iss, sub, ent, aud, iat, exp };
2244
- const token = await new jose.SignJWT(claims).setProtectedHeader({ alg: key.alg, kid: key.kid }).setIssuer(iss).setAudience(aud).setSubject(sub).setIssuedAt(iat).setExpirationTime(exp).sign(await jose.importJWK(key));
1857
+ this.logger.info(`Issuing token for ${sub}, with entities ${ent}`);
1858
+ const signingKey = await jose.importJWK(key);
1859
+ const uip = await this.createUserIdentityClaim({
1860
+ header: {
1861
+ typ: pluginAuthNode.tokenTypes.limitedUser.typParam,
1862
+ alg: key.alg,
1863
+ kid: key.kid
1864
+ },
1865
+ payload: { sub, ent, iat, exp },
1866
+ key: signingKey
1867
+ });
1868
+ const claims = {
1869
+ ...additionalClaims,
1870
+ iss,
1871
+ sub,
1872
+ ent,
1873
+ aud,
1874
+ iat,
1875
+ exp,
1876
+ uip
1877
+ };
1878
+ const token = await new jose.SignJWT(claims).setProtectedHeader({
1879
+ typ: pluginAuthNode.tokenTypes.user.typParam,
1880
+ alg: key.alg,
1881
+ kid: key.kid
1882
+ }).sign(signingKey);
2245
1883
  if (token.length > MAX_TOKEN_LENGTH) {
2246
1884
  throw new Error(
2247
1885
  `Failed to issue a new user token. The resulting token is excessively large, with either too many ownership claims or too large custom claims. You likely have a bug either in the sign-in resolver or catalog data. The following claims were requested: '${JSON.stringify(
@@ -2308,6 +1946,26 @@ class TokenFactory {
2308
1946
  }
2309
1947
  return promise;
2310
1948
  }
1949
+ // Creates a string claim that can be used as part of reconstructing a limited
1950
+ // user token. The output of this function is only the signature part of a
1951
+ // JWS.
1952
+ async createUserIdentityClaim(options) {
1953
+ const header = {
1954
+ typ: options.header.typ,
1955
+ alg: options.header.alg,
1956
+ ...options.header.kid ? { kid: options.header.kid } : {}
1957
+ };
1958
+ const payload = {
1959
+ sub: options.payload.sub,
1960
+ ent: options.payload.ent,
1961
+ iat: options.payload.iat,
1962
+ exp: options.payload.exp
1963
+ };
1964
+ const jws = await new jose.GeneralSign(
1965
+ new TextEncoder().encode(JSON.stringify(payload))
1966
+ ).addSignature(options.key).setProtectedHeader(header).done().sign();
1967
+ return jws.signatures[0].signature;
1968
+ }
2311
1969
  }
2312
1970
 
2313
1971
  const TABLE = "signing_keys";
@@ -2807,6 +2465,7 @@ async function createRouter(options) {
2807
2465
  httpAuth
2808
2466
  });
2809
2467
  bindOidcRouter(router, {
2468
+ auth,
2810
2469
  tokenIssuer,
2811
2470
  baseUrl: authUrl
2812
2471
  });
@@ -2839,6 +2498,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2839
2498
  database: backendPluginApi.coreServices.database,
2840
2499
  discovery: backendPluginApi.coreServices.discovery,
2841
2500
  tokenManager: backendPluginApi.coreServices.tokenManager,
2501
+ auth: backendPluginApi.coreServices.auth,
2502
+ httpAuth: backendPluginApi.coreServices.httpAuth,
2842
2503
  catalogApi: alpha.catalogServiceRef
2843
2504
  },
2844
2505
  async init({
@@ -2848,6 +2509,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2848
2509
  database,
2849
2510
  discovery,
2850
2511
  tokenManager,
2512
+ auth,
2513
+ httpAuth,
2851
2514
  catalogApi
2852
2515
  }) {
2853
2516
  const router = await createRouter({
@@ -2856,6 +2519,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2856
2519
  database,
2857
2520
  discovery,
2858
2521
  tokenManager,
2522
+ auth,
2523
+ httpAuth,
2859
2524
  catalogApi,
2860
2525
  providerFactories: Object.fromEntries(providers),
2861
2526
  disableDefaultProviderFactories: true