@backstage/plugin-auth-backend 0.13.0 → 0.13.1-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.13.1-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/backend-common@0.13.3-next.2
9
+ - @backstage/config@1.0.1-next.0
10
+ - @backstage/catalog-model@1.0.2-next.0
11
+ - @backstage/plugin-auth-node@0.2.1-next.1
12
+ - @backstage/catalog-client@1.0.2-next.0
13
+
14
+ ## 0.13.1-next.1
15
+
16
+ ### Patch Changes
17
+
18
+ - cac3ba68a2: Fixed a bug that was introduced in `0.13.1-next.0` which caused the `ent` claim of issued tokens to be dropped.
19
+ - 5d268623dd: Updates the OAuth2 Proxy provider to require less infrastructure configuration.
20
+
21
+ The auth result object of the OAuth2 Proxy now provides access to the request headers, both through the `headers` object as well as `getHeader` method. The existing logic that parses and extracts the user information from ID tokens is deprecated and will be removed in a future release. See the OAuth2 Proxy provider documentation for more details.
22
+
23
+ The OAuth2 Proxy provider now also has a default `authHandler` implementation that reads the display name and email from the incoming request headers.
24
+
25
+ - Updated dependencies
26
+ - @backstage/backend-common@0.13.3-next.1
27
+
28
+ ## 0.13.1-next.0
29
+
30
+ ### Patch Changes
31
+
32
+ - cfc0f19699: Updated dependency `fs-extra` to `10.1.0`.
33
+ - 787ae0d541: Add more common predefined sign-in resolvers to auth providers.
34
+
35
+ Add the existing resolver to more providers (already available at `google`):
36
+
37
+ - `providers.microsoft.resolvers.emailLocalPartMatchingUserEntityName()`
38
+ - `providers.okta.resolvers.emailLocalPartMatchingUserEntityName()`
39
+
40
+ Add a new resolver for simple email-to-email matching:
41
+
42
+ - `providers.google.resolvers.emailMatchingUserEntityProfileEmail()`
43
+ - `providers.microsoft.resolvers.emailMatchingUserEntityProfileEmail()`
44
+ - `providers.okta.resolvers.emailMatchingUserEntityProfileEmail()`
45
+
46
+ - 9ec4e0613e: Update to `jose` 4.6.0
47
+ - Updated dependencies
48
+ - @backstage/backend-common@0.13.3-next.0
49
+ - @backstage/plugin-auth-node@0.2.1-next.0
50
+
3
51
  ## 0.13.0
4
52
 
5
53
  ### Minor Changes
package/dist/index.cjs.js CHANGED
@@ -763,10 +763,6 @@ const createAuth0Provider = auth0.create;
763
763
 
764
764
  const ALB_JWT_HEADER = "x-amzn-oidc-data";
765
765
  const ALB_ACCESS_TOKEN_HEADER = "x-amzn-oidc-accesstoken";
766
- const getJWTHeaders = (input) => {
767
- const encoded = input.split(".")[0];
768
- return JSON.parse(Buffer.from(encoded, "base64").toString("utf8"));
769
- };
770
766
  class AwsAlbAuthProvider {
771
767
  constructor(options) {
772
768
  this.region = options.region;
@@ -801,9 +797,8 @@ class AwsAlbAuthProvider {
801
797
  throw new errors.AuthenticationError(`Missing ALB OIDC header: ${ALB_ACCESS_TOKEN_HEADER}`);
802
798
  }
803
799
  try {
804
- const headers = getJWTHeaders(jwt);
805
- const key = await this.getKey(headers.kid);
806
- const claims = jose.JWT.verify(jwt, key);
800
+ const verifyResult = await jose.jwtVerify(jwt, this.getKey);
801
+ const claims = verifyResult.payload;
807
802
  if (this.issuer && claims.iss !== this.issuer) {
808
803
  throw new errors.AuthenticationError("Issuer mismatch on JWT token");
809
804
  }
@@ -843,14 +838,17 @@ class AwsAlbAuthProvider {
843
838
  profile
844
839
  };
845
840
  }
846
- async getKey(keyId) {
847
- 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);
848
846
  if (optionalCacheKey) {
849
847
  return crypto__namespace.createPublicKey(optionalCacheKey);
850
848
  }
851
- const keyText = await fetch__default["default"](`https://public-keys.auth.elb.${encodeURIComponent(this.region)}.amazonaws.com/${encodeURIComponent(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());
852
850
  const keyValue = crypto__namespace.createPublicKey(keyText);
853
- this.keyCache.set(keyId, keyValue.export({ format: "pem", type: "spki" }));
851
+ this.keyCache.set(header.kid, keyValue.export({ format: "pem", type: "spki" }));
854
852
  return keyValue;
855
853
  }
856
854
  }
@@ -1257,6 +1255,17 @@ const commonByEmailLocalPartResolver = async (info, ctx) => {
1257
1255
  entityRef: { name: localPart }
1258
1256
  });
1259
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
+ }
1267
+ });
1268
+ };
1260
1269
 
1261
1270
  class GoogleAuthProvider {
1262
1271
  constructor(options) {
@@ -1354,6 +1363,7 @@ const google = createAuthProviderIntegration({
1354
1363
  },
1355
1364
  resolvers: {
1356
1365
  emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1366
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1357
1367
  emailMatchingUserEntityAnnotation() {
1358
1368
  return async (info, ctx) => {
1359
1369
  const { profile } = info;
@@ -1483,6 +1493,8 @@ const microsoft = createAuthProviderIntegration({
1483
1493
  });
1484
1494
  },
1485
1495
  resolvers: {
1496
+ emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1497
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1486
1498
  emailMatchingUserEntityAnnotation() {
1487
1499
  return async (info, ctx) => {
1488
1500
  const { profile } = info;
@@ -1629,7 +1641,20 @@ class Oauth2ProxyAuthProvider {
1629
1641
  }
1630
1642
  async refresh(req, res) {
1631
1643
  try {
1632
- const result = this.getResult(req);
1644
+ const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
1645
+ const jwt = pluginAuthNode.getBearerTokenFromAuthorizationHeader(authHeader);
1646
+ const decodedJWT = jwt && jose.decodeJwt(jwt);
1647
+ const result = {
1648
+ fullProfile: decodedJWT || {},
1649
+ accessToken: jwt || "",
1650
+ headers: req.headers,
1651
+ getHeader(name) {
1652
+ if (name.toLocaleLowerCase("en-US") === "set-cookie") {
1653
+ throw new Error("Access Set-Cookie via the headers object instead");
1654
+ }
1655
+ return req.get(name);
1656
+ }
1657
+ };
1633
1658
  const response = await this.handleResult(result);
1634
1659
  res.json(response);
1635
1660
  } catch (e) {
@@ -1653,18 +1678,14 @@ class Oauth2ProxyAuthProvider {
1653
1678
  profile
1654
1679
  };
1655
1680
  }
1656
- getResult(req) {
1657
- const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
1658
- const jwt = pluginAuthNode.getBearerTokenFromAuthorizationHeader(authHeader);
1659
- if (!jwt) {
1660
- throw new errors.AuthenticationError(`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`);
1681
+ }
1682
+ async function defaultAuthHandler$1(result) {
1683
+ return {
1684
+ profile: {
1685
+ email: result.getHeader("x-forwarded-email"),
1686
+ displayName: result.getHeader("x-forwarded-preferred-username") || result.getHeader("x-forwarded-user")
1661
1687
  }
1662
- const decodedJWT = jose.JWT.decode(jwt);
1663
- return {
1664
- fullProfile: decodedJWT,
1665
- accessToken: jwt
1666
- };
1667
- }
1688
+ };
1668
1689
  }
1669
1690
  const oauth2Proxy = createAuthProviderIntegration({
1670
1691
  create(options) {
@@ -1674,7 +1695,7 @@ const oauth2Proxy = createAuthProviderIntegration({
1674
1695
  return new Oauth2ProxyAuthProvider({
1675
1696
  resolverContext,
1676
1697
  signInResolver,
1677
- authHandler
1698
+ authHandler: authHandler != null ? authHandler : defaultAuthHandler$1
1678
1699
  });
1679
1700
  };
1680
1701
  }
@@ -1919,6 +1940,8 @@ const okta = createAuthProviderIntegration({
1919
1940
  });
1920
1941
  },
1921
1942
  resolvers: {
1943
+ emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver,
1944
+ emailMatchingUserEntityProfileEmail: () => commonByEmailResolver,
1922
1945
  emailMatchingUserEntityAnnotation() {
1923
1946
  return async (info, ctx) => {
1924
1947
  const { profile } = info;
@@ -2299,10 +2322,10 @@ class TokenFactory {
2299
2322
  throw new Error('"sub" claim provided by the auth resolver is not a valid EntityRef.');
2300
2323
  }
2301
2324
  this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2302
- return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2303
- alg: key.alg,
2304
- kid: key.kid
2305
- });
2325
+ if (!key.alg) {
2326
+ throw new errors.AuthenticationError("No algorithm was provided in the key");
2327
+ }
2328
+ return new jose.SignJWT({ iss, sub, ent, 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));
2306
2329
  }
2307
2330
  async listPublicKeys() {
2308
2331
  const { items: keys } = await this.keyStore.listKeys();
@@ -2339,14 +2362,14 @@ class TokenFactory {
2339
2362
  seconds: this.keyDurationSeconds
2340
2363
  }).toJSDate();
2341
2364
  const promise = (async () => {
2342
- const key = await jose.JWK.generate("EC", "P-256", {
2343
- use: "sig",
2344
- kid: uuid.v4(),
2345
- alg: "ES256"
2346
- });
2347
- this.logger.info(`Created new signing key ${key.kid}`);
2348
- await this.keyStore.addKey(key.toJWK(false));
2349
- return key;
2365
+ const key = await jose.generateKeyPair("ES256");
2366
+ const publicKey = await jose.exportJWK(key.publicKey);
2367
+ const privateKey = await jose.exportJWK(key.privateKey);
2368
+ publicKey.kid = privateKey.kid = uuid.v4();
2369
+ publicKey.alg = privateKey.alg = "ES256";
2370
+ this.logger.info(`Created new signing key ${publicKey.kid}`);
2371
+ await this.keyStore.addKey(publicKey);
2372
+ return privateKey;
2350
2373
  })();
2351
2374
  this.privateKeyPromise = promise;
2352
2375
  try {