Logout
Why proper logout matters 🔒​
A secure logout process is essential for protecting user data, especially on shared devices. This guide shows how to properly terminate sessions by clearing tokens and optionally revoking them server-side.
Make sure you've completed the Quick Start and understand Token Management before implementing logout functionality.
1 · Client-side logout (clearing tokens)​
When implementing logout, the Account Management token is the primary security concern since it identifies the specific user. The implicit token acts more like a public API key to access the store.
Here's a React TypeScript component for logout:
import React from "react";
import { useNavigate } from "react-router-dom";
const LogoutButton: React.FC = () => {
const navigate = useNavigate();
const handleLogout = () => {
// Clear account token (the primary user identity token)
localStorage.removeItem("ep_account_token");
localStorage.removeItem("ep_account_refresh_token");
// The implicit token can optionally be kept as it's not tied to a user identity
// but clearing it ensures a fresh state
localStorage.removeItem("ep_implicit_token");
// Redirect to login page
navigate("/login");
};
return (
<button onClick={handleLogout} className="logout-button">
Sign Out
</button>
);
};
export default LogoutButton;
For cookie-based storage:
import React from "react";
import { useNavigate } from "react-router-dom";
const CookieLogoutButton: React.FC = () => {
const navigate = useNavigate();
const handleLogout = () => {
// Clear cookies by setting expiration in the past
document.cookie =
"ep_account_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
document.cookie =
"ep_implicit_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
// Redirect to login page
navigate("/login");
};
return <button onClick={handleLogout}>Sign Out</button>;
};
export default CookieLogoutButton;
2 · Server-side logout (for HttpOnly cookies)​
If you're using HttpOnly cookies through a server:
// Client-side React component
import React from "react";
import { useNavigate } from "react-router-dom";
const ServerLogoutButton: React.FC = () => {
const navigate = useNavigate();
const [isLoggingOut, setIsLoggingOut] = React.useState(false);
const handleLogout = async () => {
setIsLoggingOut(true);
try {
// Call your backend logout endpoint
await fetch("/api/logout", {
method: "POST",
credentials: "include", // Important to include cookies
});
// Redirect to login page after server confirms logout
navigate("/login");
} catch (error) {
console.error("Logout failed:", error);
// Fallback: redirect anyway
navigate("/login");
} finally {
setIsLoggingOut(false);
}
};
return (
<button onClick={handleLogout} disabled={isLoggingOut}>
{isLoggingOut ? "Signing out..." : "Sign Out"}
</button>
);
};
export default ServerLogoutButton;
// Server-side implementation (Node.js/Express example)
app.post("/api/logout", (req, res) => {
// Clear HttpOnly cookies
res.clearCookie("ep_implicit_token");
res.clearCookie("ep_account_token");
res.status(200).json({ success: true });
});
Note on token invalidation
Elastic Path currently does not offer a direct API endpoint to revoke account tokens. The most secure approach is to:
- Clear all tokens from client storage
- For server-rendered applications, clear any session state on your server
- For added security with HttpOnly cookies, set an immediate expiry on the server
3 · Handling logout across multiple tabs​
While localStorage is shared across all tabs from the same domain (meaning tokens removed in one tab are removed everywhere), other tabs won't automatically detect this change and update their UI state unless specifically notified. Here's how to synchronize logout UI state across tabs:
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
const MultiTabLogoutButton: React.FC = () => {
const navigate = useNavigate();
const [isAuthenticated, setIsAuthenticated] = useState(true);
// Listen for logout events from other tabs
useEffect(() => {
const checkAuth = () => {
const accountToken = localStorage.getItem("ep_account_token");
setIsAuthenticated(!!accountToken);
// If no token found, redirect to login
if (!accountToken && isAuthenticated) {
navigate("/login");
}
};
// Initial check
checkAuth();
// Event handler for storage changes
const handleStorageChange = (event: StorageEvent) => {
// When another tab triggers logout by setting this flag
if (event.key === "ep_user_logged_out") {
checkAuth();
}
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, [navigate, isAuthenticated]);
const handleLogout = () => {
// Clear tokens (will affect all tabs)
localStorage.removeItem("ep_account_token");
localStorage.removeItem("ep_implicit_token");
// Notify other tabs about logout
localStorage.setItem("ep_user_logged_out", Date.now().toString());
// Update local state and navigate
setIsAuthenticated(false);
navigate("/login");
};
return (
<button onClick={handleLogout} disabled={!isAuthenticated}>
Sign Out
</button>
);
};
export default MultiTabLogoutButton;
The key point here isn't about clearing the tokens (which are automatically shared), but rather notifying other tabs to update their UI state in response to the tokens being removed. This synchronization helps provide a consistent experience across all open tabs.
4 · Automatically logging out inactive users​
For security-sensitive applications, you might want to automatically log out users after a period of inactivity:
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
const INACTIVITY_LIMIT = 30 * 60 * 1000; // 30 minutes
const InactivityMonitor: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const navigate = useNavigate();
useEffect(() => {
let inactivityTimeout: number | undefined;
// Function to logout and redirect
const logout = () => {
localStorage.removeItem("ep_account_token");
localStorage.removeItem("ep_implicit_token");
navigate("/login");
};
// Reset the timer on user activity
const resetTimer = () => {
if (inactivityTimeout) window.clearTimeout(inactivityTimeout);
inactivityTimeout = window.setTimeout(logout, INACTIVITY_LIMIT);
};
// Monitor user activity
const events = [
"mousedown",
"mousemove",
"keypress",
"scroll",
"touchstart",
];
events.forEach((event) => {
document.addEventListener(event, resetTimer, true);
});
// Initial timer setup
resetTimer();
// Cleanup
return () => {
if (inactivityTimeout) window.clearTimeout(inactivityTimeout);
events.forEach((event) => {
document.removeEventListener(event, resetTimer, true);
});
};
}, [navigate]);
return <>{children}</>;
};
// Usage in your app
const App: React.FC = () => {
return (
<InactivityMonitor>
<YourApp />
</InactivityMonitor>
);
};
export default App;
When considering which tokens to clear during logout, remember:
- Account Management token - This is the critical one that represents user identity and must always be cleared during logout
- Implicit token - This functions more like a public API key to access the store and could technically be preserved, but it's generally cleaner to clear it as well
5 · Best practices​
- Prioritize clearing Account tokens: The Account Management token is what identifies the user, so always clear it
- Use a consistent logout flow: Apply the same pattern across your application
- Provide visual feedback: Show users that logout is in progress/complete
- Handle errors gracefully: If logout fails, ensure a fallback path is available
- Consider server-side session tracking: For additional security, maintain session state server-side
For complete implementations, check out the authentication examples in the Composable Frontend repository.