Passport

Token Management

Managing OAuth2 tokens, refresh, and expiration with Laravel Passport

Learn how to manage OAuth2 tokens, handle automatic refresh, and deal with token expiration in Laravel Passport.


Token Overview

Laravel Passport uses OAuth2 tokens for authentication. Nuxt Umbu handles token management automatically, including:

  • Access tokens - Short-lived tokens for API requests
  • Refresh tokens - Long-lived tokens for obtaining new access tokens
  • Token expiration - Automatic detection and refresh
  • Secure storage - HTTP-only cookies and localStorage fallback

Token Storage

Server-Side (SSR)

Tokens are stored in secure HTTP-only cookies:

// Automatic cookie management
const tokenCookie = `${prefix}_token.${strategyName}`
const refreshCookie = `${prefix}_refresh.${strategyName}`
const expirationCookie = `${prefix}_token_expiration.${strategyName}`

Client-Side

Tokens fall back to localStorage for SPA mode:

// Client-side storage
localStorage.setItem(`${prefix}_token.${strategyName}`, token)
localStorage.setItem(`${prefix}_refresh.${strategyName}`, refreshToken)
localStorage.setItem(`${prefix}_token_expiration.${strategyName}`, expires)

Automatic Token Refresh

Nuxt Umbu automatically refreshes tokens when they're about to expire.

Configuration

nuxt.config.ts
export default defineNuxtConfig({
  auth: {
    provider: 'passport',
    strategies: {
      client: {
        endpoints: {
          refresh: {
            url: '/oauth/token/refresh',
            method: 'POST',
            alias: 'token-refresh'
          }
        }
      }
    }
  }
})

Manual Token Refresh

<script setup>
const { fetchProfile } = useAuth()

const refreshToken = async () => {
  try {
    await fetchProfile('client')
    // Token automatically refreshed if needed
  } catch (error) {
    console.error('Token refresh failed:', error)
    // Handle refresh failure (e.g., redirect to login)
  }
}
</script>

Token Expiration Handling

Check Token Status

<script setup>
const { loggedIn, user } = useAuth()

const isTokenExpired = computed(() => {
  if (!loggedIn.value) return true
  
  const expiration = localStorage.getItem('__Secure-_token_expiration.client')
  return expiration ? Date.now() > parseInt(expiration) : false
})

// Watch for token expiration
watch(isTokenExpired, (expired) => {
  if (expired && loggedIn.value) {
    console.log('Token expired, need to re-authenticate')
    // Redirect to login or refresh token
  }
})
</script>

Token Expiration Event

<script setup>
const { fetchProfile } = useAuth()

onMounted(async () => {
  try {
    await fetchProfile('client')
    // This will automatically refresh expired tokens
  } catch (error) {
    // Token is invalid, redirect to login
    await navigateTo('/login')
  }
})
</script>

Custom Token Logic

Token Interceptor

plugins/auth-interceptor.client.ts
export default defineNuxtPlugin(() => {
  const { $fetch: originalFetch } = useNuxtApp()
  const { fetchProfile } = useAuth()
  
  // Intercept API calls to check token validity
  const interceptedFetch = async (url, options) => {
    try {
      return await originalFetch(url, options)
    } catch (error) {
      if (error.statusCode === 401) {
        // Try to refresh token
        try {
          await fetchProfile('client')
          return await originalFetch(url, options)
        } catch (refreshError) {
          // Refresh failed, redirect to login
          await navigateTo('/login')
        }
      }
      throw error
    }
  }
  
  return {
    provide: {
      $fetch: interceptedFetch
    }
  }
})

Token Validation Middleware

middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const { loggedIn, fetchProfile } = useAuth()
  
  if (!loggedIn.value) {
    return navigateTo('/login')
  }
  
  try {
    await fetchProfile('client')
    // Token is valid
  } catch (error) {
    // Token is invalid, redirect to login
    return navigateTo('/login')
  }
})

Advanced Token Management

Multiple Token Strategies

nuxt.config.ts
export default defineNuxtConfig({
  auth: {
    provider: 'passport',
    strategies: {
      admin: {
        endpoints: {
          refresh: { url: '/api/admin/refresh', method: 'POST' }
        }
      },
      api: {
        endpoints: {
          refresh: { url: '/api/refresh', method: 'POST' }
        }
      }
    }
  }
})

Token Refresh with Custom Logic

<script setup>
const { fetchProfile } = useAuth()

const customTokenRefresh = async (strategyName: string) => {
  try {
    // Custom refresh logic
    const response = await $fetch('/custom-refresh', {
      method: 'POST',
      body: { strategy: strategyName }
    })
    
    // Update token storage manually
    if (response.token) {
      const prefix = '__Secure-'
      localStorage.setItem(`${prefix}_token.${strategyName}`, response.token)
      localStorage.setItem(`${prefix}_token_expiration.${strategyName}`, response.expires)
    }
    
    return response
  } catch (error) {
    console.error('Custom refresh failed:', error)
    throw error
  }
}
</script>

Token Security Best Practices

Secure Token Transmission

// Tokens are automatically included in headers
const { headers } = useAuth()

// Manual API call with auth headers
const apiCall = await $fetch('/api/protected', {
  headers: {
    // Authorization header is automatically added
    ...headers
  }
})

Token Cleanup on Logout

<script setup>
const { logout } = useAuth()

const handleLogout = async () => {
  try {
    await logout('client')
    // All tokens automatically cleared:
    // - Access token
    // - Refresh token  
    // - Expiration timestamps
    // - 2FA tokens
  } catch (error) {
    console.error('Logout error:', error)
  }
}
</script>

Token Storage Security

nuxt.config.ts
export default defineNuxtConfig({
  auth: {
    cookie: {
      prefix: '__Secure-', // Secure prefix
      options: {
        httpOnly: true,    // Prevent XSS
        secure: true,      // HTTPS only
        sameSite: 'lax',   // CSRF protection
        priority: 'high'   // Cookie priority
      }
    }
  }
})

Debugging Token Issues

Token Inspector

<template>
  <div class="token-debug">
    <h3>Token Status</h3>
    <p>Logged In: {{ loggedIn }}</p>
    <p>Strategy: {{ strategy }}</p>
    <p>Token Present: !!token</p>
    <p>Expires: {{ tokenExpiration }}</p>
    <p>Is Expired: isTokenExpired</p>
  </div>
</template>

<script setup>
const { loggedIn, strategy, token, tokenExpiration } = useAuth()

const isTokenExpired = computed(() => {
  if (!tokenExpiration.value) return true
  return Date.now() > parseInt(tokenExpiration.value)
})
</script>

Common Token Issues

IssueCauseSolution
Token not foundUser not logged inRedirect to login
Token expiredToken reached expirationRefresh token or re-authenticate
Invalid tokenToken revoked or corruptedClear storage and re-authenticate
Refresh failedRefresh token expiredForce logout and re-authenticate

Next: Learn about Two-Factor Authentication for enhanced security.