feat(auth0): implement federated logout to clear Auth0 and IdP sessions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
This commit is contained in:
Jonathan Roebuck
2026-03-31 14:17:23 +01:00
parent 0ef5a03fb3
commit 97850d0ef1
2 changed files with 54 additions and 1 deletions
@@ -85,7 +85,7 @@ export const auth0Authenticator = createOAuthAuthenticator({
},
),
);
return { helper, audience, connection, connectionScope };
return { helper, audience, connection, connectionScope, domain, clientID };
},
async start(
@@ -115,4 +115,13 @@ export const auth0Authenticator = createOAuthAuthenticator({
async refresh(input, { helper }) {
return helper.refresh(input);
},
async logout(input, { domain, clientID }) {
const origin = input.req.get('origin') ?? '';
return {
logoutUrl: `https://${domain}/v2/logout?federated&client_id=${encodeURIComponent(
clientID,
)}&returnTo=${encodeURIComponent(origin)}`,
};
},
});
@@ -180,4 +180,48 @@ describe('authModuleAuth0Provider', () => {
'Organization mismatch. The organization provided in the request does not match the organization configured in the strategy.',
);
});
it('should return Auth0 logout URL on logout', async () => {
const { server } = await startTestBackend({
features: [
authPlugin,
authModuleAuth0Provider,
mockServices.rootConfig.factory({
data: {
app: {
baseUrl: 'http://localhost:3000',
},
auth: {
providers: {
auth0: {
development: {
clientId: 'test-client-id',
clientSecret: 'clientSecret',
domain: 'test.eu.auth0.com',
},
},
},
session: {
secret: 'secret',
},
},
},
}),
],
});
const res = await request(server)
.post('/api/auth/auth0/logout')
.query({ env: 'development' })
.set('X-Requested-With', 'XMLHttpRequest')
.set('Origin', 'http://localhost:3000');
expect(res.status).toBe(200);
expect(res.body.logoutUrl).toContain('test.eu.auth0.com/v2/logout');
expect(res.body.logoutUrl).toContain('federated');
expect(res.body.logoutUrl).toContain('client_id=test-client-id');
expect(res.body.logoutUrl).toContain(
`returnTo=${encodeURIComponent('http://localhost:3000')}`,
);
});
});