Laravel CSRF token for AJAX CORS - ajax

How can I supply a csrf token for cross domain(subdomain) request in Laravel.
Both of the domain domain.tld and sub.domain.tld is run under the same Laravel Framework,
therefore I could use csrf_token() in the sub.domain.tld, I directly attach it to the request but it keep giving me tokenMismatchException, and I tried to turn off the csrf filter and dump the Input::('_token') and the Session::token(), it always DOES NOT MATCH
So, how can I supply a token for for another domain?

Have you added the correct headers to allow ajax requests cross domain/subdomain?
If not, in app/filters.php in your App::before filter, add:
App::before(function($request)
{
$host = explode( '.', $_SERVER['HTTP_HOST'] );
$subdomain = array_shift( $host );
header('Access-Control-Allow-Origin: http://' . $subdomain . '.yourdomain.com');
});

Related

Provide csrf _token in headers when using Laravel API

How can I create a Laravel Gate where client has to provide CSRF _token in headers when when using my api?
Idea is that I want somekind of gate when using my route:
http://127.0.0.1:50004/api/third-party/unsplash
Otherwise, anyone can copy and use above route.
api.php
Route::get('/third-party/unsplash', [UnsplashController::class, 'show'])
**// my gate here!!!!**
UnsplashController.php
public function show()
{
return ['authorizationKey' => 'Client-ID 1234'];
}
Unsplash.vue file:
const myAsync = async function fetchUnsplash() {
const myAPIKey = await fetch(
'http://127.0.0.1:50004/api/third-party/unsplash'
);
const dataMyAPIKey = await myAPIKey.json();
const response = await fetch('https://api.unsplash.com', {
headers: {
Authorization: dataMyAPIKey,
},
});
console.log(response);
};
Rather than relying on a CSRF token, you probably want full API authentication with something like Laravel Sanctum. Implementing Sanctum will help you protect your API effectively (including from CSRF attacks).
If you really do want to require a CSRF token in the header, you can implement that by adding custom middleware to those routes that would check for the CSRF token. You can look at the VerifyCsrfToken middleware as a starting point. Typically, the X-CSRF-TOKEN header is used to pass the CSRF token in a request.
Finally, you could potentially just use the built-in VerifyCsrfToken middleware if you choose to convert your /third-party/unsplash route from a GET to a POST.
All-in-all, I think using a more robust solution like Sanctum will serve you well and prevent other potential issues that you might not have thought of yet.

How to verify a token with Laravel Passport?

I want to validate a token using Laravel Passport. My API's consumer will pass the token via the Authorization header as a Bearer token and I want that Laravel Passport returns me if is a valid token.
I don't want to use a middleware, my API will be in another Laravel Project, but I want this project to call the Laravel Passport server just for check if a token is valid, how can I check the token?
I'm issuing the tokens right, just left verify them, but I don't know how:(
This is how you can verify tokens without the middleware:
Auth::guard('api')->check();
You can create your own middleware. Inside that middleware's handle, pick the Bearer token and call your Passport server, depending on the response returned call next if true, or abort if false. Something like this:
public function handle($request, Closure $next)
{
try {
$passportEndpoint = 'your_passport_endpoint_here';
$client = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => $request->header('Authorization')
]);
$response = $client->get($passportEndpoint);
if ($response->status() === 200) {
$body = $response->object();
//do some stuff with response here, like setting the global logged in user
return $next($request);
}
}
catch (RequestException $exception) {
}
return abort(401, 'You are not authenticated to this service');
}
If you don't want to use the Passport middleware in the project where you want to validate the tokens, you would have to create an endpoint in the Laravel Passport server that can accept the token, perform the usual Passport validation and return a response to your service.
It would be an implementation of the Token Introspection spec: https://www.rfc-editor.org/rfc/rfc7662 - though you have to implement it yourself, as I think that Laravel Passport doesn't support it out-of-the-box.
Also, when verifying JSON Web Tokens (if this is the type of tokens that you use), remember that verifying the signature is not enough. Have a look at this best practices article to know how to properly work with JWTs.

how to solve cors Allow Access control in vue js and laravel application

I Have tried almost everything. My front end is developed in vue js . backend is in laravel. we have written api for another website from which we are trying to fetch data. If directly access that website Url it gives all the data but when i try to access it from my website with axios it gives me this error.
Access to XMLHttpRequest at 'https://example.com/api/tickets/fetch_tickets?page=undefined' from origin 'http://localhost:8000' has been blocked by CORS policy: Request header field x-requested-with is not allowed by Access-Control-Allow-Headers in preflight response.
that website form which i am trying to fetch data also build in laravel. i have created middleware and applied it on api routes. I added chrome extension Allow Cors with which it works fine but we cant ask every client to use that extension.
We access that url from other website which is accessing data nicely. only vue js app creating these issue.
Vue Code
getTickets() {
axios.get( 'example.com/api/tickets/fetch_tickets?page=' + this.pagination.current, {
}).then((response) => {
// console.log(res.data.data)
// this.desserts = res.data.data;
// this.loadingprop = false;
this.desserts = response.data.data;
this.pagination.current = response.data.current_page;
this.pagination.total = response.data.last_page;
console.log(response.data.data);
}).catch((err) => {
this.handleErrors(err.response.data.errors);
})
.then(() => {
this.loading = false;
});
}
other website's routes
Route::group(['middleware' => ['api','cors']], function () {
Route::group(['prefix' => 'tickets'], function () {
Route::post('/store_ticket_auth', 'TicketApiController#storeTicketAuth'); //enter ticket auth
Route::get('/fetch_tickets', 'TicketApiController#fetchTickets'); //get all tickets
Route::get('/fetch_replies/{ticket_id}', 'TicketApiController#fetchTicketReplies'); // get all replies by ticket id
Route::post('/send_reply', 'TicketApiController#sendTicketReply'); // Send reply
Route::post('/update_ticket', 'TicketApiController#updateTicketStatus'); // Update Status
});
});
Do I need to add this on my cuurent project too?
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
I think the issue is on client side but dont know why it is not working.
I tried all answers on stackoverflow but nothing works
I have to add these lines in my index.php file of laravel
header("Access-Control-Allow-Origin: *");
//header("Access-Control-Allow-Methods", "DELETE, POST, GET, OPTIONS");
header("Access-Control-Allow-Headers:*");
if ($_SERVER['REQUEST_METHOD'] == "OPTIONS") {//send back preflight request response
return "";
}
Solved my issues by commenting out:
// window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
in resources/js/bootstrap.js
The error is telling you that the server won't allow the client to use a x-requested-with header.
In php you can do this to allow the server to accept that header:
header('Access-Control-Allow-Headers: X-Requested-With');
If you want the easy way you can use laravel-cors
You can follow the installation step and add this code in your config/cors.php
'allow_origins' => [
'https://yourfrontendrequest.url',
],
Install Moesif Origin & CORS Changer Chrome extension and
Then go to resources/js/bootstrap.js and comment out this line // window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
you can disable same origin policy in chrome
press win + R
and then copy this :
chrome.exe --user-data-dir="C://Chrome dev session" --disable-web-security

Can't get auth user with laravel passport, keep getting "Unauthenticated" error

I can't get the information of the authenticated user in a Laravel passport app with JWT and vue.
I've installed laravel passport. Ive done everything in the documentation and added:
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
To consume it with js for a SPA app.
I've protected my routes with the auth:api middleware, but i keep getting:
{"Status":{"api_status":0,"Code":401,"Message":"Unauthenticated"}}
When i use postman to manually insert the CSRF-TOKEN in Authorization Bearer Token. It does give me the auth user.
Whatever i do, i keep getting null on Auth::user(); in my controllers and routes
Laravel V5.7
Node V10.15.3
Npm V.6.9.0
You need to send a POST request (using Postman/Insomnia) with the details of the user you want to log in as to /oauth/token in your app which will respond with an API token. You save this api token locally, and then add add it as a Header variable to your guzzle/axios/whatever function's http calls (every one of them!) to your API.
$http = new GuzzleHttp\Client;
$data = 'Whatever';
$response = $http->request('POST','api/user',[
'data' => $data,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer xxxxxxxxxxxxThis is the long authentication string returned from the Postman requestxxxxxxxxxxxxxx'
]
]);
dd(json_decode((string) $response->getBody())); // To view the response
From: https://laravel.com/docs/5.5/passport#creating-a-password-grant-client

Another Laravel Token Mismatch Exception issue

I have an application which uses AJAX quite a bit on a Laravel 5.3 application. I have some pages that are behind authentication and some that are not. The ones that are inside of authentication are working fine. The one that is outside (public facing) are giving me a the infamous TokenMismatchException in VerifyCsrfToken.php line 68. In order to attach the token to the AJAX header, I am using this...
$.ajaxSetup({
cache: false,
async: true,
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
And it is working because when I make a request, I can see this...
...but the tokens are not matching. When I go to the framework file Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class and do a dd() on the session token and the token that was passed, they do not match.
Things I have tried:
Clearing Cache (views, config, cache)
Changing session driver from
file to Redis
Using csrf_field() instead of AJAX headers
I cannot figure out why this is not working. Any other ideas?
If you look at this code of laravel Github Link
/**
* Determine if the session and input CSRF tokens match.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function tokensMatch($request)
{
$sessionToken = $request->session()->token();
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
if (! is_string($sessionToken) || ! is_string($token)) {
return false;
}
return hash_equals($sessionToken, $token);
}
It checks for the X-CSRF-TOKEN and also tries to check for X-XSRF-TOKEN. You can also try to send the _token from the ajax. I hope this helps you.
And, I finally figured it out. I am using BrowserSync for livereload, which proxies all my requests to localhost:3000/*. When I was testing the public side, I was visiting it through the original domain name and not proxied through browsersync's localhost:3000 so that was causing session issues.
Basically, if you have BrowserSync running and you try in use your site not through browsersync, you can get token mismatch errors.

Resources