FastAPI logout works but user gets logged in again after page refresh

Summary

FastAPI logout appears to work because the endpoint returns 200 and deletes the auth cookie, but after a page refresh the user is automatically logged in again. This indicates that the cookie is either not being removed with the exact attributes it was set with, or another endpoint is resetting it immediately after logout (e.g., a token verification call that inadvertently sets a new cookie).

Root Cause

  • Cookie attribute mismatch – the logout call uses path="/" but the cookie may have been set with a different path, domain, or missing Secure/SameSite flags, causing the browser to treat them as separate cookies.
  • Token verification endpoint resets the cookie/auth/tokenVerification calls manager.optional(request) and, on success, re‑issues a new auth cookie without clearing the old one.
  • Frontend auto‑runs verifyAuth on load – the Redux thunk verifyAuth is dispatched on page refresh; if the backend still returns a valid user (because the cookie wasn’t cleared), the frontend restores the logged‑in state.
  • Missing withCredentials on logout request – if the logout request omits withCredentials: true, the browser does not send the cookie, so the server cannot delete it.

Why This Happens in Real Systems

  • Inconsistent cookie scoping across login, refresh, and logout routes is common when cookie settings are defined in multiple places.
  • Shared authentication middleware that automatically creates a session cookie on any successful request can unintentionally revive a session after logout.
  • SPA architectures often run an auth‑check on every route change or page load, meaning a single backend slip‑up instantly re‑authenticates the user.
  • Development vs. production environment differences (e.g., secure flag) cause cookies to behave differently locally versus in staging/production.

Real-World Impact

  • Users experience unexpected re‑login, eroding trust in the logout functionality.
  • Security session fixation risk – an old session may remain valid if the cookie isn’t properly cleared.
  • Increased support overhead as users report being logged in after they intended to log out.
  • Potential compliance violations for applications that must guarantee session termination (e.g., finance, healthcare).

Example or Code (if necessary and relevant)

# Correct logout – ensure attributes exactly match those used when setting the cookie
def _clear_auth_cookie(response: Response):
    response.delete_cookie(
        key=manager.cookie_name,
        path="/",               # must match the path used in set_cookie
        domain=None,            # omit if not set when creating; otherwise match
        httponly=True,
        samesite="none",        # must be same as set_cookie
        secure=True,            # must be same as set_cookie
    )

@router.post("/logout")
def logout(response: Response):
    _clear_auth_cookie(response)
    _clear_google_oauth_cookies(response)
    return {"message": "Logged out successfully"}
// Frontend – always send credentials so the cookie is present for deletion
export const verifyAuth = createAsyncThunk(
  "auth/verifyAuth",
  async (_, thunkAPI) => {
    try {
      const res = await axios.post(`${BASE_URL}/auth/tokenVerification`, null, {
        withCredentials: true, // important for cookie‑based auth
      });
      return res.data ?? { valid: false, role: "user", is_active: false, user_id: null, is_super_admin: false };
    } catch {
      return thunkAPI.rejectWithValue(false);
    }
  }
);

How Senior Engineers Fix It

  • Audit cookie attributes in every place they are set (login, refresh, token verification) and ensure logout uses the exact same path, domain, secure, samesite, and httponly values.
  • Remove automatic cookie re‑issuance from verification endpoints; they should only validate, not create, a session cookie.
  • Explicitly call logout before any auth‑check on page load, or design the frontend to skip verifyAuth after a logout navigation.
  • Add tests that simulate login → logout → page refresh and assert that the auth cookie is absent and the Redux state is logged out.
  • Use a centralized cookie helper (e.g., set_auth_cookie and clear_auth_cookie) to guarantee consistency and reduce duplication.

Why Juniors Miss It

  • Assuming that calling delete_cookie always removes the cookie, without checking that the attribute set must be identical.
  • Overlooking that middleware or helper functions may re‑set a cookie after logout.
  • Forgetting to include withCredentials: true on AJAX requests, leading to the server not seeing the cookie to delete.
  • Not writing end‑to‑end tests that cover the full login‑logout‑refresh flow, so the bug only appears in manual testing.

Leave a Comment