diff --git a/.changeset/rich-cooks-camp.md b/.changeset/rich-cooks-camp.md new file mode 100644 index 0000000000..04d86033f7 --- /dev/null +++ b/.changeset/rich-cooks-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Cloudflare Access Provider: Add JWT to CloudflareAccessResult diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 649a31c963..d4472504a9 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -204,6 +204,7 @@ export type CloudflareAccessResult = { claims: CloudflareAccessClaims; cfIdentity: CloudflareAccessIdentityProfile; expiresInSeconds?: number; + token: string; }; // @public diff --git a/plugins/auth-backend/src/providers/cloudflare-access/provider.test.ts b/plugins/auth-backend/src/providers/cloudflare-access/provider.test.ts index 21ec7371e5..7570eb9156 100644 --- a/plugins/auth-backend/src/providers/cloudflare-access/provider.test.ts +++ b/plugins/auth-backend/src/providers/cloudflare-access/provider.test.ts @@ -138,12 +138,28 @@ describe('CloudflareAccessAuthProvider', () => { const provider = new CloudflareAccessAuthProvider({ teamName: 'foobar', resolverContext: {} as AuthResolverContext, - authHandler: async ({ claims }) => ({ - profile: { - email: claims.email, - }, - }), - signInResolver: async () => { + authHandler: async result => { + expect(result).toEqual( + expect.objectContaining({ + claims: mockClaims, + cfIdentity: mockCfIdentity, + token: mockJwt, + }), + ); + return { + profile: { + email: result.claims.email, + }, + }; + }, + signInResolver: async ({ result }) => { + expect(result).toEqual( + expect.objectContaining({ + claims: mockClaims, + cfIdentity: mockCfIdentity, + token: mockJwt, + }), + ); return { token: 'eyblob.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iLCJlbnQiOlsidXNlcjpkZWZhdWx0L2ppbW15bWFya3VtIl19.eyblob', diff --git a/plugins/auth-backend/src/providers/cloudflare-access/provider.ts b/plugins/auth-backend/src/providers/cloudflare-access/provider.ts index a2c16fcebf..f7cb7c7283 100644 --- a/plugins/auth-backend/src/providers/cloudflare-access/provider.ts +++ b/plugins/auth-backend/src/providers/cloudflare-access/provider.ts @@ -153,6 +153,7 @@ export type CloudflareAccessResult = { claims: CloudflareAccessClaims; cfIdentity: CloudflareAccessIdentityProfile; expiresInSeconds?: number; + token: string; }; /** @@ -262,7 +263,11 @@ export class CloudflareAccessAuthProvider implements AuthProviderRouteHandlers { const sub = verifyResult.payload.sub; const cfAccessResultStr = await this.cache?.get(`${CACHE_PREFIX}/${sub}`); if (typeof cfAccessResultStr === 'string') { - return JSON.parse(cfAccessResultStr) as CloudflareAccessResult; + const result = JSON.parse(cfAccessResultStr) as CloudflareAccessResult; + return { + ...result, + token: jwt, + }; } const claims = verifyResult.payload as CloudflareAccessClaims; // Builds a passport profile from JWT claims first @@ -273,13 +278,16 @@ export class CloudflareAccessAuthProvider implements AuthProviderRouteHandlers { const cfIdentity = await this.getIdentityProfile(jwt); // Stores a stringified JSON object in cfaccess provider cache only when // we complete all steps - const cfAccessResult: CloudflareAccessResult = { + const cfAccessResult = { claims, cfIdentity, expiresInSeconds: claims.exp - claims.iat, }; this.cache?.set(`${CACHE_PREFIX}/${sub}`, JSON.stringify(cfAccessResult)); - return cfAccessResult; + return { + ...cfAccessResult, + token: jwt, + }; } catch (err) { throw new ForwardedError( 'Failed to populate access identity information',