@backstage/plugin-auth-backend 0.15.0-next.1 → 0.15.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/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 9d4040777e: **BREAKING**: Removed all directly exported auth provider factories, option types, and sign-in resolvers. For example: `AwsAlbProviderOptions`, `bitbucketUserIdSignInResolver`, `createGithubProvider`. These are all still accessible via the `providers` export. For example, use `providers.github.create()` rather than `createGithubProvider()`, and `providers.bitbucket.resolvers.userIdMatchingUserEntityAnnotation()` rather than `bitbucketUserIdSignInResolver`.
8
+
9
+ **BREAKING**: Removed the exported `AuthProviderFactoryOptions` type as well as the deprecated option fields of the `AuthProviderFactory` callback. This includes the `tokenManager`, `tokenIssuer`, `discovery`, and `catalogApi` fields. Existing usage of these should be replaced with the new utilities in the `resolverContext` field. The deprecated `TokenIssuer` type is now also removed, since it is no longer used.
10
+
11
+ **BREAKING**: Removed `getEntityClaims`, use `getDefaultOwnershipEntityRefs` instead.
12
+
13
+ **DEPRECATION**: Deprecated `AtlassianAuthProvider` as it was unintentionally exported.
14
+
15
+ - fe8e025af5: Allowed post method on /refresh path
16
+
17
+ ### Patch Changes
18
+
19
+ - 3cedfd8365: add Cloudflare Access auth provider to auth-backend
20
+ - f2cf79d62e: Added an option for the auth backend router to select the algorithm for the JWT token signing keys
21
+ - 8e03db907a: Auth provider now also export createAuthProviderIntegration
22
+ - a70869e775: Updated dependency `msw` to `^0.43.0`.
23
+ - 4e9a90e307: Updated dependency `luxon` to `^3.0.0`.
24
+ - 8006d0f9bf: Updated dependency `msw` to `^0.44.0`.
25
+ - 679b32172e: Updated dependency `knex` to `^2.0.0`.
26
+ - 859346bfbb: Updated dependency `google-auth-library` to `^8.0.0`.
27
+ - 3a014730dc: Add new config option for okta auth server and IDP
28
+ - Updated dependencies
29
+ - @backstage/backend-common@0.14.1
30
+ - @backstage/catalog-model@1.1.0
31
+ - @backstage/catalog-client@1.0.4
32
+ - @backstage/plugin-auth-node@0.2.3
33
+ - @backstage/errors@1.1.0
34
+
35
+ ## 0.15.0-next.3
36
+
37
+ ### Minor Changes
38
+
39
+ - fe8e025af5: Allowed post method on /refresh path
40
+
41
+ ### Patch Changes
42
+
43
+ - a70869e775: Updated dependency `msw` to `^0.43.0`.
44
+ - 4e9a90e307: Updated dependency `luxon` to `^3.0.0`.
45
+ - 3a014730dc: Add new config option for okta auth server and IDP
46
+ - Updated dependencies
47
+ - @backstage/backend-common@0.14.1-next.3
48
+ - @backstage/catalog-client@1.0.4-next.2
49
+ - @backstage/plugin-auth-node@0.2.3-next.2
50
+ - @backstage/catalog-model@1.1.0-next.3
51
+
52
+ ## 0.15.0-next.2
53
+
54
+ ### Patch Changes
55
+
56
+ - 8e03db907a: Auth provider now also export createAuthProviderIntegration
57
+ - 679b32172e: Updated dependency `knex` to `^2.0.0`.
58
+ - Updated dependencies
59
+ - @backstage/catalog-model@1.1.0-next.2
60
+ - @backstage/backend-common@0.14.1-next.2
61
+
3
62
  ## 0.15.0-next.1
4
63
 
5
64
  ### Minor Changes
package/config.d.ts CHANGED
@@ -116,6 +116,9 @@ export interface Config {
116
116
  issuer?: string;
117
117
  region: string;
118
118
  };
119
+ cfaccess?: {
120
+ teamName: string;
121
+ };
119
122
  };
120
123
  };
121
124
  }
package/dist/index.cjs.js CHANGED
@@ -22,7 +22,7 @@ var passportGoogleOauth20 = require('passport-google-oauth20');
22
22
  var passportMicrosoft = require('passport-microsoft');
23
23
  var pluginAuthNode = require('@backstage/plugin-auth-node');
24
24
  var openidClient = require('openid-client');
25
- var passportOktaOauth = require('passport-okta-oauth');
25
+ var passportOktaOauth = require('@davidzemon/passport-okta-oauth');
26
26
  var passportOneloginOauth = require('passport-onelogin-oauth');
27
27
  var passportSaml = require('passport-saml');
28
28
  var catalogClient = require('@backstage/catalog-client');
@@ -995,6 +995,143 @@ const bitbucket = createAuthProviderIntegration({
995
995
  }
996
996
  });
997
997
 
998
+ const commonByEmailLocalPartResolver = async (info, ctx) => {
999
+ const { profile } = info;
1000
+ if (!profile.email) {
1001
+ throw new Error("Login failed, user profile does not contain an email");
1002
+ }
1003
+ const [localPart] = profile.email.split("@");
1004
+ return ctx.signInWithCatalogUser({
1005
+ entityRef: { name: localPart }
1006
+ });
1007
+ };
1008
+ const commonByEmailResolver = async (info, ctx) => {
1009
+ const { profile } = info;
1010
+ if (!profile.email) {
1011
+ throw new Error("Login failed, user profile does not contain an email");
1012
+ }
1013
+ return ctx.signInWithCatalogUser({
1014
+ filter: {
1015
+ "spec.profile.email": profile.email
1016
+ }
1017
+ });
1018
+ };
1019
+
1020
+ const CF_JWT_HEADER = "cf-access-jwt-assertion";
1021
+ const COOKIE_AUTH_NAME = "CF_Authorization";
1022
+ const CACHE_PREFIX = "providers/cloudflare-access/profile-v1";
1023
+ class CloudflareAccessAuthProvider {
1024
+ constructor(options) {
1025
+ this.teamName = options.teamName;
1026
+ this.authHandler = options.authHandler;
1027
+ this.signInResolver = options.signInResolver;
1028
+ this.resolverContext = options.resolverContext;
1029
+ this.jwtKeySet = jose.createRemoteJWKSet(new URL(`https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/certs`));
1030
+ this.cache = options.cache;
1031
+ }
1032
+ frameHandler() {
1033
+ return Promise.resolve();
1034
+ }
1035
+ async refresh(req, res) {
1036
+ const result = await this.getResult(req);
1037
+ const response = await this.handleResult(result);
1038
+ res.json(response);
1039
+ }
1040
+ start() {
1041
+ return Promise.resolve();
1042
+ }
1043
+ async getIdentityProfile(jwt) {
1044
+ const headers = new fetch.Headers();
1045
+ headers.set(CF_JWT_HEADER, jwt);
1046
+ headers.set("cookie", `${COOKIE_AUTH_NAME}=${jwt}`);
1047
+ try {
1048
+ const res = await fetch__default["default"](`https://${this.teamName}.cloudflareaccess.com/cdn-cgi/access/get-identity`, { headers });
1049
+ if (!res.ok) {
1050
+ throw errors.ResponseError.fromResponse(res);
1051
+ }
1052
+ const cfIdentity = await res.json();
1053
+ return cfIdentity;
1054
+ } catch (err) {
1055
+ throw new errors.ForwardedError("getIdentityProfile failed", err);
1056
+ }
1057
+ }
1058
+ async getResult(req) {
1059
+ var _a, _b;
1060
+ let jwt = req.header(CF_JWT_HEADER);
1061
+ if (!jwt) {
1062
+ jwt = req.cookies.CF_Authorization;
1063
+ }
1064
+ if (!jwt) {
1065
+ throw new errors.AuthenticationError(`Missing ${CF_JWT_HEADER} from Cloudflare Access`);
1066
+ }
1067
+ const verifyResult = await jose.jwtVerify(jwt, this.jwtKeySet, {
1068
+ issuer: `https://${this.teamName}.cloudflareaccess.com`
1069
+ });
1070
+ const sub = verifyResult.payload.sub;
1071
+ const cfAccessResultStr = await ((_a = this.cache) == null ? void 0 : _a.get(`${CACHE_PREFIX}/${sub}`));
1072
+ if (typeof cfAccessResultStr === "string") {
1073
+ return JSON.parse(cfAccessResultStr);
1074
+ }
1075
+ const claims = verifyResult.payload;
1076
+ try {
1077
+ const cfIdentity = await this.getIdentityProfile(jwt);
1078
+ const cfAccessResult = {
1079
+ claims,
1080
+ cfIdentity,
1081
+ expiresInSeconds: claims.exp - claims.iat
1082
+ };
1083
+ (_b = this.cache) == null ? void 0 : _b.set(`${CACHE_PREFIX}/${sub}`, JSON.stringify(cfAccessResult));
1084
+ return cfAccessResult;
1085
+ } catch (err) {
1086
+ throw new errors.ForwardedError("Failed to populate access identity information", err);
1087
+ }
1088
+ }
1089
+ async handleResult(result) {
1090
+ const { profile } = await this.authHandler(result, this.resolverContext);
1091
+ const backstageIdentity = await this.signInResolver({
1092
+ result,
1093
+ profile
1094
+ }, this.resolverContext);
1095
+ return {
1096
+ providerInfo: {
1097
+ expiresInSeconds: result.expiresInSeconds,
1098
+ claims: result.claims,
1099
+ cfAccessIdentityProfile: result.cfIdentity
1100
+ },
1101
+ backstageIdentity: prepareBackstageIdentityResponse(backstageIdentity),
1102
+ profile
1103
+ };
1104
+ }
1105
+ }
1106
+ const cfAccess = createAuthProviderIntegration({
1107
+ create(options) {
1108
+ return ({ config, resolverContext }) => {
1109
+ const teamName = config.getString("teamName");
1110
+ if (!options.signIn.resolver) {
1111
+ throw new Error("SignInResolver is required to use this authentication provider");
1112
+ }
1113
+ const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ claims, cfIdentity }) => {
1114
+ return {
1115
+ profile: {
1116
+ email: claims.email,
1117
+ displayName: cfIdentity.name
1118
+ }
1119
+ };
1120
+ };
1121
+ return new CloudflareAccessAuthProvider({
1122
+ teamName,
1123
+ signInResolver: options == null ? void 0 : options.signIn.resolver,
1124
+ authHandler,
1125
+ resolverContext,
1126
+ ...options.cache && { cache: options.cache }
1127
+ });
1128
+ };
1129
+ },
1130
+ resolvers: {
1131
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver
1132
+ }
1133
+ });
1134
+
998
1135
  const IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
999
1136
 
1000
1137
  function createTokenValidator(audience, mockClient) {
@@ -1314,28 +1451,6 @@ const gitlab = createAuthProviderIntegration({
1314
1451
  }
1315
1452
  });
1316
1453
 
1317
- const commonByEmailLocalPartResolver = async (info, ctx) => {
1318
- const { profile } = info;
1319
- if (!profile.email) {
1320
- throw new Error("Login failed, user profile does not contain an email");
1321
- }
1322
- const [localPart] = profile.email.split("@");
1323
- return ctx.signInWithCatalogUser({
1324
- entityRef: { name: localPart }
1325
- });
1326
- };
1327
- const commonByEmailResolver = async (info, ctx) => {
1328
- const { profile } = info;
1329
- if (!profile.email) {
1330
- throw new Error("Login failed, user profile does not contain an email");
1331
- }
1332
- return ctx.signInWithCatalogUser({
1333
- filter: {
1334
- "spec.profile.email": profile.email
1335
- }
1336
- });
1337
- };
1338
-
1339
1454
  class GoogleAuthProvider {
1340
1455
  constructor(options) {
1341
1456
  this.authHandler = options.authHandler;
@@ -1911,6 +2026,8 @@ class OktaAuthProvider {
1911
2026
  clientSecret: options.clientSecret,
1912
2027
  callbackURL: options.callbackUrl,
1913
2028
  audience: options.audience,
2029
+ authServerID: options.authServerId,
2030
+ idp: options.idp,
1914
2031
  passReqToCallback: false,
1915
2032
  store: this.store,
1916
2033
  response_type: "code"
@@ -1979,6 +2096,8 @@ const okta = createAuthProviderIntegration({
1979
2096
  const clientId = envConfig.getString("clientId");
1980
2097
  const clientSecret = envConfig.getString("clientSecret");
1981
2098
  const audience = envConfig.getString("audience");
2099
+ const authServerId = envConfig.getOptionalString("authServerId");
2100
+ const idp = envConfig.getOptionalString("idp");
1982
2101
  const customCallbackUrl = envConfig.getOptionalString("callbackUrl");
1983
2102
  const callbackUrl = customCallbackUrl || `${globalConfig.baseUrl}/${providerId}/handler/frame`;
1984
2103
  if (!audience.startsWith("https://")) {
@@ -1989,6 +2108,8 @@ const okta = createAuthProviderIntegration({
1989
2108
  });
1990
2109
  const provider = new OktaAuthProvider({
1991
2110
  audience,
2111
+ authServerId,
2112
+ idp,
1992
2113
  clientId,
1993
2114
  clientSecret,
1994
2115
  callbackUrl,
@@ -2215,6 +2336,7 @@ const providers = Object.freeze({
2215
2336
  auth0,
2216
2337
  awsAlb,
2217
2338
  bitbucket,
2339
+ cfAccess,
2218
2340
  gcpIap,
2219
2341
  github,
2220
2342
  gitlab,
@@ -2724,6 +2846,7 @@ async function createRouter(options) {
2724
2846
  }
2725
2847
  if (provider.refresh) {
2726
2848
  r.get("/refresh", provider.refresh.bind(provider));
2849
+ r.post("/refresh", provider.refresh.bind(provider));
2727
2850
  }
2728
2851
  router.use(`/${providerId}`, r);
2729
2852
  } catch (e) {
@@ -2770,6 +2893,7 @@ exports.AtlassianAuthProvider = AtlassianAuthProvider;
2770
2893
  exports.CatalogIdentityClient = CatalogIdentityClient;
2771
2894
  exports.OAuthAdapter = OAuthAdapter;
2772
2895
  exports.OAuthEnvironmentHandler = OAuthEnvironmentHandler;
2896
+ exports.createAuthProviderIntegration = createAuthProviderIntegration;
2773
2897
  exports.createOriginFilter = createOriginFilter;
2774
2898
  exports.createRouter = createRouter;
2775
2899
  exports.defaultAuthProviderFactories = defaultAuthProviderFactories;