@backstage/plugin-auth-backend 0.10.2 → 0.12.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/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.12.1-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - ab7cd7d70e: Do some groundwork for supporting the `better-sqlite3` driver, to maybe eventually replace `@vscode/sqlite3` (#9912)
8
+ - e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1
9
+ - bf95bb806c: Remove usages of now-removed `CatalogApi.getEntityByName`
10
+ - 3c2bc73901: Use `setupRequestMockHandlers` from `@backstage/backend-test-utils`
11
+ - Updated dependencies
12
+ - @backstage/backend-common@0.13.0-next.0
13
+ - @backstage/catalog-model@0.13.0-next.0
14
+ - @backstage/catalog-client@0.9.0-next.0
15
+ - @backstage/plugin-auth-node@0.1.5-next.0
16
+
17
+ ## 0.12.0
18
+
19
+ ### Minor Changes
20
+
21
+ - 0c8ba31d72: **BREAKING**: The `TokenFactory.issueToken` used by custom sign-in resolvers now ensures that the sub claim given is a full entity reference of the format `<kind>:<namespace>/<name>`. Any existing custom sign-in resolver functions that do not supply a full entity reference must be updated.
22
+
23
+ ### Patch Changes
24
+
25
+ - 899f196af5: Use `getEntityByRef` instead of `getEntityByName` in the catalog client
26
+ - 36aa63022b: Use `CompoundEntityRef` instead of `EntityName`, and `getCompoundEntityRef` instead of `getEntityName`, from `@backstage/catalog-model`.
27
+ - Updated dependencies
28
+ - @backstage/catalog-model@0.12.0
29
+ - @backstage/catalog-client@0.8.0
30
+ - @backstage/backend-common@0.12.0
31
+ - @backstage/plugin-auth-node@0.1.4
32
+
33
+ ## 0.11.0
34
+
35
+ ### Minor Changes
36
+
37
+ - 3884bf0348: **BREAKING**: The default sign-in resolvers for all providers, if you choose to
38
+ use them, now emit the token `sub` and `ent` claims on the standard,
39
+ all-lowercase form, instead of the mixed-case form. The mixed-case form causes
40
+ problems for implementations that naively do string comparisons on refs. The end
41
+ result is that you may for example see your Backstage token `sub` claim now
42
+ become `'user:default/my-id'` instead of `'user:default/My-ID'`.
43
+
44
+ On a related note, specifically the SAML provider now correctly issues both
45
+ `sub` and `ent` claims, and on the full entity ref form instead of the short
46
+ form with only the ID.
47
+
48
+ **NOTE**: For a long time, it has been strongly recommended that you provide
49
+ your own sign-in resolver instead of using the builtin ones, and that will
50
+ become mandatory in the future.
51
+
52
+ ### Patch Changes
53
+
54
+ - d64b8d3678: chore(deps): bump `minimatch` from 3.0.4 to 5.0.0
55
+ - 6e1cbc12a6: Updated according to the new `getEntityFacets` catalog API method
56
+ - 919cf2f836: Minor updates to match the new `targetRef` field of relations, and to stop consuming the `target` field
57
+ - Updated dependencies
58
+ - @backstage/backend-common@0.11.0
59
+ - @backstage/catalog-model@0.11.0
60
+ - @backstage/catalog-client@0.7.2
61
+ - @backstage/plugin-auth-node@0.1.3
62
+
3
63
  ## 0.10.2
4
64
 
5
65
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -607,10 +607,10 @@ class CatalogIdentityClient {
607
607
  }
608
608
  const memberOf = entities.flatMap((e) => {
609
609
  var _a, _b;
610
- return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.target)) != null ? _b : [];
610
+ return (_b = (_a = e.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF).map((r) => r.targetRef)) != null ? _b : [];
611
611
  });
612
612
  const newEntityRefs = [
613
- ...new Set(resolvedEntityRefs.concat(memberOf).map(catalogModel.stringifyEntityRef))
613
+ ...new Set(resolvedEntityRefs.map(catalogModel.stringifyEntityRef).concat(memberOf))
614
614
  ];
615
615
  logger == null ? void 0 : logger.debug(`Found catalog membership: ${newEntityRefs.join()}`);
616
616
  return newEntityRefs;
@@ -620,7 +620,7 @@ class CatalogIdentityClient {
620
620
  function getEntityClaims(entity) {
621
621
  var _a, _b;
622
622
  const userRef = catalogModel.stringifyEntityRef(entity);
623
- const membershipRefs = (_b = (_a = entity.relations) == null ? void 0 : _a.filter((r) => r.type === catalogModel.RELATION_MEMBER_OF && r.target.kind.toLocaleLowerCase("en-US") === "group").map((r) => catalogModel.stringifyEntityRef(r.target))) != null ? _b : [];
623
+ 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 : [];
624
624
  return {
625
625
  sub: userRef,
626
626
  ent: [userRef, ...membershipRefs]
@@ -1261,10 +1261,15 @@ class GithubAuthProvider {
1261
1261
  const githubDefaultSignInResolver = async (info, ctx) => {
1262
1262
  const { fullProfile } = info.result;
1263
1263
  const userId = fullProfile.username || fullProfile.id;
1264
+ const entityRef = catalogModel.stringifyEntityRef({
1265
+ kind: "User",
1266
+ namespace: catalogModel.DEFAULT_NAMESPACE,
1267
+ name: userId
1268
+ });
1264
1269
  const token = await ctx.tokenIssuer.issueToken({
1265
1270
  claims: {
1266
- sub: `user:default/${userId}`,
1267
- ent: [`user:default/${userId}`]
1271
+ sub: entityRef,
1272
+ ent: [entityRef]
1268
1273
  }
1269
1274
  });
1270
1275
  return { id: userId, token };
@@ -1333,8 +1338,16 @@ const gitlabDefaultSignInResolver = async (info, ctx) => {
1333
1338
  if (profile.email) {
1334
1339
  id = profile.email.split("@")[0];
1335
1340
  }
1341
+ const entityRef = catalogModel.stringifyEntityRef({
1342
+ kind: "User",
1343
+ namespace: catalogModel.DEFAULT_NAMESPACE,
1344
+ name: id
1345
+ });
1336
1346
  const token = await ctx.tokenIssuer.issueToken({
1337
- claims: { sub: `user:default/${id}`, ent: [`user:default/${id}`] }
1347
+ claims: {
1348
+ sub: entityRef,
1349
+ ent: [entityRef]
1350
+ }
1338
1351
  });
1339
1352
  return { id, token };
1340
1353
  };
@@ -1566,8 +1579,16 @@ const googleDefaultSignInResolver = async (info, ctx) => {
1566
1579
  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`);
1567
1580
  userId = profile.email.split("@")[0];
1568
1581
  }
1582
+ const entityRef = catalogModel.stringifyEntityRef({
1583
+ kind: "User",
1584
+ namespace: catalogModel.DEFAULT_NAMESPACE,
1585
+ name: userId
1586
+ });
1569
1587
  const token = await ctx.tokenIssuer.issueToken({
1570
- claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
1588
+ claims: {
1589
+ sub: entityRef,
1590
+ ent: [entityRef]
1591
+ }
1571
1592
  });
1572
1593
  return { id: userId, token };
1573
1594
  };
@@ -1723,10 +1744,15 @@ const microsoftDefaultSignInResolver = async (info, ctx) => {
1723
1744
  throw new Error("Profile contained no email");
1724
1745
  }
1725
1746
  const userId = profile.email.split("@")[0];
1747
+ const entityRef = catalogModel.stringifyEntityRef({
1748
+ kind: "User",
1749
+ namespace: catalogModel.DEFAULT_NAMESPACE,
1750
+ name: userId
1751
+ });
1726
1752
  const token = await ctx.tokenIssuer.issueToken({
1727
1753
  claims: {
1728
- sub: `user:default/${userId}`,
1729
- ent: [`user:default/${userId}`]
1754
+ sub: entityRef,
1755
+ ent: [entityRef]
1730
1756
  }
1731
1757
  });
1732
1758
  return { id: userId, token };
@@ -1868,14 +1894,22 @@ class OAuth2AuthProvider {
1868
1894
  return Buffer.from(`${clientID}:${clientSecret}`).toString("base64");
1869
1895
  }
1870
1896
  }
1871
- const oAuth2DefaultSignInResolver$1 = async (info, ctx) => {
1897
+ const oAuth2DefaultSignInResolver = async (info, ctx) => {
1872
1898
  const { profile } = info;
1873
1899
  if (!profile.email) {
1874
1900
  throw new Error("Profile contained no email");
1875
1901
  }
1876
1902
  const userId = profile.email.split("@")[0];
1903
+ const entityRef = catalogModel.stringifyEntityRef({
1904
+ kind: "User",
1905
+ namespace: catalogModel.DEFAULT_NAMESPACE,
1906
+ name: userId
1907
+ });
1877
1908
  const token = await ctx.tokenIssuer.issueToken({
1878
- claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
1909
+ claims: {
1910
+ sub: entityRef,
1911
+ ent: [entityRef]
1912
+ }
1879
1913
  });
1880
1914
  return { id: userId, token };
1881
1915
  };
@@ -1906,7 +1940,7 @@ const createOAuth2Provider = (options) => {
1906
1940
  const authHandler = (options == null ? void 0 : options.authHandler) ? options.authHandler : async ({ fullProfile, params }) => ({
1907
1941
  profile: makeProfileInfo(fullProfile, params.id_token)
1908
1942
  });
1909
- const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver$1;
1943
+ const signInResolverFn = (_c = (_b = options == null ? void 0 : options.signIn) == null ? void 0 : _b.resolver) != null ? _c : oAuth2DefaultSignInResolver;
1910
1944
  const signInResolver = (info) => signInResolverFn(info, {
1911
1945
  catalogIdentityClient,
1912
1946
  tokenIssuer,
@@ -2102,16 +2136,21 @@ class OidcAuthProvider {
2102
2136
  return response;
2103
2137
  }
2104
2138
  }
2105
- const oAuth2DefaultSignInResolver = async (info, ctx) => {
2139
+ const oidcDefaultSignInResolver = async (info, ctx) => {
2106
2140
  const { profile } = info;
2107
2141
  if (!profile.email) {
2108
2142
  throw new Error("Profile contained no email");
2109
2143
  }
2110
2144
  const userId = profile.email.split("@")[0];
2145
+ const entityRef = catalogModel.stringifyEntityRef({
2146
+ kind: "User",
2147
+ namespace: catalogModel.DEFAULT_NAMESPACE,
2148
+ name: userId
2149
+ });
2111
2150
  const token = await ctx.tokenIssuer.issueToken({
2112
2151
  claims: {
2113
- sub: `user:default/${userId}`,
2114
- ent: [`user:default/${userId}`]
2152
+ sub: entityRef,
2153
+ ent: [entityRef]
2115
2154
  }
2116
2155
  });
2117
2156
  return { id: userId, token };
@@ -2146,7 +2185,7 @@ const createOidcProvider = (options) => {
2146
2185
  picture: userinfo.picture
2147
2186
  }
2148
2187
  });
2149
- const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oAuth2DefaultSignInResolver;
2188
+ const signInResolverFn = (_b = (_a = options == null ? void 0 : options.signIn) == null ? void 0 : _a.resolver) != null ? _b : oidcDefaultSignInResolver;
2150
2189
  const signInResolver = (info) => signInResolverFn(info, {
2151
2190
  catalogIdentityClient,
2152
2191
  tokenIssuer,
@@ -2281,8 +2320,16 @@ const oktaDefaultSignInResolver = async (info, ctx) => {
2281
2320
  throw new Error("Okta profile contained no email");
2282
2321
  }
2283
2322
  const userId = profile.email.split("@")[0];
2323
+ const entityRef = catalogModel.stringifyEntityRef({
2324
+ kind: "User",
2325
+ namespace: catalogModel.DEFAULT_NAMESPACE,
2326
+ name: userId
2327
+ });
2284
2328
  const token = await ctx.tokenIssuer.issueToken({
2285
- claims: { sub: `user:default/${userId}`, ent: [`user:default/${userId}`] }
2329
+ claims: {
2330
+ sub: entityRef,
2331
+ ent: [entityRef]
2332
+ }
2286
2333
  });
2287
2334
  return { id: userId, token };
2288
2335
  };
@@ -2520,8 +2567,16 @@ class SamlAuthProvider {
2520
2567
  }
2521
2568
  const samlDefaultSignInResolver = async (info, ctx) => {
2522
2569
  const id = info.result.fullProfile.nameID;
2570
+ const entityRef = catalogModel.stringifyEntityRef({
2571
+ kind: "User",
2572
+ namespace: catalogModel.DEFAULT_NAMESPACE,
2573
+ name: id
2574
+ });
2523
2575
  const token = await ctx.tokenIssuer.issueToken({
2524
- claims: { sub: id }
2576
+ claims: {
2577
+ sub: entityRef,
2578
+ ent: [entityRef]
2579
+ }
2525
2580
  });
2526
2581
  return { id, token };
2527
2582
  };
@@ -2731,6 +2786,11 @@ class TokenFactory {
2731
2786
  const aud = "backstage";
2732
2787
  const iat = Math.floor(Date.now() / MS_IN_S);
2733
2788
  const exp = iat + this.keyDurationSeconds;
2789
+ try {
2790
+ catalogModel.parseEntityRef(sub);
2791
+ } catch (error) {
2792
+ throw new Error('"sub" claim provided by the auth resolver is not a valid EntityRef.');
2793
+ }
2734
2794
  this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2735
2795
  return jose.JWS.sign({ iss, sub, aud, iat, exp, ent }, key, {
2736
2796
  alg: key.alg,