I’m having trouble with authentication where I have a Laravel backend and a Nuxt frontend. When I log in and log out successfully, everything works fine. The problem occurs when the login fails due to incorrect credentials, yet I’m still able to access the dashboard. Even though the login fails, I can see a new XSRF token being added in the browser’s Application tab. The issue is that I can access the dashboard and even the profile section, but when I try to log out, I get an error saying I’m unauthorized, which makes sense because the token wasn’t provided. However, for some reason, a session with the XSRF token is hanging around, causing me to be partially logged in even though I’ve entered incorrect login credentials. Does anyone know what could be causing this issue? I’m new to REST APIs, so I’m probably doing something very wrong.
This is my useAuth.js
export default function useAuth() {
const user = useState('auth-user', () => null)
const { errorBag, transformValidationErrors, resetErrorBag } = useCustomError()
const { api, csrf } = useAxios()
async function me() {
try {
const data = await api.get("/api/me")
user.value = data.data
} catch (e) {
user.value = null
console.log("error")
}
}
function login(userForm) {
resetErrorBag()
csrf().then(() => {
api.post("/login", userForm).then(({ data }) => {
user.value = data.user
$fetch('/api/set-cookie', {
method: "POST",
body: { token: data.token }
}).then(res => {
navigateTo("/dashboard")
})
}).catch(err => {
transformValidationErrors(err.response)
})
})
}
function logout() {
api.post("/api/logout").then(() => {
user.value = null
$fetch('/api/logout', {
method: "POST",
}).then(res => {
navigateTo("/")
})
})
}
function register(userForm) {
resetErrorBag()
csrf().then(() => {
api.post("/register", userForm).then(({ data }) => {
// verify email screen
}).catch(err => {
transformValidationErrors(err.response)
})
})
}
return { login, logout, register, errorBag, user, me }
}
this is my useAxios.js
import axios from "axios";
export default function useAxios(){
const rtConfig = useRuntimeConfig()
let api = axios.create({
baseURL: rtConfig.public.API_URL,
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
// "Authorization:": "Bearer " + localStorage.getItem("token"),
},
withCredentials: true,
withXSRFToken: true
})
async function csrf(){
return await api.get("/sanctum/csrf-cookie")
}
return {
api, csrf
}
}
guest middleware
export default defineNuxtRouteMiddleware((to, from) => {
const key = process.server ? 'token' : 'XSRF-TOKEN'
const token = useCookie(key)
if(token.value)
return navigateTo('/')
})
auth middlware
export default defineNuxtRouteMiddleware((to, from) => {
const key = process.server ? 'token' : 'XSRF-TOKEN'
const token = useCookie(key)
if(!token.value)
return navigateTo('/')
})
So even when I defined auth middleware for dashboard page lije this
definePageMeta({
middleware: ['auth']
})
I can still access it, but at all I’m not authorized. I don’t understand where is mistake :/
2
Answers
Laravel will almost always include
XSRF-TOKEN
cookie unless the request is from an exlucded path, when the route is define inweb
routes instead ofapi
routes.You can try this with this simple route:
web
routes are stateful andapi
routes are stateless.web
routes by default have this middleware handling CSRF:https://github.com/laravel/framework/blob/v11.23.1/src/Illuminate/Foundation/Configuration/Middleware.php#L446
That said,
XSRF-TOKEN
is not used for checking authentication.More on here: https://laravel.com/docs/11.x/csrf
What you can do instead is actually getting the user.
Your process then goes like:
To do the always fetch user, since I assume you’re most likely want to show the user data like in a User dropdown, and not just for authentication check, is to do it via Nuxt plugin.
Create
plugins/auth.server.ts
plugin:Now user data should always be available throughout your Nuxt app.
In your authentication check middlwares, you can now do:
and
It looks like there are a few key points causing the issue:
XSRF Token Handling: The XSRF token (used to protect against CSRF attacks) is being set regardless of whether the login succeeds or fails. This means that even on a failed login, the token is being created and stored, allowing partial access to protected routes.
Authorization Flow: Your
auth
middleware only checks for the presence of a token, not whether the user is actually authenticated. The XSRF token alone doesn’t represent an authenticated session.Improper Error Handling on Failed Logins: When login fails, your code still sets the XSRF token, which allows partial access.
Suggestions to Fix:
Fix Login Error Handling:
You need to ensure that the token is not set unless the login succeeds. This will prevent setting the XSRF token on failed login attempts. Modify the login function:
This ensures that if the login fails, the user and token are not set.
Check Authentication in Middleware:
The current
auth
middleware only checks for the presence of a token, which can lead to false positives. Modify it to check the user’s authentication status instead of just the token:This middleware will ensure that the user is fully authenticated before allowing access to protected routes.
Ensure Session-Based Authentication:
If you’re using session-based authentication, ensure that
withCredentials: true
is properly set on all requests and that cookies are being handled correctly by the backend.Logout Function:
The logout process is also critical. It should invalidate the session properly, removing both the user token and any session cookies.
Debugging:
Set-Cookie
headers are being sent on failed logins. They should only be set on successful login./api/me
route is correctly protected and doesn’t return authenticated user data on failed logins.