diff --git a/.changeset/shy-doors-repair.md b/.changeset/shy-doors-repair.md new file mode 100644 index 0000000000..e9330a231a --- /dev/null +++ b/.changeset/shy-doors-repair.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +**BREAKING**: The setting `auth.omitIdentityTokenOwnershipClaim` has had its default value switched to `true`. + +With this setting Backstage user tokens issued by the `auth` backend will no longer contain an `ent` claim - the one with the user's ownership entity refs. This means that tokens issued in large orgs no longer risk hitting HTTP header size limits. + +To get ownership info for the current user, code should use the `userInfo` core service. In practice code will typically already conform to this since the `ent` claim has not been readily exposed in any other way for quite some time. But code which explicitly decodes Backstage tokens - which is strongly discouraged - may be affected by this change. + +The setting will remain for some time to allow it to be set back to `false` if need be, but it will be removed entirely in a future release. diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md index 26b48dec69..ef77e69c22 100644 --- a/docs/auth/identity-resolver.md +++ b/docs/auth/identity-resolver.md @@ -399,50 +399,6 @@ async signInResolver({ profile }, ctx) { } ``` -## Reducing the size of issued tokens - -By default the auth backend will issue user identity tokens that include the -ownership references of the user in the `ent` claim of the JWT payload. This is -done to make it easier and more efficient for consumers of the token to resolve -ownership of the user. However, depending on the shape of your organization and -how you resolve ownership claims, these tokens can grow quite large. - -To address this, the auth backend now supports the configuration flag -`auth.omitIdentityTokenOwnershipClaim` that causes the `ent` claim to be omitted -from the token. This can be set to `true` in the `app-config.yaml` file. - -```yaml title="in app-config.yaml" -auth: - omitIdentityTokenOwnershipClaim: true -``` - -When this flag is set, the `ent` claim will no longer be present in the token, -and consumers of the token will need to call the `/v1/userinfo` endpoint on the -auth backend to fetch the ownership references of the user. However, there's usually no -action required for consumers. Clients will still receive the full set -of claims during authentication, and any plugin backends will already need to -use the -[`UserInfoService`](../backend-system/core-services/user-info.md) to -access the ownership references from user credentials, which already calls the -user info endpoint if necessary. - -When enabling this flag, it is important that any custom sign-in resolvers directly return the result of the sign-in method. For example, the following would not work: - -```ts -const { token } = await ctx.issueToken({ - claims: { sub: entityRef, ent: [entityRef] }, -}); -return { token }; // WARNING: This will not work -``` - -Instead, the sign-in resolver should directly return the result: - -```ts -return ctx.issueToken({ - claims: { sub: entityRef, ent: [entityRef] }, -}); -``` - ##### Using the `dangerouslyAllowSignInWithoutUserInCatalog` Option Another way to bypass this requirement is to enable the `dangerouslyAllowSignInWithoutUserInCatalog` option for resolvers. diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index 9326394a2a..b35e39d90d 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -45,10 +45,16 @@ export interface Config { /** * Whether to omit the entity ownership references (`ent`) claim from the - * identity token. If this is enabled the `ent` claim will only be available - * via the user info endpoint and the `UserInfoService`. + * identity token. * - * Defaults to `false`. + * If this is disabled an `ent` claim will be included in the token + * containing all of the user's ownership refs as returned by the sign in + * resolver. This can in extreme cases lead to tokens that risk hitting HTTP + * header size limits. Setting it to `false` is therefore discouraged, and + * is only provided for backward compatibility reasons. + * + * Defaults to `true`, which means that the `ent` claim instead is available + * via the user info endpoint and the `UserInfoService`. */ omitIdentityTokenOwnershipClaim?: boolean; diff --git a/plugins/auth-backend/src/authPlugin.test.ts b/plugins/auth-backend/src/authPlugin.test.ts index 65d44614fe..6dede3b39b 100644 --- a/plugins/auth-backend/src/authPlugin.test.ts +++ b/plugins/auth-backend/src/authPlugin.test.ts @@ -100,7 +100,7 @@ describe('authPlugin', () => { const token = refreshRes.body.backstageIdentity.token; const decoded = JSON.parse(atob(token.split('.')[1])); expect(decoded.sub).toEqual(expectedIdentity.userEntityRef); - expect(decoded.ent).toEqual(expectedIdentity.ownershipEntityRefs); + expect('ent' in decoded).toBeFalsy(); const userInfoRes = await request(server) .get('/api/auth/v1/userinfo') @@ -115,7 +115,7 @@ describe('authPlugin', () => { }); }); - it('should omit ownership claims from the token when the config is set', async () => { + it('should include ownership claims in the token when the config is set to false', async () => { const { server } = await startTestBackend({ features: [ authPlugin, @@ -127,7 +127,7 @@ describe('authPlugin', () => { baseUrl: 'http://localhost', }, auth: { - omitIdentityTokenOwnershipClaim: true, + omitIdentityTokenOwnershipClaim: false, ...mockProvidersConfig, }, }, @@ -149,7 +149,7 @@ describe('authPlugin', () => { const token = refreshRes.body.backstageIdentity.token; const decoded = JSON.parse(atob(token.split('.')[1])); expect(decoded.sub).toEqual(expectedIdentity.userEntityRef); - expect(decoded.ent).toBeUndefined(); + expect(decoded.ent).toEqual(expectedIdentity.ownershipEntityRefs); const userInfoRes = await request(server) .get('/api/auth/v1/userinfo') diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 8a8118c43e..560fa79f50 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -87,11 +87,10 @@ export async function createRouter( database, }); - const omitClaimsFromToken = config.getOptionalBoolean( - 'auth.omitIdentityTokenOwnershipClaim', - ) - ? ['ent'] - : []; + const omitClaimsFromToken = + config.getOptionalBoolean('auth.omitIdentityTokenOwnershipClaim') ?? true + ? ['ent'] + : []; let tokenIssuer: TokenIssuer; if (keyStore instanceof StaticKeyStore) {