diff --git a/packages/backend-app-api/src/services/implementations/auth/UserTokenHandler.ts b/packages/backend-app-api/src/services/implementations/auth/UserTokenHandler.ts index 8a7debbe17..dbada2f1a7 100644 --- a/packages/backend-app-api/src/services/implementations/auth/UserTokenHandler.ts +++ b/packages/backend-app-api/src/services/implementations/auth/UserTokenHandler.ts @@ -19,6 +19,7 @@ import { DiscoveryService } from '@backstage/backend-plugin-api'; import { AuthenticationError } from '@backstage/errors'; import { TokenTypes } from '@backstage/plugin-auth-node'; import { + base64url, createRemoteJWKSet, decodeJwt, decodeProtectedHeader, @@ -102,6 +103,37 @@ export class UserTokenHandler { return { userEntityRef }; } + createLimitedUserToken(backstageToken: string) { + const [headerRaw, payloadRaw] = backstageToken.split('.'); + const header = JSON.parse( + new TextDecoder().decode(base64url.decode(headerRaw)), + ); + const payload = JSON.parse( + new TextDecoder().decode(base64url.decode(payloadRaw)), + ); + + const limitedUserToken = [ + base64url.encode( + JSON.stringify({ + typ: 'vnd.backstage.limited-user', + alg: header.alg, + kid: header.kid, + }), + ), + base64url.encode( + JSON.stringify({ + sub: payload.sub, + ent: payload.ent, + iat: payload.iat, + exp: payload.exp, + }), + ), + payload.uip, + ].join('.'); + + return { token: limitedUserToken, expiresAt: new Date(payload.exp * 1000) }; + } + /** * If the last keystore refresh is stale, update the keystore URL to the latest */ diff --git a/packages/backend-app-api/src/services/implementations/auth/authServiceFactory.ts b/packages/backend-app-api/src/services/implementations/auth/authServiceFactory.ts index 81172f360b..41b8a12a3a 100644 --- a/packages/backend-app-api/src/services/implementations/auth/authServiceFactory.ts +++ b/packages/backend-app-api/src/services/implementations/auth/authServiceFactory.ts @@ -27,7 +27,7 @@ import { } from '@backstage/backend-plugin-api'; import { AuthenticationError } from '@backstage/errors'; import { TokenTypes } from '@backstage/plugin-auth-node'; -import { base64url, decodeJwt, decodeProtectedHeader } from 'jose'; +import { decodeJwt, decodeProtectedHeader } from 'jose'; import { UserTokenHandler } from './UserTokenHandler'; /** @internal */ @@ -231,34 +231,7 @@ class DefaultAuthService implements AuthService { ); } - const [headerRaw, payloadRaw] = backstageToken.split('.'); - const header = JSON.parse( - new TextDecoder().decode(base64url.decode(headerRaw)), - ); - const payload = JSON.parse( - new TextDecoder().decode(base64url.decode(payloadRaw)), - ); - - const limitedUserToken = [ - base64url.encode( - JSON.stringify({ - typ: 'vnd.backstage.limited-user', - alg: header.alg, - kid: header.kid, - }), - ), - base64url.encode( - JSON.stringify({ - sub: payload.sub, - ent: payload.ent, - iat: payload.iat, - exp: payload.exp, - }), - ), - payload.uip, - ].join('.'); - - return { token: limitedUserToken, expiresAt: new Date(payload.exp * 1000) }; + return this.userTokenHandler.createLimitedUserToken(backstageToken); } #getJwtExpiration(token: string) {