feat(auth-node): return logoutUrl in logout response when provided by authenticator
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:
@@ -1264,6 +1264,49 @@ describe('createOAuthRouteHandlers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return logoutUrl as JSON when authenticator provides one', async () => {
|
||||
mockAuthenticator.logout.mockResolvedValueOnce({
|
||||
logoutUrl: 'https://example.auth0.com/v2/logout?federated',
|
||||
});
|
||||
|
||||
const agent = request.agent(
|
||||
wrapInApp(createOAuthRouteHandlers(baseConfig)),
|
||||
);
|
||||
|
||||
agent.jar.setCookie(
|
||||
'my-provider-refresh-token=my-refresh-token',
|
||||
'127.0.0.1',
|
||||
'/my-provider',
|
||||
);
|
||||
|
||||
const res = await agent
|
||||
.post('/my-provider/logout')
|
||||
.set('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({
|
||||
logoutUrl: 'https://example.auth0.com/v2/logout?federated',
|
||||
});
|
||||
|
||||
// Cookie should still be cleared even when logoutUrl is returned
|
||||
expect(getRefreshTokenCookie(agent)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return empty body when authenticator logout returns void', async () => {
|
||||
mockAuthenticator.logout.mockResolvedValueOnce(undefined);
|
||||
|
||||
const agent = request.agent(
|
||||
wrapInApp(createOAuthRouteHandlers(baseConfig)),
|
||||
);
|
||||
|
||||
const res = await agent
|
||||
.post('/my-provider/logout')
|
||||
.set('X-Requested-With', 'XMLHttpRequest');
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({});
|
||||
});
|
||||
|
||||
it('should set error search param and redirect on caught error', async () => {
|
||||
const app = wrapInApp(createOAuthRouteHandlers(baseConfig));
|
||||
const res = await request(app)
|
||||
|
||||
@@ -280,9 +280,13 @@ export function createOAuthRouteHandlers<TProfile>(
|
||||
throw new AuthenticationError('Invalid X-Requested-With header');
|
||||
}
|
||||
|
||||
let logoutResult: void | { logoutUrl?: string };
|
||||
if (authenticator.logout) {
|
||||
const refreshToken = cookieManager.getRefreshToken(req);
|
||||
await authenticator.logout({ req, refreshToken }, authenticatorCtx);
|
||||
logoutResult = await authenticator.logout(
|
||||
{ req, refreshToken },
|
||||
authenticatorCtx,
|
||||
);
|
||||
}
|
||||
|
||||
// remove refresh token cookie if it is set
|
||||
@@ -291,7 +295,11 @@ export function createOAuthRouteHandlers<TProfile>(
|
||||
// remove persisted scopes
|
||||
await scopeManager.clear(req);
|
||||
|
||||
res.status(200).end();
|
||||
if (logoutResult?.logoutUrl) {
|
||||
res.status(200).json({ logoutUrl: logoutResult.logoutUrl });
|
||||
} else {
|
||||
res.status(200).end();
|
||||
}
|
||||
},
|
||||
|
||||
async refresh(
|
||||
|
||||
Reference in New Issue
Block a user