Closed MarcoTroost closed 2 months ago
Hey @MarcoTroost, could you please provide sanctum config from your nuxt.config.ts
file?
Are you trying to get the token using the sanctum client or do you just want to get it manually in your code with plain $fetch
?
I also noticed that in session domain you have localhost:8000
instead of regular localhost
, did you specify that in the config on Laravel side (e.g. in .env
file)?
Hi Artem,
Thank you for the quick reply. I want users to be able to vote on certain topics. I therefore need to be able to post from the frontend to the Laravel backend.
I do not need the token for login purposes.
Here are my configs:
.env (frontend)
NUXT_BASE_URL="http://localhost:3000"
NUXT_PUBLIC_API_BASE="http://localhost:8000/disco"
NUXT_SITE_ENV=preview
Nuxt.config.ts:
export default defineNuxtConfig({
runtimeConfig: {
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE || 'https://api.trauminfo.de/disco',
},
},
modules: [
'nuxt-auth-sanctum'
],
sanctum: {
baseUrl: process.env.NUXT_PUBLIC_API_BASE || 'https://api.trauminfo.de/disco', // Laravel API
endpoints: {
csrf: {
url: '/sanctum/csrf-cookie',
},
user: {
url: '/user',
}
}
},
});
.env (backend)
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:SYlo/9LcvyqgoIEX7GxlJ2V3lYdFD/+V7Rnniy8k+eE=
APP_DEBUG=true
APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
SESSION_DOMAIN=localhost:8000
SANCTUM_STATEFUL_DOMAINS=localhost:3000
...
Cors.php:
<?php
return [
'paths' => ['api/*', 'disco/*', '/disco/sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['http://localhost:3000'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['Content-Type', 'X-Requested-With', 'Authorization', 'X-XSRF-TOKEN','X-CSRF-TOKEN'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
@MarcoTroost while you work locally, you should not put SESSION_DOMAIN=localhost:8000
in your env config since it is not a domain but the full address, it should be either localhost
or empty (better be empty to not interfere with some cases like 127.0.0.1), so Laravel will use localhost
by default for cookies. I also mentioned that in the module docs, you can check the use case for SPA Cookie.
Regarding requests against your API, there is a composable provided by the module plugin that you can use instead of plain $fetch
to call Laravel with any method including POST, please check the docs - useSanctumClient
@manchenkoff Changing SESSION_DOMAIN=localhost
(without the port) did the trick!
Perhaps it's an idea to include your explanation of explicitly not including the port in the official documentation? I think it could prove beneficial for the understanding of the module.
That composable looks just awesome. I will certainly try this!
kind regards, Marco
@manchenkoff Changing
SESSION_DOMAIN=localhost
(without the port) did the trick!
I'm glad it helped!
Perhaps it's an idea to include your explanation of explicitly not including the port in the official documentation?
I believe it was mentioned in the Laravel docs, but I will definitely add this note to my docs as well 👍
Page has been updated - SPA Cookie
Super! Thank you for your time & help.
If it is not a bother, i do have one question: Since voting in my website doesn't need a user to be logged in, do i really need a user? I only need the XSRF-TOKEN.
When vite starts up, the nuxt-auth-sanctum
module looks for a user, which it can't find (i have no routes for in place for users nor login)
Is it possible to opt out of this?
p.s. Making API calls works just fine
greetz, Marco
Is it possible to opt out of this?
Yes, since 0.4.12 version you can disable user request by setting sanctum.client.initialRequest
to false
in the config.
Great, works perfectly! Thanks a bunch!
Hi @manchenkoff
What worked on localhost, unfortunately doesn't work on my (apache) production server. The xsrf cookie is set, but the actual api call doesn't work.
Vue component:
const config = useRuntimeConfig()
const handleVote = async (id: number) => {
// const client = useSanctumClient()
// const response = await client(`${config.public.apiBase}/emotion`, {
// method: 'PUT',
// body: JSON.stringify({
// emotie_id: id,
// droom_id: props.dreamId,
// }),
// credentials: 'include',
// })
// choice.value = id
// if (response) {
// result.value = response.value
// }
await $fetch(`${config.public.apiBase}/sanctum/csrf-cookie`, {
credentials: 'include',
})
const xsrfToken = useCookie('XSRF-TOKEN').value
if (xsrfToken) {
const response = await $fetch(`${config.public.apiBase}/emotion`, {
method: 'POST',
headers: {
Referer: config.public.baseUrl,
Accept: 'application/json',
'X-XSRF-TOKEN': xsrfToken,
},
credentials: 'include',
body: JSON.stringify({
emotie_id: id,
droom_id: props.dreamId,
}),
})
choice.value = id
if (response) {
result.value = response
}
}
}
env (backend):
APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:SYlo/9LcvyqgoIEX7GxlJ2V3lYdFD/+V7Rnniy8k+eE=
APP_DEBUG=true
APP_URL=https://api.trauminfo.de
FRONTEND_URL=https://trauminfo.de
SESSION_DOMAIN=.trauminfo.de
SANCTUM_STATEFULL_DOMAINS=trauminfo.de
...
config/cors.php:
<?php
return [
'paths' => ['api/*', 'disco/*', 'disco/emotion', '/disco/sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://www.trauminfo.de', 'https://trauminfo.de'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['Content-Type', 'X-Requested-With', 'Authorization', 'X-XSRF-TOKEN','X-CSRF-TOKEN'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
Nuxt.config.ts:
export default defineNuxtConfig({
runtimeConfig: {
public: {
baseUrl: 'https://trauminfo.de',
apiBase: 'https://api.trauminfo.de/disco',
},
},
modules: [
'nuxt-auth-sanctum'
],
sanctum: {
baseUrl: 'https://api.trauminfo.de/disco', // Laravel API
endpoints: {
csrf: {
url: '/sanctum/csrf-cookie',
},
}
},
});
It's driving me crazy. Do you know what i'm doing wrong?
kind regards,
Marco
Hi @MarcoTroost, I highly recommend using client from the module (that handles all the things related to cookies and headers), try it with the following configurations:
nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
baseUrl: 'https://trauminfo.de',
apiBase: 'https://api.trauminfo.de/disco',
},
},
modules: ['nuxt-auth-sanctum'],
sanctum: {
baseUrl: 'https://api.trauminfo.de/disco',
endpoints: {
csrf: '/sanctum/csrf-cookie', // is not necessary since the default value is the same, but you had incorrect nestedness
},
},
})
.env
APP_NAME=Laravel
APP_ENV=production
APP_KEY=base64:SYlo/9LcvyqgoIEX7GxlJ2V3lYdFD/+V7Rnniy8k+eE=
APP_DEBUG=true
APP_URL=https://api.trauminfo.de
FRONTEND_URL=https://trauminfo.de
SESSION_DOMAIN=.trauminfo.de
SANCTUM_STATEFULL_DOMAINS=trauminfo.de
# ...
config/cors.php
<?php
return [
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => [env('FRONTEND_URL'), 'https://www.trauminfo.de'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
Vue Component
<script setup lang="ts">
const client = useSanctumClient()
const choice = ref(0)
const result = ref(0)
const props = defineProps<{
dreamId: number
}>()
const handleVote = async (id: number) => {
const response = await client('emotion', {
method: 'PUT',
body: JSON.stringify({
emotie_id: id,
droom_id: props.dreamId,
}),
})
choice.value = id
if (response) {
result.value = response.value
}
}
</script>
<template>
<button @click="handleVote(1)">
Click me!
</button>
</template>
<style scoped>
</style>
You can also find other example in the template applications:
Hi @manchenkoff
Thank you for your feedback, But unfortunately, the api call doesn't work. I really haven't got a clue.
All works fine on localhost, but not in production @ https://trauminfo.de/droomwoordenboek/krokodil
Whenever i click on "Bang" (= Scared in dutch):
I get a Cors error:
My website runs on an apache server.
Hi @MarcoTroost
This issue indeed is related to CORS settings on Laravel side:
Access to fetch at 'https://api.trauminfo.de/api/emotion' from origin 'https://trauminfo.de' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
.
Please make sure that your config is properly updated, cache was reset and fpm restarted. Also, check that correct middleware covers your endpoint.
If nothing helps, I would recommend debugging it on your production server to see what are the headers/details available in the Laravel request/response.
Good afternoon @manchenkoff
I've removed cors.php on production altogether & placed all the necessary headers in apache. This now works in production:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
# Set environment variable for allowed origins
SetEnvIf Origin "^https?://(www\.)?trauminfo\.de$" ORIGIN_ALLOWED=$0
Header always set Access-Control-Allow-Origin "%{ORIGIN_ALLOWED}e" env=ORIGIN_ALLOWED
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization, X-CSRF-TOKEN, X-XSRF-TOKEN"
Header always set Access-Control-Allow-Credentials true
I would still rather have it to work using Laravel's default Cors.php.
In order to test this thoroughly, i've changed the top level domain on my computer from localhost
to trauminfo.disco
, using Laravel Valet.
frontend: trauminfo.disco:3000 backend: api.trauminfo.disco
Using these settings, the module does not work.
Altered configs:
Nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
baseUrl: 'http://trauminfo.disco',
apiBase: 'http://api.trauminfo.disco/api',
},
},
modules: ['nuxt-auth-sanctum'],
sanctum: {
baseUrl: 'http://api.trauminfo.de/disco',
endpoints: {
csrf: '/sanctum/csrf-cookie',
},
},
})
.env
APP_NAME=Laravel
APP_ENV=production
APP_KEY=base64:SYlo/9LcvyqgoIEX7GxlJ2V3lYdFD/+V7Rnniy8k+eE=
APP_DEBUG=true
APP_URL=https://api.trauminfo.de
FRONTEND_URL=https://trauminfo.de
SESSION_DOMAIN=.trauminfo.disco
SANCTUM_STATEFULL_DOMAINS=trauminfo.disco
# ...
Cors.php
return [
'paths' => ['api/*', '/api/sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['http://trauminfo.disco:3000'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['Content-Type', 'X-Requested-With', 'Authorization', 'X-XSRF-TOKEN','X-CSRF-TOKEN'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
Cookie seems to be set, but i can't read it:
Do you know what's wrong when using different top level domains instead of localhost?
kind regards,
Marco
Hey @MarcoTroost, using a custom domain on a local machine might be tricky. I always recommend using localhost
for development and a real domain in production. I have never used Laravel Valet, only plain artisan serve
or docker-based sail
.
I can try to help to point out the possible issue, but I would need you to upgrade to the latest version of the module 0.4.18
, change sanctum.logLevel
to 5
in your nuxt.config.ts
and then to attach logs output here in the comments (from both SSR console and CSR browser console).
P.S.
I've removed cors.php on production altogether & placed all the necessary headers in apache.
This is not how you are supposed to configure your server 😄 for example, if you need to migrate to nginx (which Valet is based on afaik), you will face the same problem again. You should double-check your config to set it up properly.
Just for reference, here are some examples that I use in production in one of my projects
config/cors.php
<?php
declare(strict_types=1);
return [
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => [
env('FRONTEND_URL', 'http://manchenkoff.me'), // http://localhost:3000 on local machine
env('BACKOFFICE_URL', 'http://admin.manchenkoff.me'), // http://localhost:3001 on local machine
],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
config/sanctum.php
<?php
declare(strict_types=1);
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
return [
'stateful' => explode(
',',
env('SANCTUM_STATEFUL_DOMAINS', 'manchenkoff.me,admin.manchenkoff.me,api.manchenkoff.me') // localhost:3000,localhost:3001 on local machine
),
'guard' => ['web'],
'expiration' => null,
'middleware' => [
'verify_csrf_token' => VerifyCsrfToken::class,
'encrypt_cookies' => EncryptCookies::class,
],
];
.env
# ...
APP_URL=http://api.manchenkoff.me
FRONTEND_URL=http://manchenkoff.me
BACKOFFICE_URL=http://admin.manchenkoff.me
SESSION_DOMAIN=.manchenkoff.me
# ...
and this is what I use in the Nuxt instance
nuxt.config.ts
export default defineNuxtConfig({
modules: [
'nuxt-auth-sanctum',
],
sanctum: {
baseUrl: 'https://api.manchenkoff.me',
},
});
Good morning @manchenkoff
Thanks for your elaborate reply. You're absolutely right, a server shouldn't be configured this way.
Point is that i got so frustrated, that i chose to alter the apache config just to get going.
I DO want to use middleware. That's why i've set up my local top level domain to trauminfo.disco
& api.trauminfo.disco
using valet to find where the problem lies.
On localhost; something, somewhere manipulates the Access-Control-Allow-Origin header
, adding another *
resulting in multiple origins:
I haven't got a clue what proces is responsible for this.
I will investigate further this weekend, using tha latest version & increasing the log-level.
regards, Marco
adding another
*
resulting in multiple origins
Yes, it can be either multiple domains or *
to allow everything
I will investigate further this weekend, using tha latest version & increasing the log-level.
Sure thing, I will try to help once more details are shared!
Hi!,
First of all, thank you for your great work on this module!
My Nuxt 3 frontend runs on localhost:3000. My Laravel 11 backend runs on localhost:8000 (by means of php artisan serve).
I cannot use the XSRF-TOKEN cookie in my frontend.
"xsrfToken" is undefined, since the response i get is empty.
The set-cookie command seems to be executed though:
How can i get the XSRF-TOKEN cookie in localhost:3000 ?
kind regards,
Marco Troost