@backstage/plugin-auth-backend 0.22.3 → 0.22.4-next.1

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,59 @@
1
1
  # @backstage/plugin-auth-backend
2
2
 
3
+ ## 0.22.4-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d62bc51: Added token type header parameter and user identity proof to issued user tokens.
8
+ - bf4d71a: Initial implementation of the `/v1/userinfo` endpoint, which is now able to parse and return the `sub` and `ent` claims from a Backstage user token.
9
+ - Updated dependencies
10
+ - @backstage/backend-common@0.21.7-next.1
11
+ - @backstage/backend-plugin-api@0.6.17-next.1
12
+ - @backstage/plugin-auth-node@0.4.12-next.1
13
+ - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.9-next.1
14
+ - @backstage/plugin-auth-backend-module-oidc-provider@0.1.8-next.1
15
+ - @backstage/catalog-client@1.6.4-next.0
16
+ - @backstage/catalog-model@1.4.5
17
+ - @backstage/config@1.2.0
18
+ - @backstage/errors@1.2.4
19
+ - @backstage/types@1.1.1
20
+ - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.9-next.1
21
+ - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.12-next.1
22
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.14-next.1
23
+ - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.14-next.1
24
+ - @backstage/plugin-auth-backend-module-google-provider@0.1.14-next.1
25
+ - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.12-next.1
26
+ - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.14-next.1
27
+ - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.10-next.1
28
+ - @backstage/plugin-auth-backend-module-okta-provider@0.0.10-next.1
29
+ - @backstage/plugin-catalog-node@1.11.1-next.1
30
+
31
+ ## 0.22.4-next.0
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies
36
+ - @backstage/backend-common@0.21.7-next.0
37
+ - @backstage/backend-plugin-api@0.6.17-next.0
38
+ - @backstage/catalog-client@1.6.3
39
+ - @backstage/catalog-model@1.4.5
40
+ - @backstage/config@1.2.0
41
+ - @backstage/errors@1.2.4
42
+ - @backstage/types@1.1.1
43
+ - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.9-next.0
44
+ - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.9-next.0
45
+ - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.12-next.0
46
+ - @backstage/plugin-auth-backend-module-github-provider@0.1.14-next.0
47
+ - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.14-next.0
48
+ - @backstage/plugin-auth-backend-module-google-provider@0.1.14-next.0
49
+ - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.12-next.0
50
+ - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.14-next.0
51
+ - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.10-next.0
52
+ - @backstage/plugin-auth-backend-module-oidc-provider@0.1.8-next.0
53
+ - @backstage/plugin-auth-backend-module-okta-provider@0.0.10-next.0
54
+ - @backstage/plugin-auth-node@0.4.12-next.0
55
+ - @backstage/plugin-catalog-node@1.11.1-next.0
56
+
3
57
  ## 0.22.3
4
58
 
5
59
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -2156,7 +2156,7 @@ function createOriginFilter(config) {
2156
2156
  }
2157
2157
 
2158
2158
  function bindOidcRouter(targetRouter, options) {
2159
- const { baseUrl, tokenIssuer } = options;
2159
+ const { baseUrl, auth, tokenIssuer } = options;
2160
2160
  const router = Router__default.default();
2161
2161
  targetRouter.use(router);
2162
2162
  const config = {
@@ -2180,7 +2180,7 @@ function bindOidcRouter(targetRouter, options) {
2180
2180
  ],
2181
2181
  scopes_supported: ["openid"],
2182
2182
  token_endpoint_auth_methods_supported: [],
2183
- claims_supported: ["sub"],
2183
+ claims_supported: ["sub", "ent"],
2184
2184
  grant_types_supported: []
2185
2185
  };
2186
2186
  router.get("/.well-known/openid-configuration", (_req, res) => {
@@ -2193,8 +2193,31 @@ function bindOidcRouter(targetRouter, options) {
2193
2193
  router.get("/v1/token", (_req, res) => {
2194
2194
  res.status(501).send("Not Implemented");
2195
2195
  });
2196
- router.get("/v1/userinfo", (_req, res) => {
2197
- res.status(501).send("Not Implemented");
2196
+ router.get("/v1/userinfo", async (req, res) => {
2197
+ var _a;
2198
+ const matches = (_a = req.headers.authorization) == null ? void 0 : _a.match(/^Bearer[ ]+(\S+)$/i);
2199
+ const token = matches == null ? void 0 : matches[1];
2200
+ if (!token) {
2201
+ throw new errors.AuthenticationError("No token provided");
2202
+ }
2203
+ const credentials = await auth.authenticate(token, {
2204
+ allowLimitedAccess: true
2205
+ });
2206
+ if (!auth.isPrincipal(credentials, "user")) {
2207
+ throw new errors.InputError(
2208
+ "Userinfo endpoint must be called with a token that represents a user principal"
2209
+ );
2210
+ }
2211
+ const { sub: userEntityRef, ent: ownershipEntityRefs = [] } = jose.decodeJwt(token);
2212
+ if (typeof userEntityRef !== "string") {
2213
+ throw new Error("Invalid user token, user entity ref must be a string");
2214
+ }
2215
+ if (!Array.isArray(ownershipEntityRefs) || ownershipEntityRefs.some((ref) => typeof ref !== "string")) {
2216
+ throw new Error(
2217
+ "Invalid user token, ownership entity refs must be an array of strings"
2218
+ );
2219
+ }
2220
+ res.json({ sub: userEntityRef, ent: ownershipEntityRefs });
2198
2221
  });
2199
2222
  }
2200
2223
 
@@ -2225,8 +2248,8 @@ class TokenFactory {
2225
2248
  async issueToken(params) {
2226
2249
  const key = await this.getKey();
2227
2250
  const iss = this.issuer;
2228
- const { sub, ent, ...additionalClaims } = params.claims;
2229
- const aud = "backstage";
2251
+ const { sub, ent = [sub], ...additionalClaims } = params.claims;
2252
+ const aud = pluginAuthNode.tokenTypes.user.audClaim;
2230
2253
  const iat = Math.floor(Date.now() / MS_IN_S$1);
2231
2254
  const exp = iat + this.keyDurationSeconds;
2232
2255
  try {
@@ -2236,12 +2259,35 @@ class TokenFactory {
2236
2259
  '"sub" claim provided by the auth resolver is not a valid EntityRef.'
2237
2260
  );
2238
2261
  }
2239
- this.logger.info(`Issuing token for ${sub}, with entities ${ent != null ? ent : []}`);
2240
2262
  if (!key.alg) {
2241
2263
  throw new errors.AuthenticationError("No algorithm was provided in the key");
2242
2264
  }
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));
2265
+ this.logger.info(`Issuing token for ${sub}, with entities ${ent}`);
2266
+ const signingKey = await jose.importJWK(key);
2267
+ const uip = await this.createUserIdentityClaim({
2268
+ header: {
2269
+ typ: pluginAuthNode.tokenTypes.limitedUser.typParam,
2270
+ alg: key.alg,
2271
+ kid: key.kid
2272
+ },
2273
+ payload: { sub, ent, iat, exp },
2274
+ key: signingKey
2275
+ });
2276
+ const claims = {
2277
+ ...additionalClaims,
2278
+ iss,
2279
+ sub,
2280
+ ent,
2281
+ aud,
2282
+ iat,
2283
+ exp,
2284
+ uip
2285
+ };
2286
+ const token = await new jose.SignJWT(claims).setProtectedHeader({
2287
+ typ: pluginAuthNode.tokenTypes.user.typParam,
2288
+ alg: key.alg,
2289
+ kid: key.kid
2290
+ }).sign(signingKey);
2245
2291
  if (token.length > MAX_TOKEN_LENGTH) {
2246
2292
  throw new Error(
2247
2293
  `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 +2354,26 @@ class TokenFactory {
2308
2354
  }
2309
2355
  return promise;
2310
2356
  }
2357
+ // Creates a string claim that can be used as part of reconstructing a limited
2358
+ // user token. The output of this function is only the signature part of a
2359
+ // JWS.
2360
+ async createUserIdentityClaim(options) {
2361
+ const header = {
2362
+ typ: options.header.typ,
2363
+ alg: options.header.alg,
2364
+ ...options.header.kid ? { kid: options.header.kid } : {}
2365
+ };
2366
+ const payload = {
2367
+ sub: options.payload.sub,
2368
+ ent: options.payload.ent,
2369
+ iat: options.payload.iat,
2370
+ exp: options.payload.exp
2371
+ };
2372
+ const jws = await new jose.GeneralSign(
2373
+ new TextEncoder().encode(JSON.stringify(payload))
2374
+ ).addSignature(options.key).setProtectedHeader(header).done().sign();
2375
+ return jws.signatures[0].signature;
2376
+ }
2311
2377
  }
2312
2378
 
2313
2379
  const TABLE = "signing_keys";
@@ -2807,6 +2873,7 @@ async function createRouter(options) {
2807
2873
  httpAuth
2808
2874
  });
2809
2875
  bindOidcRouter(router, {
2876
+ auth,
2810
2877
  tokenIssuer,
2811
2878
  baseUrl: authUrl
2812
2879
  });
@@ -2839,6 +2906,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2839
2906
  database: backendPluginApi.coreServices.database,
2840
2907
  discovery: backendPluginApi.coreServices.discovery,
2841
2908
  tokenManager: backendPluginApi.coreServices.tokenManager,
2909
+ auth: backendPluginApi.coreServices.auth,
2910
+ httpAuth: backendPluginApi.coreServices.httpAuth,
2842
2911
  catalogApi: alpha.catalogServiceRef
2843
2912
  },
2844
2913
  async init({
@@ -2848,6 +2917,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2848
2917
  database,
2849
2918
  discovery,
2850
2919
  tokenManager,
2920
+ auth,
2921
+ httpAuth,
2851
2922
  catalogApi
2852
2923
  }) {
2853
2924
  const router = await createRouter({
@@ -2856,6 +2927,8 @@ const authPlugin = backendPluginApi.createBackendPlugin({
2856
2927
  database,
2857
2928
  discovery,
2858
2929
  tokenManager,
2930
+ auth,
2931
+ httpAuth,
2859
2932
  catalogApi,
2860
2933
  providerFactories: Object.fromEntries(providers),
2861
2934
  disableDefaultProviderFactories: true