From 9244b70c57817fdc7655508754249c72eb0dda8b Mon Sep 17 00:00:00 2001 From: Jonathan Roebuck Date: Tue, 31 Mar 2026 15:15:51 +0100 Subject: [PATCH] chore: add changesets, update API reports, fix type errors Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Jonathan Roebuck --- .changeset/auth-node-logout-result.md | 5 +++++ .changeset/auth0-federated-logout.md | 5 +++++ .changeset/core-app-api-logout-redirect.md | 5 +++++ plugins/auth-node/report.api.md | 10 +++++++++- .../src/oauth/createOAuthRouteHandlers.test.ts | 4 ++-- .../auth-node/src/oauth/createOAuthRouteHandlers.ts | 2 +- 6 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .changeset/auth-node-logout-result.md create mode 100644 .changeset/auth0-federated-logout.md create mode 100644 .changeset/core-app-api-logout-redirect.md diff --git a/.changeset/auth-node-logout-result.md b/.changeset/auth-node-logout-result.md new file mode 100644 index 0000000000..a571fde853 --- /dev/null +++ b/.changeset/auth-node-logout-result.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': minor +--- + +Added `OAuthAuthenticatorLogoutResult` type. The `logout` method on `OAuthAuthenticator` can now optionally return `{ logoutUrl }` to trigger a browser redirect after sign-out. This allows providers like Auth0 to clear their session cookies by redirecting to their logout endpoint. diff --git a/.changeset/auth0-federated-logout.md b/.changeset/auth0-federated-logout.md new file mode 100644 index 0000000000..ad50b03ac9 --- /dev/null +++ b/.changeset/auth0-federated-logout.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-auth0-provider': minor +--- + +Added federated logout support. On sign-out, the Auth0 authenticator now returns a logout URL that redirects the browser to Auth0's `/v2/logout?federated` endpoint, clearing both the Auth0 session and any upstream IdP session. This ensures users must fully re-authenticate after signing out. diff --git a/.changeset/core-app-api-logout-redirect.md b/.changeset/core-app-api-logout-redirect.md new file mode 100644 index 0000000000..32aa405e94 --- /dev/null +++ b/.changeset/core-app-api-logout-redirect.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +The `DefaultAuthConnector` now checks for a `logoutUrl` in the logout response body. If the auth provider returns one (e.g. Auth0 federated logout), the browser is redirected to that URL to clear the provider's session cookies. This is backward compatible — providers that return an empty response are unaffected. diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index b360c2ae6a..c653897e21 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -293,7 +293,10 @@ export interface OAuthAuthenticator { // (undocumented) initialize(ctx: { callbackUrl: string; config: Config }): TContext; // (undocumented) - logout?(input: OAuthAuthenticatorLogoutInput, ctx: TContext): Promise; + logout?( + input: OAuthAuthenticatorLogoutInput, + ctx: TContext, + ): Promise; // (undocumented) refresh( input: OAuthAuthenticatorRefreshInput, @@ -329,6 +332,11 @@ export interface OAuthAuthenticatorLogoutInput { req: Request_2; } +// @public (undocumented) +export interface OAuthAuthenticatorLogoutResult { + logoutUrl?: string; +} + // @public (undocumented) export interface OAuthAuthenticatorRefreshInput { // (undocumented) diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts index c0fece0eaf..62e7246003 100644 --- a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts @@ -1265,7 +1265,7 @@ describe('createOAuthRouteHandlers', () => { }); it('should return logoutUrl as JSON when authenticator provides one', async () => { - mockAuthenticator.logout.mockResolvedValueOnce({ + (mockAuthenticator.logout as jest.Mock).mockResolvedValueOnce({ logoutUrl: 'https://example.auth0.com/v2/logout?federated', }); @@ -1293,7 +1293,7 @@ describe('createOAuthRouteHandlers', () => { }); it('should return empty body when authenticator logout returns void', async () => { - mockAuthenticator.logout.mockResolvedValueOnce(undefined); + (mockAuthenticator.logout as jest.Mock).mockResolvedValueOnce(undefined); const agent = request.agent( wrapInApp(createOAuthRouteHandlers(baseConfig)), diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts index 0d268eb052..d12060b20f 100644 --- a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts @@ -280,7 +280,7 @@ export function createOAuthRouteHandlers( throw new AuthenticationError('Invalid X-Requested-With header'); } - let logoutResult: void | { logoutUrl?: string }; + let logoutResult: void | { logoutUrl?: string } = undefined; if (authenticator.logout) { const refreshToken = cookieManager.getRefreshToken(req); logoutResult = await authenticator.logout(