manchenkoff / nuxt-auth-sanctum

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

[Bug] No `Accept: application/json` header in request #106

Closed ahoiroman closed 5 months ago

ahoiroman commented 5 months ago

Describe the bug Module does not make use of accept application/json headers, which make Laravel 11's wantsJson()-method to return false. In this case the user is being redirected to /login instead of returning a json, if a protected route is called.

    public function wantsJson()
    {
        $acceptable = $this->getAcceptableContentTypes();

        return isset($acceptable[0]) && Str::contains(strtolower($acceptable[0]), ['/json', '+json']);
    }

This is the header I send using this module:


{
"accept": 
"*/*",
"x-forwarded-port": 
"58653",
"accept-encoding": 
"gzip, deflate",
"user-agent": 
"node",
"sec-fetch-mode": 
"cors",
"accept-language": 
"*",
"cookie": 
"i18n_redirected=en",
"origin": 
"http://web.example.test",
"referer": 
"http://web.example.test",
"x-nginx-proxy": 
"true",
"x-client-verify": 
"SUCCESS",
"x-forwarded-proto": 
"http",
"x-forwarded-for": 
"127.0.0.1",
"x-real-ip": 
"127.0.0.1",
"connection": 
"keep-alive",
"host": 
"api.example.test"
}

To Reproduce Steps to reproduce the behavior:

  1. Install module with Laravel 11 backend
  2. Fetch a protected route
  3. See error

Module information

    sanctum: {
        baseUrl: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/api/v1/`,
        origin: process.env.NUXT_PUBLIC_SITE_URL,
        endpoints: {
            csrf: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/csrf`,
            login: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/auth/login`,
            logout: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/auth/logout`,
            user: '/auth/me'
        },
        redirect: {
            keepRequestedRoute: true,
            onLogin: '/dashboard',
            onLogout: '/',
            onAuthOnly: '/auth/login',
            onGuestOnly: '/dashboard'
        }
    },
manchenkoff commented 5 months ago

Hey @ahoiroman! The module should send all requests with the following headers:

Could you please confirm that it happens on both SSR/CSR requests? A screenshot from the browser dev tools would be enough.

Also, could you please send more details or a code sample sending the request from your Nuxt application?

manchenkoff commented 5 months ago

I have a sample project based on Laravel 11 (https://github.com/manchenkoff/breeze-api) and a separate Nuxt template (https://github.com/manchenkoff/breeze-nuxt) with the module installed, but couldn't reproduce this behavior so far 🤔

ahoiroman commented 5 months ago

That's strange.

This is my nuxt.config.ts:

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
    extends: [process.env.NUXT_UI_PRO_PATH || '@nuxt/ui-pro'],
    modules: [
        '@nuxt/content',
        '@nuxt/eslint',
        '@nuxt/image',
        '@nuxt/ui',
        '@nuxt/fonts',
        '@nuxthq/studio',
        '@vueuse/nuxt',
        'nuxt-og-image',
        '@nuxtjs/i18n',
        'nuxt-auth-sanctum'
    ],
    hooks: {
        // Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need)
        'components:extend': (components) => {
            const globals = components.filter(c => ['UButton'].includes(c.pascalName))

            globals.forEach(c => c.global = true)
        }
    },
    ui: {
        icons: ['heroicons', 'simple-icons']
    },
    colorMode: {
        disableTransition: true
    },
    sanctum: {
        baseUrl: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/api/v1/`,
        origin: process.env.NUXT_PUBLIC_SITE_URL,
        endpoints: {
            csrf: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/csrf`,
            login: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/auth/login`,
            logout: `${process.env.NUXT_PUBLIC_SITE_URL}/backend/auth/logout`,
            user: '/auth/me'
        },
        redirect: {
            keepRequestedRoute: true,
            onLogin: '/dashboard',
            onLogout: '/',
            onAuthOnly: '/auth/login',
            onGuestOnly: '/dashboard'
        }
    },
    routeRules: {
        '/backend/api/v1/**': {
            proxy: `${process.env.NUXT_BACKEND_URL}/api/${process.env.NUXT_PUBLIC_API_VERSION}/**`
        },
        '/backend/web/**': {
            proxy: `${process.env.NUXT_BACKEND_URL}/**`
        },
        '/backend/auth/**': {
            proxy: `${process.env.NUXT_BACKEND_URL}/auth/**`
        },
        '/backend/csrf': {
            proxy: `${process.env.NUXT_BACKEND_URL}/sanctum/csrf-cookie`
        },
        '/api/search.json': {prerender: true},
        '/docs': {redirect: '/docs/getting-started', prerender: false}
    },
    i18n: {
        lazy: true,
        defaultLocale: 'en',
        strategy: 'prefix_and_default',
        langDir: 'locales',
        locales: [
            {
                code: 'en',
                iso: 'en-GB',
                name: 'English',
                file: 'en-GB.ts'
            },
            {
                code: 'de',
                iso: 'de-DE',
                name: 'German',
                file: 'de-DE.ts'
            }
        ],
        vueI18n: './i18n.config.ts'
    },
    devtools: {
        enabled: true
    },
    typescript: {
        strict: false
    },
    eslint: {
        config: {
            stylistic: {
                commaDangle: 'never',
                braceStyle: '1tbs'
            }
        }
    }
})
ahoiroman commented 5 months ago

Hm, while debugging this: This only seems to happen using the routeRules 🤔

manchenkoff commented 5 months ago

@ahoiroman thanks for more details. I am not sure about the behavior of the additional routeRules, maybe they somehow interfere with the headers prepared by the ofetch client. Since Laravel has more incoming headers than should be built by the module, we should make sure that the actual request from Nuxt has the expected values. Please, try to make a request from the CSR side and make a screenshot of Request Headers section. I would also suggest upgrading to v0.4.0, as far as I see, with your configuration it will not break anything.

manchenkoff commented 5 months ago

@ahoiroman Oh, I see, then I would recommend using custom interceptors for ofetch client, you can check this section - https://manchenkoff.gitbook.io/nuxt-auth-sanctum/usage/interceptors

ahoiroman commented 5 months ago

Alright, thanks so far. As I am not familiar with those, I'd need to read docs first. But so far: Thanks a lot for your help and for your work.

manchenkoff commented 5 months ago

You can also check this conversation - #94, maybe it is related. But so far, I don't think that proxying requests can be modified on the module side. Let me know if more help is needed or feel free to close the issue once not valid anymore!

ahoiroman commented 5 months ago

Seems to be a breaking change in https://github.com/unjs/h3/pull/646 (https://github.com/unjs/h3/issues/709) that lead to this behavior. I am sorry to bother you, as this module indeed did not include a bug.

I close this issue. If someone else faces this issue, you - for now - can just add the header like this:

'/backend/api/**': {
            proxy: {
                to: `http://foo.bar/api/**`,
                headers: {accept: 'application/json'},
            }
        },