Tokens and Sessions

Refresh, store, verify, and revoke AuthOS session tokens safely

AuthOS release 0.8.2 TypeScript SDK 0.8.0 Latest-only documentation
Updated Jul 15, 2026 Edit this page
On this page

Start after completing a login flow from the authentication flows overview.

Token Refresh Workflow

Access tokens expire after a period of time (typically 1 hour). Use refresh tokens to obtain new access tokens without requiring the user to log in again.

When to Refresh Tokens

  • When an API call returns a 401 Unauthorized error
  • Proactively before the access token expires (if you track expiration)
  • On application startup if the stored token might be expired

Basic Token Refresh

async function refreshAccessToken() {
  const refreshToken = localStorage.getItem('sso_refresh_token');

  if (!refreshToken) {
    // No refresh token - user needs to log in
    window.location.href = '/login';
    return null;
  }

  try {
    const tokens = await sso.auth.refreshToken(refreshToken);

    // Store new tokens (both are rotated)
    localStorage.setItem('sso_access_token', tokens.access_token);
    localStorage.setItem('sso_refresh_token', tokens.refresh_token);

    // Update SDK
    sso.setAuthToken(tokens.access_token);

    return tokens.access_token;
  } catch (error) {
    console.error('Token refresh failed:', error);
    // Refresh token invalid or expired - user must log in
    localStorage.removeItem('sso_access_token');
    localStorage.removeItem('sso_refresh_token');
    window.location.href = '/login';
    return null;
  }
}

Automatic Token Refresh with Interceptor

Implement automatic token refresh by intercepting 401 errors:

import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';

class AuthenticatedSsoClient {
  private sso: SsoClient;
  private isRefreshing: boolean = false;
  private refreshPromise: Promise<string | null> | null = null;

  constructor(baseURL: string) {
    const accessToken = localStorage.getItem('sso_access_token');
    this.sso = new SsoClient({ baseURL, token: accessToken || undefined });
  }

  async request<T>(fn: () => Promise<T>): Promise<T> {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof SsoApiError && error.statusCode === 401) {
        // Token expired, try to refresh
        const newToken = await this.refreshToken();

        if (newToken) {
          // Retry the original request with new token
          return await fn();
        }
      }
      throw error;
    }
  }

  private async refreshToken(): Promise<string | null> {
    // Prevent multiple simultaneous refresh attempts
    if (this.isRefreshing && this.refreshPromise) {
      return this.refreshPromise;
    }

    this.isRefreshing = true;
    this.refreshPromise = this.performRefresh();

    const result = await this.refreshPromise;
    this.isRefreshing = false;
    this.refreshPromise = null;

    return result;
  }

  private async performRefresh(): Promise<string | null> {
    const refreshToken = localStorage.getItem('sso_refresh_token');

    if (!refreshToken) {
      this.redirectToLogin();
      return null;
    }

    try {
      const tokens = await this.sso.auth.refreshToken(refreshToken);

      localStorage.setItem('sso_access_token', tokens.access_token);
      localStorage.setItem('sso_refresh_token', tokens.refresh_token);
      this.sso.setAuthToken(tokens.access_token);

      return tokens.access_token;
    } catch (error) {
      console.error('Token refresh failed:', error);
      this.redirectToLogin();
      return null;
    }
  }

  private redirectToLogin() {
    localStorage.removeItem('sso_access_token');
    localStorage.removeItem('sso_refresh_token');
    window.location.href = '/login';
  }

  getClient(): SsoClient {
    return this.sso;
  }
}

// Usage
const authClient = new AuthenticatedSsoClient('https://sso.example.com');

// Wrap all API calls with automatic retry
const profile = await authClient.request(() =>
  authClient.getClient().user.getProfile()
);

React Hook for Token Management

import { useEffect, useState } from 'react';
import { SsoClient } from '@drmhse/sso-sdk';

export function useAuth() {
  const [sso, setSso] = useState<SsoClient | null>(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    initializeAuth();
  }, []);

  const initializeAuth = async () => {
    const accessToken = localStorage.getItem('sso_access_token');
    const refreshToken = localStorage.getItem('sso_refresh_token');

    if (!accessToken || !refreshToken) {
      setIsLoading(false);
      return;
    }

    const client = new SsoClient({
      baseURL: process.env.REACT_APP_SSO_URL!,
      token: accessToken
    });

    try {
      // Try to fetch profile to verify token
      await client.user.getProfile();
      setSso(client);
      setIsAuthenticated(true);
    } catch (error) {
      // Token invalid, try to refresh
      try {
        const tokens = await client.auth.refreshToken(refreshToken);
        localStorage.setItem('sso_access_token', tokens.access_token);
        localStorage.setItem('sso_refresh_token', tokens.refresh_token);
        client.setAuthToken(tokens.access_token);
        setSso(client);
        setIsAuthenticated(true);
      } catch (refreshError) {
        // Refresh failed, clear tokens
        localStorage.removeItem('sso_access_token');
        localStorage.removeItem('sso_refresh_token');
      }
    } finally {
      setIsLoading(false);
    }
  };

  const logout = async () => {
    if (sso) {
      try {
        await sso.auth.logout();
      } catch (error) {
        console.error('Logout error:', error);
      }
    }

    localStorage.removeItem('sso_access_token');
    localStorage.removeItem('sso_refresh_token');
    setSso(null);
    setIsAuthenticated(false);
  };

  return {
    sso,
    isAuthenticated,
    isLoading,
    logout
  };
}

// Usage in components
function App() {
  const { sso, isAuthenticated, isLoading } = useAuth();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <LoginPage />;
  }

  return <Dashboard sso={sso!} />;
}

Best Practices

Token Storage

Web Applications:

  • Use localStorage for single-tab persistence
  • Use sessionStorage for single-session security
  • Consider using secure HTTP-only cookies for sensitive applications

Mobile Applications:

  • Use secure storage APIs (iOS Keychain, Android Keystore)
  • Never store tokens in plain text files

CLI Applications:

  • Store tokens in user home directory with restricted permissions
  • Use OS-specific secure storage when available

Security Considerations

  1. HTTPS Only: Always use HTTPS in production
  2. Token Rotation: The platform implements automatic refresh token rotation
  3. XSS Protection: Sanitize all user inputs to prevent token theft
  4. CSRF Protection: Use state parameters for OAuth flows
  5. Logout Everywhere: Call sso.auth.logout() to revoke tokens server-side