Closed mbougarne closed 4 years ago
Hi @mbougarne ! I've been trying to solve the same thing. If you visit the thread here: #11 I have all of my steps laid out as well. I think it's something with how Axios sends the cookie.
Hello @danpastori , It's not an Axios issue, I tried with vanilla JS using XHR, besides I disabled the CSRF on all routes and I got 401. I start to believe that Airlock assumes that the incoming requests are within Laravel application not another provider which is Vue/Nuxt in our case. So the SPA is just the front-end part that lives within Resources
folder of Laravel App, and if we want to work with Nuxt, Vue-CLI or any front-end as a seperate app we should use API Tokens
Not stateful
I'll leave this to @taylorotwell to investigate.
No. Airlock does not require you to put everything in the same app. I've tested it fine with Vue CLI. These are all CORS issues.
I don't think so, I used laravel-cors
@mbougarne agree, that same situation here.
I got to this point yesterday and part of it was CORS. However if you are to the point where you are getting a valid 401 response, try changing the SESSION_DRIVER to cookie
(mentioned in #11). A combination of correct CORS, SESSION_DOMAIN, and SESSION_DRIVER got this resolved.
I managed to get this working with the NuxtJS auth module as well and pushed the code to help out.
Here's my NuxtJS frontend: https://github.com/serversideup/airlock-nuxt-frontend Here's my Laravel Airlock Backend: https://github.com/serversideup/airlock-laravel-api
I wrote a quick guide to getting them working together focused mainly on NuxtJS frontend with their first class auth module: https://serversideup.net/using-laravel-airlock-with-nuxtjs/.
If you guys need any help, let me know!
I found that I needed the following middleware to get any form of working CSRF with the current instructions as written.
Reading the token from the cookie header like the middleware above does will not protect against CSRF since that cookie is sent along with the request regardless of where it came from, defeating the purpose of CSRF protection entirely.
In order for this to work properly the SPA would need to send back the value of the XSRF-TOKEN
cookie under the request header X-XSRF-TOKEN
, which currently does not seem to be documented here, but this is how Laravel resolves encrypted CSRF cookies.
@SeinopSys your solution worked for me, thanks!
@billisonline I sincerely hope that you meant appending the header via JS, otherwise, by adding that middleware in your codebase, you are effectively making CSRF protection pointless. I've edited my previous comment to make this clearer nonetheless.
@SeinopSys Yes, this is what I actually did with fetch. The Laravel doc is clear about how to send back the CSRF token but yes, I think that would be a good idea to add some kind of reference in the readme.
// Check for CSRF token
let csrf = RegExp('XSRF-TOKEN[^;]+').exec(document.cookie)
csrf = decodeURIComponent(csrf ? csrf.toString().replace(/^[^=]+./, '') : '')
if (csrf) {
headers.append('X-XSRF-TOKEN', csrf)
}
@danpastori solution's worked for me SESSION_DRIVER=cookie (maybe is obvious for some people, but I think it could be in the airlock's documentation), Also people need to clear cookies before every test.
I find another reason. If you use Api Token rather than SPA. Your app/Http/Kernel.php
file should look like this
'api' => [
// useless for api token
// EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
AIRLOCK_STATEFUL_DOMAINS=127.0.0.1 in the .env
file works for me.
@taylorotwell Can you support me
POST: /api/curent-user: message: "Unauthenticated."
My config:
SESSION_DRIVER=cookie
SESSION_DOMAIN=.codeky.local
AIRLOCK_STATEFUL_DOMAINS=admin.codeky.local
'supports_credentials' => true
'paths' => ['api/*', 'login'],
I had this problem with getting an "Unauthenticated" error (401) for subsequent requests after a successful login. In my case it was because I made some API requests in nuxtServerInit
or in the created
hook. Because of how Nuxt works, those requests are made from the server and not from the client. And I guess the appropriate headers is not included then by default. I found two different solutions.
Make sure the request is made only from the client by using:
if (process.browser) {
// Make request
}
This can be used in the created
hook, but won't work in nuxtServerInit
.
The other solution is to set proxyHeaders: true
in the axios options. According to the docs:
In SSR context, this options sets client requests headers as default headers for the axios requests. This is useful for making requests which need cookie based auth on server side. This also helps making consistent requests in both SSR and Client Side code.
I hope this helps someone. I was banging my head for a while, before I figured out what was going on.
@taylorotwell @driesvints I think you would spare yourself a lot of support requests if you added a note about this trap in the docs. Many users seem to be stuck because of this, and think this is a problem with Sanctum, which it's not. Nuxt and Laravel seems to be a popular combo, and it would be a shame if they gave up on using Sanctum because of this.
I'm not sure if this is relevant to your particular case, but I encountered a similar issue. I was receiving 419 while accessing /login straight after successful token request (sanctum/csrf-cookie). In my case the problem was that Auth::routes() were in web.php, rather than api.php. Moving the Auth::routes() to api.php fixed the issue to me.
@FallenNoir You're required to send back the cookie XSRF-TOKEN
as a HTTP header X-XSRF-TOKEN
in the next request.
For who has tried this all, followed the instructions etc, see if you are explicitly adding Accept and Content-type headers to every request. For example in custom Middleware. If so, change the priority of the middleware or don't add those headers for this request.
After trying all of the possible solutions, there is what I come up with, and a bit long checklist for future devs experiencing 401 Unauthorized
and 419 Token mismatch
erros.
Firstly, we should set both apps on same domain. We can use localhost for both, or if we use valet then we can configure reverse proxy for our nuxt app. If you prefer to use localhost, then launch backend app with php artisan serve
and your backend will be available at localhost:8000
and frontend at localhost:3000
.
I will go with valet, so backend will be blog.test
and frontend at front.blog.test
Create blog.conf
file at ~/.config/valet/Nginx/
map $sent_http_content_type $expires {
"text/html" epoch;
"text/html; charset=utf-8" epoch;
default off;
}
server {
listen 127.0.0.1:80;
server_name front.blog.test; # setup your domain here
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
gzip_min_length 1000;
location / {
expires $expires;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
proxy_pass http://127.0.0.1:3000; # set the adress of the Node.js instance here
}
}
/config/auth.php
In my app, I moved from JWT to Sanctum. So need to recheck auth
file. If you created fresh app then skip this.
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
/config/sanctum.php
'stateful' => [
'front.blog.test'
],
/config/cors.php
'paths' => [
'sanctum/csrf-cookie',
'login',
'api/*'
],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
/config/session.php
'driver' => 'cookie',
'domain' => '.blog.test',
'same_site' => 'lax',
/app/Http/Kernel.php
Make sure you have cors
protected $middleware = [
...
\Fruitcake\Cors\HandleCors::class,
];
/routes/api.php
Make sure you use auth:sanctum
middleware
<?php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
nuxt.config.js
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth'
],
axios: {
baseURL: "http://blog.test",
credentials: true // Attention, credentials not withCredentials
},
auth: {
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
},
strategies: {
local: {
endpoints: {
login: { url: '/login', method: 'post', propertyName: false },
user: { url: '/api/user', method: 'get', propertyName: false }
},
tokenRequired: false,
tokenType: false
}
},
localStorage: false
},
Using Laravel Sanctum/Airlock with NuxtJS Authentication in Nuxt.js using Laravel Sanctum
I also have 419 issue.My react app lives inside rerources.How do you confiigure the sanctum stateful ? my app is laravel-app.test. my backend api is in laravel-app.test/admin/v1/ and the react is in laravel-app.test/admin . I tried what the docs says in sanctum but no luck.
I find another reason. If you use Api Token rather than SPA. Your
app/Http/Kernel.php
file should look like this'api' => [ // useless for api token // EnsureFrontendRequestsAreStateful::class, 'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
its work for me, thanks !
In .env
file
...
CLIENT_URL=client.test.localhost
SESSION_DOMAIN=.test.localhost
...
config/sanctum.php
'stateful' => [
env('CLIENT_URL')
],
config/cors.php
'paths' => [
'api/*',
'sanctum/csrf-cookie',
'login',
'logout'
],
'allowed_methods' => ['*'],
'allowed_origins' => [env('CLIENT_URL')],
...
'allowed_headers' => ['*'],
...
'supports_credentials' => true
app/Http/Kernel.php
protected $middleware = [
...
\Fruitcake\Cors\HandleCors::class,
...
];
protected $middlewareGroups = [
...
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
routes/web.php
...
Route::post('login', 'Auth\LoginController@login')->name('login');
...
routes/api.php
...
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
...
.env
API_URL=api.test.localhost
nuxt.config.js
...
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth-next'
],
...
auth: {
strategies: {
cookie: {
cookie: {
name: 'X-XSRF-TOKEN'
}
},
laravelSanctum: {
provider: 'laravel/sanctum',
url: process.env.API_URL
}
}
The name for the cookie must be set to X-XSRF-TOKEN
and not XSRF-TOKEN
I find another reason. If you use Api Token rather than SPA. Your
app/Http/Kernel.php
file should look like this'api' => [ // useless for api token // EnsureFrontendRequestsAreStateful::class, 'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ],
you saved my day. I tried 3 hours to make it work.
We run our applications in google app engine with 2 different services: default (laravel) and myorders (nuxt)
MyOrders services call the laravel API and we use sanctum.
You can try it here: https://dev.digitalmanager.guru/myorders/
When the user fills the email and click to send, we call the https://dev.digitalmanager.guru/sanctum/csrf-cookie URL and the cookie is corrected created:
After we send a post request to send the user email and got a token mismatch error:
Even with the X-XSRF-TOKEN header correctly sent:
@taylorotwell, can you help us find the solution?
@taylorotwell,
Digging deeper I found the reason for the token mismatch error.
When using redis sessions, the getTokenFromRequest from Illuminate\Foundation\Http\Middleware\VerifyCsrfToken returns something like this "30995|DDjAe5opsd7urpwrpwJLEvlFePuLmvEdXHNlr00e";" while the $request->session()->token() returns something like this "DDjAe5opsd7urpwrpwJLEvlFePuLmvEdXHNlr00e".
So, the command "hash_equals($request->session()->token(), $token)" returns false.
I added the following block of code to App\Http\Middleware\VerifyCsrfToken and it worked
If anyone is facing CSRF token mismatch issue, then please try checking if SANCTUM_STATEFUL_DOMAINS=
is set in .env
When SANCTUM_STATEFUL_DOMAINS
is not present in ENV then it throws CSRF token mismatch error.
Hope it helps someone. :)
@alignwebs wow this actually worked. thanks
For my case, the cause of the CSRF token mismatch was that I have multiple host names (other than localhost
) pointing to 127.0.0.1 set on my local machine, and reactscripts
started the development server to the other hostname.
Changing it back to localhost solved the issue, they should be on the same domain :)
it may seem obvious, but as developers we learn from mistakes to improve.
like @ToNyRANDRIAMANANTSOA mentioned, i had a smiliar issue where the frontend client was pointing to http://127.0.0.1:8000
rather than http://localhost:8000
where my SESSION_DOMAIN=localhost
and SANCTUM_STATEFUL_DOMAINS=localhost:8086
(quasar).
thank you !
I encountered the same issue when using localhost
domain, but when I switched to a valet
domain like app.test
, everything worked perfectly.
I found that I needed the following middleware to get any form of working CSRF with the current instructions as written.
Reading the token from the cookie header like the middleware above does will not protect against CSRF since that cookie is sent along with the request regardless of where it came from, defeating the purpose of CSRF protection entirely.
In order for this to work properly the SPA would need to send back the value of the
XSRF-TOKEN
cookie under the request headerX-XSRF-TOKEN
, which currently does not seem to be documented here, but this is how Laravel resolves encrypted CSRF cookies.
where is this file?
If you are using JWT. I commented this line in the kernel and it worked!
I found that I needed the following middleware to get any form of working CSRF with the current instructions as written. Reading the token from the cookie header like the middleware above does will not protect against CSRF since that cookie is sent along with the request regardless of where it came from, defeating the purpose of CSRF protection entirely. In order for this to work properly the SPA would need to send back the value of the
XSRF-TOKEN
cookie under the request headerX-XSRF-TOKEN
, which currently does not seem to be documented here, but this is how Laravel resolves encrypted CSRF cookies.where is this file?
Is this process secure?
I can't get it to work with Nuxt in the front-end, firstly I got the 419 error number when I tried to access to
/login
which is a CSRF token issue, I disabled the CSRF token by adding wildcard access in VerifyCsrfToken Middleware:I passed the login part with that, but I faced another one which is 401 ~ Unauthenticated: Although I'm in the stateful mode
Laravel app is running on:
http://localhost:8000/
Nuxt app is running on:http://localhost:3000/
I think, there's an issue on
`EnsureFrontendRequestsAreStateful
My Request using Axios as Nuxt Module: