signOut

The signOut method signs out the current user by clearing all stored authentication data and redirecting to a specified URL. It ensures complete cleanup of the authentication state.

This method is accessed via authClient.signOut() and provides a clean way to log users out of your application.


Method Signature

signOut(redirectUri?: string): void

Parameters

redirectUri

  • type: string (optional)
  • URL to redirect to after sign out
  • Default: '/' (homepage)

Return Value

Returns void. The method performs its operations synchronously and redirects immediately.


How It Works

  1. Clears localStorage - Removes the vuer_token key containing access and refresh tokens
  2. Clears sessionStorage - Removes all PKCE parameters (pkce_state, pkce_verifier, vuer_callback_url)
  3. Redirects the page - Navigates to the specified URL (default: homepage)

Usage

Basic Sign Out

import { authClient } from "@/auth";

function SignOutButton() {
  return (
    <button onClick={() => authClient.signOut()}>
      Sign Out
    </button>
  );
}

Sign Out with Custom Redirect

import { authClient } from "@/auth";

function SignOutButton() {
  const handleSignOut = () => {
    // Redirect to login page after sign out
    authClient.signOut("/login");
  };

  return <button onClick={handleSignOut}>Sign Out</button>;
}

Sign Out with Confirmation

import { authClient } from "@/auth";

function SignOutButton() {
  const handleSignOut = () => {
    if (confirm("Are you sure you want to sign out?")) {
      authClient.signOut();
    }
  };

  return <button onClick={handleSignOut}>Sign Out</button>;
}

Sign Out from Navigation Menu

import { authClient } from "@/auth";

function UserMenu() {
  const { user, isAuthenticated } = authClient.useAuth();

  if (!isAuthenticated || !user) return null;

  return (
    <div className="user-menu">
      <img src={user.picture} alt={user.name} />
      <span>{user.name}</span>
      <button onClick={() => authClient.signOut()}>
        Sign Out
      </button>
    </div>
  );
}

Sign Out with Loading State

import { authClient } from "@/auth";
import { useState } from "react";

function SignOutButton() {
  const [isSigningOut, setIsSigningOut] = useState(false);

  const handleSignOut = async () => {
    setIsSigningOut(true);

    // Optional: Call backend to invalidate server-side session
    try {
      await fetch("/api/logout", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${authClient.token()?.access_token}`,
        },
      });
    } catch (error) {
      console.error("Failed to invalidate server session:", error);
    }

    // Sign out client-side
    authClient.signOut();
  };

  return (
    <button onClick={handleSignOut} disabled={isSigningOut}>
      {isSigningOut ? "Signing out..." : "Sign Out"}
    </button>
  );
}

Sign Out from Multiple Locations

import { authClient } from "@/auth";

function Header() {
  return (
    <header>
      <button onClick={() => authClient.signOut()}>Sign Out</button>
    </header>
  );
}

function ProfilePage() {
  return (
    <div>
      <h1>Profile</h1>
      <button onClick={() => authClient.signOut("/login")}>
        Sign Out
      </button>
    </div>
  );
}

Complete Sign Out Flow

For a complete sign out that includes server-side cleanup:

import { authClient } from "@/auth";

async function completeSignOut() {
  try {
    // 1. Optional: Revoke refresh token on server
    const token = authClient.token();
    if (token?.refresh_token) {
      await fetch("/api/auth/revoke", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token.access_token}`,
        },
        body: JSON.stringify({
          token: token.refresh_token,
          token_type_hint: "refresh_token",
        }),
      });
    }
  } catch (error) {
    console.error("Failed to revoke token:", error);
    // Continue with client-side sign out even if server revocation fails
  }

  // 2. Clear client-side state and redirect
  authClient.signOut("/login");
}

function SignOutButton() {
  return <button onClick={completeSignOut}>Sign Out</button>;
}

What Gets Cleared

localStorage

  • vuer_token - Access token, refresh token, ID token, and metadata

sessionStorage

  • pkce_state - PKCE state parameter
  • pkce_verifier - PKCE code verifier
  • vuer_callback_url - Stored callback URL

In-Memory State

  • Cached token (cleared implicitly when localStorage is cleared)
  • Zustand store state (user info, loading states)

Redirect Behavior

After sign out, the page is redirected using window.location.href:

// Default behavior
authClient.signOut(); // Redirects to '/'

// Custom redirect
authClient.signOut("/login"); // Redirects to '/login'
authClient.signOut("/goodbye"); // Redirects to '/goodbye'

The redirect is immediate and synchronous, so any code after signOut() will not execute:

// ❌ This won't work
authClient.signOut();
console.log("This will never execute");

// ✅ Do cleanup before sign out
console.log("Signing out...");
authClient.signOut();

Common Patterns

Sign Out on Token Expiration

import { authClient } from "@/auth";
import { useEffect } from "react";

function TokenExpirationHandler() {
  useEffect(() => {
    const checkTokenExpiration = () => {
      const token = authClient.token();

      if (token) {
        const expiresAt = Date.now() + token.expires_in * 1000;
        const isExpired = Date.now() >= expiresAt;

        if (isExpired) {
          alert("Your session has expired. Please sign in again.");
          authClient.signOut("/login");
        }
      }
    };

    const interval = setInterval(checkTokenExpiration, 60000); // Check every minute
    return () => clearInterval(interval);
  }, []);

  return null;
}

Sign Out on 401 Error

import { authClient } from "@/auth";

async function fetchWithAutoSignOut(url: string) {
  const response = await authClient.$fetch(url);

  if (response.status === 401) {
    alert("Your session has expired. Please sign in again.");
    authClient.signOut("/login");
    return null;
  }

  return response;
}

Sign Out All Tabs

import { authClient } from "@/auth";
import { useEffect } from "react";

function GlobalSignOutHandler() {
  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      // If token was removed in another tab, sign out in this tab too
      if (e.key === "vuer_token" && e.newValue === null) {
        window.location.href = "/login";
      }
    };

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, []);

  return null;
}

Security Considerations

  1. Client-Side Only

    • signOut() only clears client-side data
    • For complete security, also invalidate the session on your backend
    • Consider revoking refresh tokens on the auth server
  2. No Server Communication

    • By default, no request is made to the server
    • Server-side sessions or tokens remain valid until they expire
    • Implement server-side sign out if needed
  3. Immediate Effect

    • All authentication data is cleared immediately
    • The redirect prevents any further code execution
    • User cannot access protected resources after sign out

Troubleshooting

User still appears signed in after sign out:

  • Check if using proper storage keys
  • Verify localStorage is not disabled
  • Check for browser extensions that might cache data

Redirect doesn't work:

  • Ensure the URL is valid and accessible
  • Check for JavaScript errors that might prevent redirect
  • Verify no other code is preventing navigation

Sign out doesn't clear all data:

  • The method only clears vuer-auth-specific keys
  • Application-specific data should be cleared separately
  • Consider implementing a custom sign out function for complete cleanup

See Also