Closed maxacarvalho closed 7 months ago
Hey @maxacarvalho, thanks for a detailed description!
Since I noticed the custom domain in the request, I would like to ask you to provide a configuration of the stateful domains from the Laravel app and also make sure that you have SESSION_DOMAIN
set to .nicksaude.test
.
By default your Nuxt tries to send a request with app2.nicksaude.test
as origin
, so it has to be registered in your backend as a stateful domain as well.
And also, could you please provide more details about your dev environment, for instance, do you use simple Docker or something else for Laravel? Do you run Nuxt app in a docker or just in your console?
Hi @manchenkoff
My Laravel application runs from a Docker and it's fully accessible from my local computer (host).
The SESSION_DOMAIN
is set to .nicksaude.test
.
The Nuxt app is running from outside the Docker env. For instance, it was running from another container but I started to run it outside in order to check if that wasn't the issue.
I know the Laravel Sanctum is working because I have other apps using it as a backend. The other apps are running Vue with Vite, PWA only. I can also confirm that the Nuxt app works if I set the ssr
to false
, I can login and get the logged user back as expected.
It only fails when ssr
is turned on.
@maxacarvalho
Okay, in my case, I use Docker for Laravel and Nuxt with SSR in console mode. Could you check if this combination works with localhost
and empty SESSION_DOMAIN
instead?
Hi @manchenkoff, I'm not sure I got it. What do you mean by Nuxt with SSR in console mode
.
In order to access my Larave application using localhost
I'll have to change a lot of things since it's sitting behind an Nginx. I'd have to change the way the container runs and execute php artisan instead
(unless you know any other trick).
I also don't use, for example, Laravel Sail. I have a container based on Alpine with the necessary dependencies installed.
To give you a picture of it:
My docker-compose.yml
will produce something like
What do you mean by
Nuxt with SSR in console mode
.
I meant just running it via the default nuxt dev
command, not as a docker container or via process managers. I have SSR enabled, so it works with Laravel app (that is in the sail-based docker container) for both client and server modes since they use same origin, which is localhost
in my case.
In order to access my Larave application using
localhost
I'll have to change a lot of things since it's sitting behind an Nginx. I'd have to change the way the container runs and executephp artisan instead
(unless you know any other trick).
Yeah, php artisan serve
would also work, I guess in this case, you need to adjust just env variables to connect to other containers like mysql/redis
@manchenkoff
I'm running the Nuxt app with npm run dev
from my local machine, outside docker.
you're correct, I suppose I can simply run php artisan serve
from the container and check if it works. I'll do that and come back to you.
@maxacarvalho I was checking around what might be a reason and found this Nuxt issue with a similar problem, so just in case, could you also provide an example of code you are working with?
Hi @manchenkoff
I managed to play with the php artisan serve
option and it works as expected.
But, when a switch back to the domain it gives me the same error.
Just to be very clear about it:
php artisan serve
.npm run dev
. So not from DockerI put together a very simple Nuxt app, I used your playground example basically.
nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['nuxt-auth-sanctum'],
sanctum: {
baseUrl: "https://manager.nicksaude.test",
endpoints: {
csrf: '/sanctum/csrf-cookie',
login: "/api/nick-patients/login",
logout: "/front/auth/logout",
user: "/api/nick-patients/patient",
},
redirect: {
keepRequestedRoute: true,
onLogin: '/protected',
onLogout: '/',
onAuthOnly: '/login',
onGuestOnly: '/',
},
},
devServer: {
host: 'app2.nicksaude.test',
https: {
key: "./key.pem",
cert: "./cert.pem",
},
},
})
app.vue
<template>
<div>
<NuxtPage />
</div>
</template>
pages/index.vue
<script lang="ts" setup>
</script>
<template>
<div>
<p>
Welcome to demo application for Nuxt & Laravel Sanctum integration! Feel
free to use navigation menu to check pages' behavior
</p>
<NuxtLink to="/protected">
About page
</NuxtLink>
</div>
</template>
pages/login.vue
<script setup lang="ts">
definePageMeta({
middleware: ['sanctum:guest'],
});
const { login } = useSanctumAuth();
const route = useRoute();
const credentials = reactive({
username: '',
password: '',
remember: false,
});
const loginError = ref('');
async function onFormSubmit() {
try {
await login(credentials);
} catch (error) {
loginError.value = error as string;
}
}
</script>
<template>
<div v-if="route.query.redirect">
Hmmm, looks like you tried to open
<em>"{{ route.query.redirect }}"</em> page, login first to access it and
we can redirect you there
</div>
<h2>Login form</h2>
<p v-if="loginError" class="error-message">Error - {{ loginError }}</p>
<form class="login-form" @submit.prevent="onFormSubmit">
<div class="input-group">
<label for="email">username</label>
<input
id="username"
v-model="credentials.username"
type="text"
name="username"
/>
</div>
<div class="input-group">
<label for="password">Password</label>
<input
id="password"
v-model="credentials.password"
type="password"
autocomplete="current-password"
name="password"
/>
</div>
<div class="input-group">
<label for="remember">Remember me</label>
<input
id="remember"
v-model="credentials.remember"
type="checkbox"
name="remember"
/>
</div>
<button type="submit">Log in</button>
</form>
</template>
protected.vue
<script lang="ts" setup>
definePageMeta({
middleware: ['sanctum:auth'],
});
interface MyUser {
id: number;
name: string;
email: string;
email_verified_at: string;
created_at: string;
updated_at: string;
}
const { isAuthenticated, user, refreshIdentity } = useSanctumAuth();
</script>
<template>
<div>
<h1>Protected</h1>
<p>Your authentication status - {{ isAuthenticated }}</p>
<code>Identity object - {{ user }}</code>
<div><button @click="refreshIdentity">Refetch user</button></div>
</div>
</template>
If I ignore the error and go to the login page I can login, I can see the user data. But if I refresh the page it redirects me back to the login page.
So, the flow is:
And here's the error again
ERROR Unable to load user identity [GET] "https://manager.nicksaude.test/api/nick-patients/patient": <no response> fetch failed
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async $fetch2 (node_modules/ofetch/dist/shared/ofetch.37386b05.mjs:268:15)
at async node_modules/nuxt-auth-sanctum/dist/runtime/plugin.mjs:23:120
at async setup (virtual:nuxt:/Users/maaxcarvalho/Code/nuxt-example-app/.nuxt/plugins/server.mjs:38:116)
at async Object.callAsync (node_modules/unctx/dist/index.mjs:72:16)
at async applyPlugin (node_modules/nuxt/dist/app/nuxt.js:116:25)
at async executePlugin (node_modules/nuxt/dist/app/nuxt.js:153:9)
at async Module.applyPlugins (node_modules/nuxt/dist/app/nuxt.js:161:5)
at async createNuxtAppServer (node_modules/nuxt/dist/app/entry.js:26:7)
at async default (node_modules/@nuxt/vite-builder/dist/runtime/vite-node.mjs:33:18)
Hey @maxacarvalho, thanks for the details. It is definitely related to the request headers/cookies on the server side, I assume that the origin
header is different or the cookie domain is incorrect when Nuxt sends an SSR request. I will try reproducing it on my local machine with your configuration and will get back to you!
Hi @maxacarvalho, I've tried a lot of different combinations but couldn't reproduce exactly the same behavior as yours. Let's make sure that we have proper configurations 😄
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['nuxt-auth-sanctum'],
sanctum: {
baseUrl: "http://api.domain.test",
origin: "http://domain.test", // for 2nd app I use 'http://subapp.domain.test'
},
devServer: {
host: 'domain.test', // for 2nd app I use 'subapp.domain.test'
},
})
api.domain.test
, and here are some configs
// .env
APP_URL=http://api.domain.test
FRONTEND_URL=http://domain.test
BACKOFFICE_URL=http://subapp.domain.test
SESSION_DOMAIN=.domain.test
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s%s%s%s',
'localhost,localhost:3000,localhost:3001,127.0.0.1,127.0.0.1:8000,::1',
env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : '',
env('FRONTEND_URL') ? ',' . parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : '',
',' . parse_url('http://www.domain.test', PHP_URL_HOST),
env('BACKOFFICE_URL') ? ',' . parse_url(env('BACKOFFICE_URL'), PHP_URL_HOST) : '',
))),
// config/cors.php
'allowed_origins' => [
env('FRONTEND_URL', 'http://localhost:3000'),
env('BACKOFFICE_URL', 'http://localhost:3001'),
'http://domain.test',
'http://www.domain.test',
],
Can you check that it is the same with yours Laravel configuration and if it is not, then could you please share it?
Hey @manchenkoff For a moment I thought today would be an amazing Friday!
Here's what I did
http
instead of https
, backend and frontendorigin
on the Nuxt app isn't working. It still uses the app hostname, localhost:3000
for both Origin
and Referer
. I had to update the nuxt.config.ts
file devServer.host
for it to work.Second phase, https
.
Now I turned everything back to https
, including the Nuxt app, with a self-signed certificate created with mkcert
.
With https
I see the same error again, so no lucky. But, on the bright side, I could narrow down the issue to https
.
After realising that, I added NODE_TLS_REJECT_UNAUTHORIZED=0
to my .env
.
Once I did that I stopped seeing the error ERROR Unable to load user identity [GET] "https://manager.nicksaude.test/api/nick-patients/patient": <no response> fetch failed
in the console, but, it's still not behaving properly.
Here's the flow:
So, with the NODE_TLS_REJECT_UNAUTHORIZED=0
set I don't see the terminal error anymore. But it seems to me that the Nuxt backend call still fails.
It's clear to me that's related to HTTPS
, I just don't know how to fix it and make it work for local development.
The fact that the origin
setting didn't work also bugs me.
2. I noticed that the Sanctum setting
origin
on the Nuxt app isn't working. It still uses the app hostname,localhost:3000
for bothOrigin
andReferer
. I had to update thenuxt.config.ts
filedevServer.host
for it to work.
Here is the behavior of the module, it checks config first but if it is undefined or null, it falls back to useRequestUrl().origin
which might have your localhost:3000
. Anyway, looks weird to me that setting does not work for some reason 🤔
5. If I refresh the page I see the login page for a sec, then I see a call to the backend from the Chrome Devtools, then I'm redirected back to the dashboard.
When you refresh the page, Nuxt sends a request from the SSR side, so I assume we can find a difference between those 2 calls (CSR / SSR). Do you have any way to extract logs from your Laravel proxy nginx? For instance headers and cookies
Ok, let's go!
First of all: it's working!!!
Now, here are my findings.
If I don't set NODE_TLS_REJECT_UNAUTHORIZED=0
it won't work with SSL, that's a fact.
When I test from the console (not from Docker) running npm run dev
I must set the devServer.host
anyway, otherwise I cannot use https since my self-signed cert is bound to the domain I'm running with.
I did investigate the Nginx headers and, when the call comes from the client side both referer
and origin
headers are set properly. When the request runs from the server side it only sets the referer
. Which is expected.
So, with the NODE_TLS_REJECT_UNAUTHORIZED=0
in the .env
, devServer.host
set to my app's URL, which is accepted by the backend, and both sanctum.origin
and runtimeConfig.public.sanctum.origin
set on the nuxt.config.ts
the app works as expected 👍🏽
By chance I checked this issue and saw your comment about laravel.test
.
I got curious once I realised the "trick" in there. So I renamed my container to reflect my backend's URL, manager.nicksaude.dev
. Then I updated the Nuxt app to point to that "URL".
After that I reinstalled my dependencies running from inside the container that's dedicated to my Nuxt app.
Then I ran the app from inside the Docker container and, Voilà, it works!!!
One tiny suggestion from my end is for you to set the Origin
header as well. You see, Laravel Sanctum checks like this $domain = $request->headers->get('referer') ?: $request->headers->get('origin');
. If by any chance that order changes in the future the module might stop working.
This was an amazing debug journey and I thank you immensely for your dedication and kindness. Have an awesome weekend!
Holy moly! What a great investigation you did @maxacarvalho, very useful for later issues as well, thank you 😄 I'm glad it works for you now, I for sure will add origin
header as well.
For a moment I thought today would be an amazing Friday!
I think eventually it is! 😄 Have a nice weekend!
Hi,
This is a desperate attempt since I'm two days in a row trying to find what's wrong without success.
I have a running Laravel application, using Laravel v10. I'm starting a new frontend app with Nuxt 3 and I'm using your module because it's very cool.
Everything works as expected if I set
ssr: false
. If not, I get the following error:Module information
^0.1.2
sanctum
from yournuxt.config.ts
Nuxt environment:
^3.11.1
Laravel environment:
10.48.2
[no]
[yes]
Additional context
I tried many ways to try to figure out the issue, without success. For example, I tried to install a fresh Laravel application and point the app to it. It works as expected. So it must be something related to my Laravel application but I can't understand what, the error message doesn't make much sense to me. For example, I can see that the Nginx that sits in front of the Laravel app gets the request:
Also, when I turn off SSR and use the app as a PWA everything works as expected. So I'm utterly lost.
I searched the issues and found some that seemed related but, none of the possible fixes solved my problem. This is a desperate attempt to get this thing fixed :(
Thanks.