Skip to main content
POST
/
api
/
logout
POST /api/logout
curl --request POST \
  --url https://api.example.com/api/logout \
  --header 'Authorization: <authorization>'
{
  "success": true,
  "message": "<string>"
}

Descripción

Cierra la sesión del usuario autenticado, revocando su token Sanctum y eliminando la sesión PHP. El token usado en la solicitud queda inválido inmediatamente.

Características

  • Revocación de token: El token Sanctum se elimina de la base de datos
  • Limpieza de sesión PHP: Invalida la sesión web (para exportaciones)
  • Regeneración de CSRF: Genera nuevo token CSRF para prevenir ataques
  • Instantáneo: El token deja de funcionar inmediatamente

Request

Headers

Authorization
string
required
Bearer token obtenido en el login
Authorization: Bearer 1|abc123def456ghi789jklmnopqrstuvwxyz
Si el token ya fue revocado o es inválido, recibirás un error 401 antes de llegar al endpoint.

Body

Este endpoint no requiere body (vacío).

Response

success
boolean
required
Siempre true si se ejecutó correctamente
message
string
required
Mensaje de confirmación: "Sesión cerrada correctamente"

Ejemplos

curl -X POST "https://tu-dominio.com/api/logout" \
  -H "Authorization: Bearer 1|abc123def456ghi789jklmnopqrstuvwxyz"

Respuestas

200 OK - Logout Exitoso

{
  "success": true,
  "message": "Sesión cerrada correctamente"
}

401 Unauthorized - Token Inválido

{
  "message": "Unauthenticated."
}
Este error es lanzado por el middleware auth:sanctum si el token no existe o ya fue revocado.

Implementación Interna

Código del Controlador

app/Http/Controllers/Api/AuthController.php:97
public function logout(Request $request)
{
    // Revocar el token oficial de Sanctum
    $request->user()->currentAccessToken()->delete();

    // Cerrar sesión de Laravel (PHP Session)
    \Illuminate\Support\Facades\Auth::logout();
    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return response()->json([
        'success' => true,
        'message' => 'Sesión cerrada correctamente'
    ]);
}

Pasos de Ejecución

1

Revocación del token Sanctum

$request->user()->currentAccessToken()->delete();
Elimina el token de la tabla personal_access_tokens. El token se vuelve inválido inmediatamente.
2

Cierre de sesión PHP

Auth::logout();
Desautentica al usuario de la sesión web de Laravel.
3

Invalidación de sesión

$request->session()->invalidate();
Elimina todos los datos de la sesión PHP actual.
4

Regeneración de token CSRF

$request->session()->regenerateToken();
Genera un nuevo token CSRF para prevenir ataques de sesión.
5

Respuesta exitosa

Retorna JSON confirmando el cierre de sesión.

Limpieza en Frontend

Después de un logout exitoso, el frontend debe:

Eliminar token

localStorage.removeItem('auth_token');
// o
sessionStorage.removeItem('auth_token');

Limpiar datos de usuario

localStorage.removeItem('user');
localStorage.removeItem('permissions');
localStorage.removeItem('empresas');

Resetear estado global

// Zustand
authStore.getState().reset();

// Redux
dispatch(logout());

// Pinia
authStore.$reset();

Redirigir a login

window.location.href = '/login';
// o
router.push('/login');

Casos de Uso

Logout Voluntario

Usuario hace clic en “Cerrar Sesión”:
const handleLogout = async () => {
  try {
    const response = await fetch('/api/logout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
      }
    });
    
    if (response.ok) {
      // Limpiar y redirigir
      localStorage.clear();
      window.location.href = '/login';
    }
  } catch (error) {
    console.error('Error al cerrar sesión:', error);
  }
};

Logout por Token Expirado

Cuando una petición retorna 401, cerrar sesión automáticamente:
services/api.js
import axios from 'axios';

const api = axios.create({
  baseURL: '/api',
});

// Interceptor de respuesta
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Token expirado o inválido
      localStorage.clear();
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default api;

Logout al Cambiar de Rol

Cuando un admin cambia los permisos de un rol activo:
const updateRolePermissions = async (rolId, permissions) => {
  await api.put(`/permissions/role/${rolId}`, { permissions });
  
  // Si el usuario actual tiene ese rol, hacer logout
  if (currentUser.rol_id === rolId) {
    await handleLogout();
    alert('Tus permisos han cambiado. Por favor, inicia sesión nuevamente.');
  }
};

Seguridad

Prevención de Reutilización

Una vez revocado, el token no puede ser reutilizado:
# Primer uso (exitoso)
curl -X POST "/api/logout" -H "Authorization: Bearer {token}"
# Respuesta: 200 OK

# Segundo uso (fallido)
curl -X POST "/api/logout" -H "Authorization: Bearer {token}"
# Respuesta: 401 Unauthorized

Revocación en Base de Datos

El token se elimina físicamente de personal_access_tokens:
DELETE FROM personal_access_tokens 
WHERE id = (SELECT tokenable_id FROM ... WHERE token = 'hash_del_token');

Cierre de Todas las Sesiones

Para cerrar sesión en todos los dispositivos:
// Revocar TODOS los tokens del usuario
$user->tokens()->delete();
Esto es útil cuando:
  • El usuario cambia su contraseña
  • Se detecta actividad sospechosa
  • El usuario lo solicita explícitamente

Diferencias con Refresh

AspectoLogoutRefresh
PropósitoTerminar sesiónRenovar token
Token actualSe eliminaSe elimina
Token nuevoNo se generaSe genera
Sesión PHPSe invalidaSe mantiene
RedirecciónA loginNinguna
UsoCerrar sesión voluntarioMantener sesión activa

Códigos de Estado

CódigoDescripciónCausa
200OKLogout exitoso
401UnauthorizedToken inválido o ya revocado
500Internal Server ErrorError de base de datos

Mejores Prácticas

Confirmación de Usuario

Muestra un modal de confirmación antes de cerrar sesión:
const confirmLogout = () => {
  if (confirm('¿Estás seguro que deseas cerrar sesión?')) {
    handleLogout();
  }
};

Guardado Automático

Guarda cambios pendientes antes de cerrar sesión:
const safeLogout = async () => {
  if (hasUnsavedChanges()) {
    await saveChanges();
  }
  await handleLogout();
};

Limpieza Completa

Elimina TODOS los datos sensibles del navegador:
localStorage.clear();
sessionStorage.clear();
// Limpiar cookies si es necesario

Logging de Auditoría

Registra el evento de logout en logs del servidor:
Log::info('Usuario cerró sesión', [
    'user_id' => $user->id,
    'ip' => $request->ip(),
    'user_agent' => $request->userAgent()
]);

Testing

PHPUnit

tests/Feature/LogoutTest.php
class LogoutTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_logout()
    {
        $user = User::factory()->create();
        $token = $user->createToken('test_token')->plainTextToken;

        $response = $this->withHeaders([
            'Authorization' => 'Bearer ' . $token
        ])->postJson('/api/logout');

        $response->assertStatus(200)
                 ->assertJson([
                     'success' => true,
                     'message' => 'Sesión cerrada correctamente'
                 ]);

        // Verificar que el token fue eliminado
        $this->assertDatabaseMissing('personal_access_tokens', [
            'tokenable_id' => $user->id,
            'tokenable_type' => User::class
        ]);
    }

    public function test_cannot_logout_without_token()
    {
        $response = $this->postJson('/api/logout');

        $response->assertStatus(401);
    }

    public function test_cannot_reuse_revoked_token()
    {
        $user = User::factory()->create();
        $token = $user->createToken('test_token')->plainTextToken;

        // Primer logout (exitoso)
        $this->withHeaders(['Authorization' => 'Bearer ' . $token])
             ->postJson('/api/logout')
             ->assertStatus(200);

        // Intentar usar el token nuevamente (fallido)
        $this->withHeaders(['Authorization' => 'Bearer ' . $token])
             ->postJson('/api/logout')
             ->assertStatus(401);
    }
}

Build docs developers (and LLMs) love