manchenkoff / nuxt-auth-sanctum

Nuxt module for Laravel Sanctum authentication
https://manchenkoff.gitbook.io/nuxt-auth-sanctum/
MIT License
116 stars 16 forks source link

[Question] 401 error does not cause user clearing and redirect #116

Closed romanslonov closed 2 days ago

romanslonov commented 4 days ago

Describe the bug

Hello, thank you for the amazing module. I got a problem dealing with 401 error, I am using module in 'token' mode. Login/logout/getting profile works like a charm. But in dashboard/settings I have a list of active sessions, I am getting this list:

const client = useSanctumClient();

const { data, status, refresh } = await useAsyncData('sessions', () =>
  client<{
    data: UserSession[];
  }>('/user/sessions'),
);

And then when I hit a button to remove current session:


async function closeSession(sessionId?: number) {
  if (!sessionId) return;

  await client('/user/sessions/delete', { method: 'DELETE', body: { id: sessionId } });

  await refresh(); // Important part! It refreshes the sessions list
}

I got 401 error in browser devtools console, in network tab for that /user/sessions endpoint, but I am still on this page (dashboard/settings). I will be redirected to the login page only if I hit cmd + R.

CleanShot 2024-06-25 at 01 44 51@2x

Did I missed something?

To Reproduce

Steps to reproduce the behavior:

  1. Login
  2. Refetch data on client and for some reason you got 401 (token expired or session was removed on server by user in my case)
  3. You got error 401 but module won't trigger user clearing and redirect to onLogin page (as you implemented this behaviour in #8)

Expected behavior

A clear and concise description of what you expected to happen.

Screenshots

If applicable, add screenshots to help explain your problem.

Module information

export default defineNuxtConfig({
    modules: ['nuxt-auth-sanctum'],

    sanctum: {
      baseUrl: 'https://api.app.com/api', // Laravel API
      mode: 'token',
      redirect: {
        onLogin: '/dashboard',
        onAuthOnly: '/auth/login',
        onLogout: '/',
        onGuestOnly: '/',
      },
      endpoints: {
        login: '/auth/login',
        logout: '/user/logout',
        user: '/user/profile',
      },
    },
});

Nuxt environment:

Laravel environment:

return [
    'stateful' => explode(
        ',',
        env(
            'SANCTUM_STATEFUL_DOMAINS',
            sprintf('%s','localhost, localhost:3000')
        )
    ),
];

or

SANCTUM_STATEFUL_DOMAINS=localhost,localhost:3000
return [
    'paths' => ['*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => [
        env('FRONTEND_URL', 'http://localhost:3000'),
    ],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];

Additional context

Add any other context about the problem here. For instance, you can attach the details about the request/response of the application or logs from the backend to make this problem easier to understand.

manchenkoff commented 3 days ago

Hey @romanslonov, thanks for opening this with such a detailed description!

To be honest, it looks like this is a business logic case that should be handled on your own. Due to some inconsistencies and recursive calls, I removed the automatic redirect that you mentioned in the scope of this PR - #71.

Currently, if you want to destroy your session you should use logout functionality and revoke the token on the backend side instead of deleting the session and expecting the redirect.

Identity will be reset automatically after getting 401 response only from user endpoint (during page refresh) but only middleware for your pages is responsible for preventing access with redirects (e.g. sanctum:auth), so the current page will stay accessible until the next navigation.

Feel free to provide your feedback on that!

romanslonov commented 3 days ago

@manchenkoff Thank you for the explanation. So what I need to implement in interceptors? As I understand, now profile refetch only on page refresh (not on navigation between protected routes). Users now can navigate across dashboard pages with expired or even removed auth.cookie. Should I do refresh the page here? Because doing logout() doesn't make sense as it might return 401 again.

ahoiroman commented 3 days ago

In the past, I solved these things using a kind of "heartbeat", which called the user-endpoint every n seconds. If 401 was returned, the user had not been redirected, but I presented a modal with the login form, which could not be escaped.

Maybe that's a possible solution?

romanslonov commented 3 days ago

Yes, maybe in some cases it's a good solution. But in current project I would "logout" user immediately after getting 401 on any request.

I think if this module had the method like logout but without calling the actual ap to the backend would be awesome. Something like clear() that just reset user, cookie and perform redirect.

manchenkoff commented 2 days ago

Hey @romanslonov, I double-checked my assumption I described above and realized that actually user will not be redirected even by middleware if the page is already loaded and there are no new API requests since the user identity is being cleaned only when a page has been refreshed.

So, I fixed the user reset part to react to any 401 response and also introduced a new config parameter for automatic redirects, that might be useful for you, please check the details in #118. Thanks for highlighting this behavior.

It will be merged and published today as 0.4.2.