[Feature]: Use Redirect URL Returned by `handleLogout` in BaseAuthProvider
Description
Problem
Currently, all AdminJS authentication adapters (@adminjs/express, @adminjs/fastify, @adminjs/nestjs) handle logout by destroying the local session and redirecting to the login URL, for example:
router.get('/logout', (req, res) => {
req.session.destroy(() => res.redirect(admin.options.rootPath + '/login'));
});
This works for simple local authentication but does not support OAuth/OIDC logout flows, such as with:
- Keycloak
- Auth0
- Azure AD
- Google Workspace
In these cases, AdminJS only clears the local session but never calls the provider’s logout endpoint — so the user remains authenticated at the provider level. When they revisit the login page, they are automatically re-logged in (silent login).
Why This Matters
Each OAuth provider has its own logout URL (e.g., Keycloak’s protocol/openid-connect/logout), and these need to be called explicitly.
Because AdminJS plugins manage logout internally and redirect immediately, there’s no clean way to integrate provider-specific logout behavior.
Suggested Solution
When the configured BaseAuthProvider implements handleLogout, and that function returns a redirect URL, the adapters should redirect to that URL instead of hardcoding the login redirect. This is help
// Current behavior
router.get('/logout', async (req, res) => {
await provider.handleLogout(req, res);
req.session.destroy(() => res.redirect(admin.options.rootPath + '/login'));
});
// Proposed behavior
router.get('/logout', async (req, res) => {
const redirectUrl = await provider.handleLogout?.(req, res);
// Always destroy the session
if (req.session) {
await new Promise(resolve => req.session.destroy(resolve));
}
// If handleLogout returned a URL, redirect there
if (redirectUrl) {
return res.redirect(redirectUrl);
}
// Otherwise, fallback to AdminJS login path
return res.redirect(admin.options.rootPath + '/login');
});
Benefits
- Proper OAuth logout support: enables redirecting to provider logout endpoints.
-
Backward compatible: existing providers continue to work if
handleLogoutreturnsundefined. - Minimal change: no breaking API changes or new interfaces.
Alternatives
No response
Additional Context
No response
I'm using Firebase Authentication as provider.
In the handleLogout of my Firebase Provider I revoke the tokens from the current user and then the AdminJs destroys the session and redirect to login afterwards.
export class FirebaseAuthProvider extends BaseAuthProvider<ExpressContext> {
// ...
async handleLogout(context: ExpressContext): Promise<void> {
const token = context.req.session?.adminUser?._auth?.accessToken;
if (token) {
await this.revokeFirebaseTokens(token);
}
}
}
You can save under the _auth property any identifier of your user in the provider.
@leonakao I think, this only revoke the token but not the user session in the provider(Keycloak)?
Let me give it a try.