CORS Laravel VueJS - laravel

I'm trying to do a get with axios from VueJS to Laravel which is my API.
I got this error :
Access to XMLHttpRequest at 'http://api.test/api/events/1/' from origin >'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control->Allow-Origin' header is present on the requested resource.
Uncaught (in promise) Error: Network Error
at createError (createError.js?2d83:16)
at XMLHttpRequest.handleError (xhr.js?b50d:87)
I've tried to create a middleware named 'cors' like here but it's not working for me or maybe I'm doing it badly ?
Strange thing ? is that's working with Postman.
Thank for the help ! :)

Servers are used to host web pages, applications, images, fonts, and
much more. When you use a web browser, you are likely attempting to
access a distinct website (hosted on a server). Websites often request
these hosted resources from different locations (servers) on the
Internet. Security policies on servers mitigate the risks associated
with requesting assets hosted on different server. Let's take a look
at an example of a security policy: same-origin.
The same-origin policy is very restrictive. Under this policy, a
document (i.e., like a web page) hosted on server A can only interact
with other documents that are also on server A. In short, the
same-origin policy enforces that documents that interact with each
other have the same origin.
Check this CORS library made for Laravel usage.
Installation is easy:
$ composer require barryvdh/laravel-cors
$ php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"
The defaults are set in config/cors.php
return [
/*
|--------------------------------------------------------------------------
| Laravel CORS
|--------------------------------------------------------------------------
|
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
| to accept any value.
|
*/
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['Content-Type', 'X-Requested-With'],
'allowedMethods' => ['*'], // ex: ['GET', 'POST', 'PUT', 'DELETE']
'exposedHeaders' => [],
'maxAge' => 0,
];
allowedOrigins, allowedHeaders and allowedMethods can be set to array('*') to accept any value.
To allow CORS for all your routes, add the HandleCors middleware in the $middleware property of app/Http/Kernel.php class:
protected $middleware = [
// ...
\Barryvdh\Cors\HandleCors::class,
];
If you want to allow CORS on a specific middleware group or route, add the HandleCors middleware to your group:
protected $middlewareGroups = [
'web' => [
// ...
],
'api' => [
// ...
\Barryvdh\Cors\HandleCors::class,
],
];
https://www.codecademy.com/articles/what-is-cors

Tried sending Axios request from Vue app to Laravel backend.
I had CORS Error and I couldn't find the solution.
After following request execution in vendor files I found out that I was simply testing it wrong.
I was testing on url: http://localhost/api, but in config/cors.php there is:
'paths' => ['api/*', 'sanctum/csrf-cookie'],
So all I had to do was to change request to http://localhost/api/... and it started working.
Another solution is adding 'api' to paths array in config/cors.php if you want to use http://localhost/api

The problem you are facing is with the same origin policy. you can read about it on the Mozilla site (https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control).
it is basally to proven un authorized access to web servers. you can change the way your web server reacts and i also in that link i have included.

Related

Laravel Sanctum - sanctum/csrf-cookie (204 "No content")

Laravel sanctum has been a bit of a headache for me as i have spent hours trying to figure out why sanctum/csrf-cookie route returns no content. initially the same route return 404 not found but after adding 'prefix' => 'api/sanctum' config/sanctum.php it seems to work except that it outputs nothing and no cookie is set in my browser.
Here are some of my codes
.env
SANCTUM_STATEFUL_DOMAINS=localhost:8080
SPA_URL=http://localhost:8080
SESSION_DOMAIN=localhost
--config/cors.php
'paths' => [
'api/*',
'login',
'logout',
'register',
'user/password',
'forgot-password',
'reset-password',
'sanctum/csrf-cookie',
'user/profile-information',
'email/verification-notification',
],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
axios
export const authClient = axios.create({
baseURL: process.env.VUE_APP_API_URL,
withCredentials: true, // required to handle the CSRF token
});
and having done all of that, if i tried to generate a token using
axios.get('/sanctum/csrf-cookie').then(response => {
// Login...
});
i get 204 no content response
i have also added the api middleware in kernel.php as instructed in the doucmentation but still wont set the cookie. and when i try to make request to another route protected by sanctum i 419 token mismatch.
i have also run a fresh installation of laravel, php artisan optimize, cleared my brower history, checked the endpoints in postman but still thesame 204 and 419 exceptions
I was struggling with the same issue for days and then founded a way that worked.
so :
The '/sanctum/csrf-cookie' route return a 204 response when successfull, the then you have to send your post request with credentials. i used to get a 419 reponse status.
so after followed laravel docs here is what i added :
SPA
make sure you set the withCredentials header to true
API
.env
SESSION_DRIVER=cookie
SESSION_DOMAIN='.localhost'
SANCTUM_STATEFUL_DOMAINS='localhost,127.0.0.1'
.kernel.php
add in your middleware array : \Illuminate\Session\Middleware\StartSession::class
Hey I don't know whether you've found the answer or not but that request meant to have empty response body. Your CSRF token is available in the header of the response ;D
From HTTP Specification:
The HTTP 204 No Content success status response code indicates that a request has succeeded, but that the client doesn't need to navigate away from its current page. This might be used, for example, when implementing "save and continue editing" functionality for a wiki site.
According to specification 204 is the exptected response from server.
You can insert in bootstrap.js file
import axios from 'axios';
window._ = _;
window.axios = axios;
window.axios.defaults.baseURL = "/api/";
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

How to solve the CORS error in Laravel + Nuxt.js

I am installing the old project, which is made by Nuxt.js (frontend) and Laravel (backend) on my local.
Access to XMLHttpRequest at 'http://localhost:8000/api/user' from origin 'http://127.0.0.1:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
This is the issue that I faced to run the project.
Frontend configuration for the request(nuxt nuxt.config.js).
user: {
url: '/api/user',
method: 'get',
propertyName: false,
withCredentials: true,
headers: {
X-Requested-With': 'XMLHttpRequest',
Content-Type': 'application/json'
}
}
Backend configuration (Laravel config/cors.php)
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
I tried to find the solution by googling but I didn't find the correct solution.
withCredetials is true
CORS: credentials mode is 'include'
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'
So, I want someone to help me in this field.
Try this way in your laravel app:
php artisan serve --host YOUR_LOCAL_IP
YOUR_LOCAL_IP is your ip that can access with ifconfig command in linux and ipconfig in windows.
Finally you should request to YOUR_LOCAL_IP:PORT/api

How to retrieve laravel CSRF token using separated vue frontend

Is there a way to pass Laravel csrf token to Vue given that the Laravel backend and the Vue frontend are separated from each other (in diferent directory and in diferent subdomains) ??
I'm building an application and would like to have separated backend and frontend for organization purposes and for because it facilitates team work. So, it would be something like:
api.mydomainexample.com (Laravel backend)
mydomainexample.com (Vue frontend for public)
admin.mydomainexample.com (Vue Frontend for admin only)
Is this possible? What I thought was maybe running a nodejs server for the frontend project and make the nodejs server communicate with the laravel server. Don't know how to do that.
I found similiar stackoverflow questions, but the responses from them do not solve my problem. The best thing I found was this, which proposes to use Laravel passport. But, is the proposal the only one that works? If so, does Laravel passport protect users from CSRF attacks?
Actually, if there is an workaround which enables to have separated backend and frontend while protecting against CSRF tokens, then that would be perfect!
Use Sanctum
LARAVEL BACKEND
Install Sanctum via Composer
composer require laravel/sanctum
Publish the Sanctum configuration and migration files
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Run your migrations - Sanctum will add a table to store API tokens
php artisan migrate
Add Sanctum's middleware to your api middleware group in your App/Http/Kernel.php
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Configure which domains your SPA will be making requests from. From the docs:
You may configure these domains using the stateful configuration option in your sanctum configuration file. This configuration setting determines which domains will maintain "stateful" authentication using Laravel session cookies when making requests to your API.
So - Update your config\sanctum.php to include something like this:
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:8000,127.0.0.1,127.0.0.1:8000,::1')),
Configure your config/cors.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', '*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
Configure your config/session.php
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| Here you may change the domain of the cookie used to identify a session
| in your application. This will determine which domains the cookie is
| available to in your application. A sensible default has been set.
|
*/
'domain' => env('SESSION_DOMAIN', null),
In your .env, add the following:
// Change .your-site.local to whatever domain you are using
// Please note the leading '.'
SESSION_DOMAIN=.your-site.local
SANCTUM_STATEFUL_DOMAINS=your-site.local:8000
CORS_ALLOWED_ORIGINS=http://app.your-site.local:8000
Run a php artisan config:clear
VUE FRONTEND
In your front-end, create the following folder/file structure
#/src/services/api.js
api.js:
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.VUE_APP_API_URL,
withCredentials: true,
});
export default apiClient;
In the root directory, place an .env file with the following in it:
VUE_APP_API_URL= 'http://api.your-site.local'
To authenticate, your SPA should first make a request to the /sanctum/csrf-cookie. This sets the XSRF-TOKEN cookie. This token needs to be sent on subsequent requests ( axios will handle this for you automatically ). Immediately after, you'll want to send a POST request to your Laravel /login route.
On your Vue front-end login component:
import Vue from 'vue'
import apiClient from '../services/api';
export default {
data() {
return {
email: null,
password: null,
loading: false,
};
},
methods: {
async login() {
this.loading = true; // can use this to triggle a :disabled on login button
this.errors = null;
try {
await apiClient.get('/sanctum/csrf-cookie');
await apiClient.post('/login', {
email: this.email,
password: this.password
});
// Do something amazing
} catch (error) {
this.errors = error.response && error.response.data.errors;
}
this.loading = false;
},
},

CORS Access to XMLHttpRequest at X from origin has been blocked by CORS policy

CORS Access to XMLHttpRequest at X from origin has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Hi, I am struggling to resolve my problems with CORS denying interaction of my Vue component with an external API with axios since it returns this error. I've tried both using Barryvdh's Cors header support and making a middleware and custom route. It simply won't work. Everything that has been mentioned in README.md in Barryvdh's repo has been done and unfortunately, this problem won't get resolved by any means necessary.
Here is the code, even though I don't think there's need to show since it's exactly the same as mentioned in the repo;
inside Kernel.php:
protected $middleware = [
\Barryvdh\Cors\HandleCors::class,
inside app.php (providers array):
Barryvdh\Cors\ServiceProvider::class,
config/cors.php:
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedHeaders' => ['*'],
'allowedMethods' => ['*'], // ex: ['GET', 'POST', 'PUT', 'DELETE']
'exposedHeaders' => [],
'maxAge' => 0,
Here's the axios get call (I've replaced my token with 'TOKEN')
methods: {
loadWeatherData: function() {
axios.get( 'http://api.openweathermap.org/data/2.5/weather?q=London&mode=json&units=metric&appid=TOKEN' )
.then( function( response ) {
console.log( 'radi' );
}).catch( errors => {
console.log( errors+' ne radi');
});
}
},
I've composer dump-ed, nothing affected resolving the problem.
Is there something I am doing wrong and are there any solutions for this problem? Thanks in advance!
The problem here seems to be that axios likes to send its own default headers, and these don't pass the preflight check for your external request. To fix this, you will need to remove the offending headers.
I was able to recreate your error, and also to bypass the CORS issue using the code below.
let url = 'https://api.openweathermap.org/data/2.5/weather?q=London&mode=json&units=metric&appid=TOKEN';
// create a new instance so we don't delete the headers for other requests
let instance = axios.create();
// delete headers if existing
delete instance.defaults.headers.common['Accept'];
delete instance.defaults.headers.common['X-Requested-With'];
delete instance.defaults.headers.common['X-CSRF-TOKEN'];
// use the new axios instance to make your get request
instance.get(url)
.then(function(response) {
console.log(response);
}).catch( errors => {
console.log(errors + ' ne radi');
});
Hope this helps and good luck!
You can add into TrustHosts.php Middleware without doing anything extra. Read more from here https://stackoverflow.com/a/70361284/2612926

Laravel Passport: CreateFreshApiToken in App: Authorization Failed

I'm attempting to install Laravel Passport on Laravel 5.7.18 using PHP 7.2.13.
My application consumes the API within itself using JavaScript (Axios with Vue)
I'm getting a 401 Unauthorized error within the JavaScript web application. I've read the documentation and added CreateFreshApiToken to the web Kernel. The laravel_token cookie is in fact setting itself. However, the oauth tables are clean in the database.
Http/Kernel:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
JavaScript:
axios.get("api/users/" + id).then(({ data }) => {
this.user = data;
});
Auth.php:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Routes (Api.php):
Route::middleware('auth:api')->group(function () {
Route::resource('users', 'UserController');
Route::resource('groups', 'GroupController');
// .. plus more resources
});
Axios Config:
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
var token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
Headers in Browser that returns 401:
Working Request with Postman:
If you are using a username and password for initial login then the assumption is you are building a First Party application that has the right to make a user/pass login attempt.
It is highly recommended that if you are building a Reactive app using
Angular, React.js or Vue.js then an SPA (Single Page Application)
approach will yield a much more robust product.
https://en.wikipedia.org/wiki/Single-page_application
You should note that with this particular method, if your application
makes a static (none ajax request) and thus reloads in the browser,
you will loose the auth token. In this case you are not playing host
to an app that is by it's very definition an SPA, so if you need to retain the token between
static request reloads then you need to store the token in a cookie, I
suggest using a cookie rather than localStorage because the availability of
localStorage is not 100% guaranteed to be at your disposal in all web browsers.
If your application is on the same domain, you do not need to use
Passport. Instead native session cookie auth is perfectly fine, all
you have to do is make sure you are passing the CSRF Token for post
requests.
For user/pass token grants you should follow this guideline:
https://laravel.com/docs/5.7/passport#password-grant-tokens
From that guide, when you make a successful request to /oauth/token, the returned token should be set in your application as an Authorization header with Bearer token.
The token request response looks like this:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKVJhb...nheKL-fuTlM",
"refresh_token": "def502008d6313e...94508f1cb"
}
You should request and handle that JSON object as follows:
axios.post('/oauth/token', {
grant_type: "password",
client_id: "1",
client_secret: "zkI40Y.......KvPNH8",
username:"email#address.com",
password:"my-password"
}).then( response => {
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.access_token}`
} );
The values for client_id (id) and client_secret come from oauth_clients table, there should already be an entry in there.
If not then run php artisan passport:client --password
Don't forget that you will have to configure some headers, look to this post has some relevant information for the Oauth Authorization header:
How to send authorization header with axios
If you say your tables are clean in the database, try to run this command again:
php artisan passport:install
This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:
Laravel 5.7 Passport docs

Resources