I'm working on a personal project using Laravel 5.6 and Axios library (standard Laravel 5.6 package).
I need to send first GET then POST request using Laravel's API and axios' http requests, but I'm not using Passport or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.
If I set my API routes using auth:api middleware, I always get unauthorized response, whichever is the request type, this is the error output :
Error: Request failed with status code 401
The message :
message: "Unauthenticated."
But, as I read in the documentation, axios headers are set inside laravel's bootstrap.js to read the authorization token from meta tags (and the code is there) :
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// further down the file...
let 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');
}
While, if needed, here's the http request code :
axios.post('/api/latest', formData)
So, why am I unauthenticated if those are set?
I've tried removing the auth:api middleware and, of course, it's working:
- Route::middleware('auth:api')->get('/latest', 'InternalApiController#latestEp');
+ Route::get('/latest', 'InternalApiController#latestEp');
What am I doing wrong?
I'm not using Passort or any library like that since it's an internal API serving only VueJS to obtain stuff from the database.
If the API is not stateless, meaning that the user is known to be logged in with a standard session cookie, then you can just use the default 'web' middleware for the API routes.
In the default RouteServiceProvider, change the mapApiRoutes function to use the web middleware instead:
protected function mapApiRoutes()
{
Route::prefix('api')
// ->middleware('api')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
That being said, you should really put the API routes behind the default 'auth' middleware since they're not throttled by default.
In the routes/api.php file:
Route::group(['middleware' => 'auth'], function() {
Route::get('/latest', 'InternalApiController#latest');
});
And if you want to ensure it's an AJAX request, you can create a simple middleware that checks that the request has the X-Requested-With header set to XMLHttpRequest.
class RequestIsAjax
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!$request->ajax()) {
return redirect()->route('login.index');
}
return $next($request);
}
}
And register it within the $routeMiddleware array inside the \App\Http\Kernel class.
protected $routeMiddleware = [
'ajax' => \App\Http\Middleware\RequestIsAjax::class,
With Laravel 8, I was getting a 401 error when trying to request something from the backend api. Following the accepted answer got me close, but it still didn't work. I believe this was because I have the dashboard on a subdomain as users.mydomain.tld and where I was using Axios was on the primary domain as mydomain.tld/some/path. The issue is the path for the session cookies. When re-configuring this, you need to clear all the cookies (dev toolbar). Then re-login like normal after you have made your changes. Else, the cookies will be messed up and you won't see it fixed or you wont be able to login at all until you clear them.
I reverted all the changes I made (following the accepted answer) and pinned down the SESSION_DOMAIN environment variable being the key ingredient, at least when it comes to the main user's area being on a sub-domain.
In your .env file:
SESSION_DOMAIN=".yourdomain.tld"
In app/Providers/RouteServiceProvider.php change api to web in the boot() method:
Route::prefix('api')
//->middleware('api')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
In routes/api.php:
Route::middleware('auth')->get('/user', function (Request $request) {
return $request->user();
});
In your resources/js/bootstrap.js:
window.axios.defaults.withCredentials = true;
After modifying the bootstrap.js file, you will need to run npm run dev again to recompile.
Now, you should be able to use Axios in your frontend JavaScript. Whether it be jQuery or Vue, whatever you are using.
axios.get('/api/user')
.then(response => console.dir(response.data))
.catch(error => console.log(error));
CSRF-token is not the same as an authorization token. Most likely you will manually need to add the authorization token, which is probably like this, depending on your method of authorization.
Related
I'm trying to take advantage of the new signed middleware in Laravel 5.7, but for some reason the generated signed URL is returning 403 Invalid Signature.
I'm using the latest Laravel Version, with PHP 7.2
This is my web.php route:
Route::get('/report/{user}/{client}', function ($user, $client) {
return ("El usuario es: $user y el cliente es: $client");
})->name('report.client')->middleware('signed');
and this is in my controller:
$objDemo->tempURL = Url::temporarySignedRoute('report.client', now('America/Panama')->addDays(5), [
'user' => 1,
'client' => 1
]);
The URL is generated and shows something like this:
https://example.com/report/1/1?expires=1545440368&signature=55ad67fa049a74fe8e123c664e50f53564b76154e2dd805c5927125f63c390a1
But when i click the link the result is a 403 with the message: "Invalid signature"
Any ideas? thanks in advance
-----------UPDATE------------
Things i've done already:
Try the route without signing, and works perfectly
Try the route without parameters and only signing
Try the route without temporary setting and only signing
Set cloudflare's ip to trusted proxies
Disable HTTPS, Enable HTTPS
Nothing seems to work, always getting the 403 invalid signature page
-----------UPDATE 2------------
Ok, so after some digging and testing, i found out that laravel signed routes won't work if the user is logged in, this is weird, if i logout then the route works perfectly, but if i log-in then it shows the 403 error, might this be because Laravel adds the session cookie header after everything else? and so the signed route fails because of it? it's this the way it should be?
Weird, because let's say i want to create a temporary link for my users to download something, if they are logged into my Laravel app, they will get this 403 error message... :(
------------UPDATE 3------------------
I tried in a fresh installation of laravel and worked perfectly, so it's something from my main Laravel app, also tried to install every composer dependency into the Fresh installation of Laravel, and still worked perfectly no matter the user login status, so it's not a conflict with my dependencies.
Try below code:
class TrustProxies extends Middleware
{
protected $proxies = '*';
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
After debugging UrlGenerator::hasValidSignature(), i ended by DD the variables inside UrlGenerator.php like this:
public function hasValidSignature(Request $request, $absolute = true)
{
$url = $absolute ? $request->url() : '/'.$request->path();
//dd($url);
$original = rtrim($url.'?'.Arr::query(
Arr::except($request->query(), 'signature')
), '?');
dd($original);
$expires = Arr::get($request->query(), 'expires');
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
return hash_equals($signature, (string) $request->query('signature', '')) &&
! ($expires && Carbon::now()->getTimestamp() > $expires);
}
the $original variable showed me what was actually happening with my URL, and showed this:
https://example.com/report/1/1?expires=1546586977&settings%5Bincrementing%5D=1&settings%5Bexists%5D=1&settings%5BwasRecentlyCreated%5D=0&settings%5Btimestamps%5D=1&profile%5Bincrementing%5D=1&profile%5Bexists%5D=1&profile%5BwasRecentlyCreated%5D=0&profile%5Btimestamps%5D=1&user%5Bincrementing%5D=1&user%5Bexists%5D=1&user%5BwasRecentlyCreated%5D=0&user%5Btimestamps%5D=1
as you can see there are parameters after the expires parameter, those parameter where aded after the route creation, and that was the problem, this happened because i had a middleware sharing some information to the views like this:
UserDataMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use App\User;
use App\Setting;
use App\UserProfile;
use Illuminate\Support\Facades\View;
class UserData
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
$settings = Setting::where('user_id', Auth::user()->id)->first();
$profile = UserProfile::where('user_id', Auth::id())->first();
$user = Auth::user();
View::share('settings', $settings); //Another way to share variables, with the View::share
View::share('profile', $profile);
//Now we need to share owr variables trough the REQUEST to our controllers
$request->merge([
'settings' => $settings,
'profile' => $profile,
'user' => $user
]);
}
return $next($request);
}
}
this middleware was inside the middleware groups, so that was the problem hopefully if someone in the future experiments this, then it could check that first.
UNDERSTANDING LARAVEL EMAIL VERIFICATION WAY
Understanding the way of verification can help you simply solve this error.
laravel makes a temporary signed url using method URL::temporarySignedRoute(),
this method is called in verificationUrl() located at
\vendor\laravel\framework\src\Illuminate\Auth\Notifications\VerifyEmail.php.
/**
* Get the verification URL for the given notifiable.
*
* #param mixed $notifiable
* #return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
URL::temporarySignedRoute() makes urls according to config('app.url) which is set to .env('APP_URL') by default.
So if the url that sent to emails is different from url that will laravel get at the time of verification (time of checking signature of url), 403 | invalid signature occurs.
Example:
if you set APP_URL to http://yourdomain.com/, verification link should be look like http://yourdomain.com/email/verify/{id}/{hash}. now if you set your server configs to redirect to https, invalid signature will occured, since the url laravel gets is https://yourdomain.com/email/verify/{id}/{hash} and not same as email verification url.
I just had this problem and turns out that empty parameters in the URL will never validate. So when you do this:
URL::temporarySignedRoute('newsletter.verify', now()->addDays(3), ['name' => $name, 'email' => $email])
but name is an empty string (because it's not mandatory), URL will get generated with name= as part of query string, but this code inside Laravel
$original = rtrim($url.'?'.Arr::query(Arr::except($request->query(), 'signature')), '?');
will not return the empty name, hence the URL was 'altered' and validation fails. The commonly used middleware ConvertEmptyStringsToNull might have something to do with this.
If you're using Heroku, AWS or any other service that makes use of a LoadBalancer. Do also ensure that the proxy reaching your application is trusted.
See this answer for more.
i had similar problem in dusk,
and it was the APP_KEY in .env.dusk.testing that was not matching the APP_KEY in .env
I had the same issue and was going insane until I stumbled upon #LaravDev's answer.
Note::I'm using Laravel 7 which is different on the web.php page
My original code looked like this, which essentially just adds a variable to the request to tell my views not to show the sidebar.
Route::middleware(['noSidebar'])->group(function()
{
Auth::routes(['verify' => true]);
});
I had to remove the Auth::routes() shortcode and switch it out for the full stack of Auth routes. (Note this is different for each version of Laravel)
Route::middleware(['noSidebar'])->group(function()
{
// Authentication Routes...
Route::get('login', 'Auth\LoginController#showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController#login');
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
// Registration Routes...
Route::get('register', 'Auth\RegisterController#showRegistrationForm')->name('register');
Route::post('register', 'Auth\RegisterController#register');
// Password Reset Routes...
Route::get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('password.email');
Route::post('password/reset', 'Auth\ResetPasswordController#reset')->name('password.update');
// Confirm Password (added in v6.2)
Route::get('password/confirm', 'Auth\ConfirmPasswordController#showConfirmForm')->name('password.confirm');
Route::post('password/confirm', 'Auth\ConfirmPasswordController#confirm');
// Email Verification Routes...
Route::get('email/verify', 'Auth\VerificationController#show')->name('verification.notice');
Route::post('email/resend', 'Auth\VerificationController#resend')->name('verification.resend');
});
//Moved the routes with tokens in the URL to outside my middleware grouping.
Route::get('email/verify/{id}/{hash}', 'Auth\VerificationController#verify')->name('verification.verify');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
Tada it works!
Thank you all
I had APP_URL=http://localhost in the .env file. When I changed the value to the URL from the server, the problem was solved.
I was using Laravel 8+.
protected $proxies = '*';
Change this code on below path
app/Http/Middleware/TrustProxies.php
Essentially your signatures didn't match because the URL you generated via \Illuminate\Support\Facades\URL::signedRoute was altered by your middleware, meaning when it came to check $request->hasValidSignature() this returned false.
I had a similar issue where SendGrid was adding UTM tracking query strings to the URL's in my email (&utm_campaign=website&utm_source=sendgrid.com&utm_medium=email), which altered the URL and ultimately changes the signature.
As I hack I added the following code to my controller to strip out the additional query params and re-using the signature:
// Fix issue with sendgrid highjacking signed URL's with extra query params..
if ($request->query('utm_campaign')) {
$sig = $request->query('signature', '');
$url = route('route-key') . '?signature=' . $sig;
return redirect($url);
}
Its to my knowledge that a JWT based authorization system is usually reserved for SPA'S ( you know, one view, one React/Angular/Vue app, with one bloated app.js file), however I'm attempting to utilize the magic of JWT with a slightly separate structured application.
Structure
Rather than serving up one blade.php view from my Laravel app that garners one Vue app and instance, I'm attempting to serve up TWO separate blade.php views, that each operate as their own separate Vue SPA: one for the exterior of the application (pre-auth) and another for the interior of the app (post-auth).
Current State of App
To power my app's authentication system, I've utilized Tymon's jwt-auth lib ( a beautiful lib btw ) and tie everything together on the front with (as previously stated) Vue/Vuex. Everything works as expected, in my Register and Login components I'm able to hit my api, get a JWT in response, store it locally then annex said token into my Axios headers allowing all subsequent requests to harbor this token.
Dilemma
Now I'm at a crossroads. The post-auth route/view that I want to serve up is protected by a custom JWT middleware that redirects if a valid token is not presented:
Route::get('/home', 'Auth\HomeController#home')->middleware('jwt');
middleware
class JWT
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
JWTAuth::parseToken()->authenticate();
return $next($request);
}
}
and my pre-auth view and all its routes are protected by Laravel's native guest RedirectIfAuthenticated middleware, which is Guarded by JWT now:
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
}
Questions
So this begs down to the following questions:
1) after a successful register/login on the front-end and a JWT is generated, stored locally and in Axios headers, How do I then redirect to my post-auth route with this valid token available?
2) How do I then make sure that Valid JWT persist and is present when the guest routes are hit to successfully redirect back to my post-auth route?
I'd prefer to keep all redirects and persistance checks on the backend if feasible
On successful login, you will have the token, let's say its called $jwt_token
You can redirect to the page you are protecting once authorized and set the cookie in the response:
return redirect('/home')->cookie(
'access_token', //name
$jwt_token, //value
config('session.lifetime'), //expiration in minutes (matches laravel)
config('app.url'), // your app url
true // HttpsOnly
);
From here, Axios can have access to the cookie by parsing the cookies on the document and retrieving the access_token
let token = document.cookie.split(';') // get all your cookies
.find(cookie => cookie.includes('access_token')) // take only the one that matches our access_token name
.split('=')[1] // get just the value after =
// terrible code example above for you
Now you can use this in your Axios requests by adding it as the value to Bearer in the Authorization header:
Authorization: `Bearer ${token}`
Your JWT middleware already leverage the authenticate method and therefore it should be handling the expiry for you as it stands:
JWTAuth::parseToken()->authenticate();
Under the hood this will attempt to validate the token's expiry based on the current TTL set in the config/jwt.php file. Given your work flow, I would also blacklist the token if it expires. You can add an Event Listener that listens for expired tokens and blacklists them by listening to Event::listen('tymon.jwt.expired');.
Please excuse any syntax errors, formatting issues or misspellings, I'm on my pone and will edit later to resolve those.
So there are a couple of ways you can make sure the JWT token is available everywhere for Axios or indeed any frontend to use.
The most common way is to store the token in either a cookie or in the Web Storage of the browser (localStorage / sessionStorage)
The difference between localStorage and sessionStorage is that data stored in localStorage persists through browser sessions, sessionStorage is cleared when the page session ends.
The general consensus is that cookies are slightly more secure because they have a smaller attack vector, though neither method is completely secure. If you want to go more in depth you can start by reading this article.
To get more specific concerning your problem, first you want to setup token storage using one of the methods explained above, recommended method is cookies, you can find examples of how to do it with pure Javascript here.
Now that you have the token on every page you can redirect the user whichever way you like. Though I would suggest that instead of using your own middleware for JWT authentication, you can use the one that the JWT library provides: jwt.auth.
This middleware will automatically respond with error codes if something is wrong with the token, if there is it will return one of the following HTTP responses:
token_not_provided
token_expired
token_invalid
user_not_found
If one of these responses are returned (or if the request status code is 400) you can simply use the frontend to redirect the user back to your pre-auth routes.
When signing in, after saving the token to a cookie, use the frontend to redirect to the post-auth routes.
I know you said you wanted to keep redirect logic in the backend but that doesn't really make sense when you're for example calling the API when you're signing in, you can't really both return the token and cause a redirect at the same time from just the backend.
UPDATE
Very simple example of how you can authenticate with only guard and still get a token for the API. Borrowing from the redirect example from #Ohgodwhy, you can put the following inside your RedirectIfAuthenticated middleware.
public function handle($request, Closure $next, $guard = null)
if (Auth::guard($guard)->check()) {
if ((\Cookie::get('access_token') == null)) {
$cookie = \Cookie::make(
'access_token',
\JWTAuth::fromUser(Auth::user()),
config('session.lifetime'),
null,
$request->refeerer,
false, // to make the cookie available in javascript
false // to make the cookie available in javascript
);
return redirect('/home')->cookie($cookie);
} else {
return redirect('/home');
}
}
return $next($request);
}
Just make sure that your $redirectTo in app/Http/Controllers/Auth/LoginController.php is set to a path that implements the RedirectIfAuthenticated middleware.
So here's the logic I ended up implementing:
In my LoginController.php login function, after successful authentication and generation of JWT, I return a response with Json and a new cookie, both with new token passed:
public function login(Request $request)
{
$creds = $request->only(['email', 'password']);
if (!$token = auth()->attempt($creds)) {
return response()->json([
'errors' => [
'root' => 'Incorrect Credentials. Try again'
]
], 401);
}
return $this->respondWithToken($token);
}
protected function respondWithToken($token)
{
return response()->json([
'meta' => [
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]
], 200)
->withCookie(cookie('access_token', $token, auth()->factory()->getTTL()));
}
In my guest RedirectIfAuthenticated middleware check for cookie, if exists, setToken which in turn sets Guard to Authenticated and will always redirect to /home if token is available and valid:
public function handle($request, Closure $next, $guard = null)
{
if ($request->hasCookie('access_token')) {
Auth::setToken($request->cookie('access_token'));
}
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
return $next($request);
}
And In my post-auth Routes middleware I also setToken and if its valid and exists, will allow access, otherwise will throw a range of JWT errors which just redirect to pre-auth view:
public function handle($request, Closure $next)
{
JWTAuth::setToken($request->cookie('access_token'))->authenticate();
return $next($request);
}
Finally, I decided to handle redirection in the front-end being I'm using Axios which is promised based and can assure that cookie will be set before redirecting to post-auth view so no funny business happens! Cheers! Hope this helps anyone on their quest to Multi-Page SPA magic!
I use Dingo with Laravel 5.1 to create simple API.
So at route.php I have:
$api = app('Dingo\Api\Routing\Router');
$api->version('v1', function($api) {
$api->get('getvoucher', 'App\Http\Controllers\BitemsController#index');
$api->get('update/{key}', 'App\Http\Controllers\BitemsController#update');
$api->post('store', 'App\Http\Controllers\BitemsController#store');
$api->post('authenticate', 'App\Http\Controllers\AuthenticateController#authenticate');
$api->post('logout', 'App\Http\Controllers\AuthenticateController#logout');
$api->get('token', 'App\Http\Controllers\AuthenticateController#getToken');
});
and my BitemsController is:
public function index(Request $request)
{
$bitem = Bitem::where('key',$request->key)->where('id',$request->pin)->first();
return $bitem;
}
public function store(Request $request)
{
$bitem = new Bitem($request->all());
$bitem->save;
return $bitem;
}
Now I use POSTMAN application to test the API, and when I send GET to localhost:8888/api/getvoucher everything is fine, but when I make POST request to store some data then I got error:
"message": "500 Internal Server Error",
"status_code": 500,
"debug": {
"line": 53,
"file": "C:\\wamp\\www\\dine\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken.php",
"class": "Illuminate\\Session\\TokenMismatchException",
"trace": [
POSTMAN:
To fix the problem I try to add:
protected $except = [
'api/*',
];
inside middleware VerifyCsrfToken.php but wnt work.
Please tell me how to solve my problem...
For Postman to work, you need to either send the correct CSRF header, or remove the need for it on your routes.
I'm assuming based on your screenshot your Dingo API routes are using API_PREFIX=api in your .env file.
Check the Laravel documentation on CSRF tokens for more information about those. The gist that #BM2ilabs suggested has some basics on how to find out what CSRF token you're using for local testing in your session to put into Postman.
If you don't want to use CSRF protection, you are correct in using the $except property on the VerifyCsrfToken middleware as per the Laravel documentation - this has also come up on Stack Overflow before. Tricky to troubleshoot that without seeing your Kernel and the full middleware file you're using. If the $except property really isn't working for you, you can always override the VerifyCsrfToken::handle() method as per this post and add whatever route checks you like:
public function handle($request, Closure $next)
{
if ( ! $request->is('api/*'))
{
return parent::handle($request, $next);
}
return $next($request);
}
If you are only creating an API that is going to be stateless and not need CSRF protection, you could just comment out the usage of the VerifyCsrfToken middleware in your Kernel entirely (and possibly some of the other session middleware), although I would recommend using some kind of authentication/validation that your visitor should be allowed to access the API endpoint.
You just need to add csrf_token to the post , but that might be tricky with postman
in laravel add the header with what you use
For example Axios :
it already has that integrated
jQuery Ajax
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
for more info's
Update
After some searching i found this article that show how to make csrf work with POSTMANas well
Gists of #ethanstenis
I am building a REST user-microservice using Laravel 5.5 + Passport.
I am using the standard Passport::routes(), but I have had to modify the Auth::routes in order to make them return JSON responses, and to make them work with Passport.
I have added the following lines to my routes/web.php file:
Route::group(['middleware' => 'auth:api'], function () {
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
});
This allows me to POST https://myapi/logout
If I make the call with the header "Authorization => Bearer TOKEN", I get a successful logout response.
If I provide no header at all, I get a "not authenticated" message (which is good)
However, if I provide the header with a revoked token, I get a recursive deadloop of the function: Illuminate\Auth\RequestGuard->user() (it keeps calling itself recursively until stack-overflow)
This is all done in the auth:api middleware, my logout code is not reached, but my LoginController constructor is called. Constructor code:
public function __construct(Application $app)
{
$this->apiConsumer = $app->make('apiconsumer');
$this->middleware('guest')
->except('logout');
}
I'm struggling to understand if it's my code causing this issue, or some combination of Laravel + passport + auth.
My first thought was that the auth:api middleware fails to authenticate the user, and as a result redirects the user to /home, where for some reason it's triggered again, recursively. But if that was the case, why would it work correctly with no header?
My current thinking is that the token in question does exist in the database, but Laravel is failing to figure out that it's revoked.
Any suggestions appreciated,
I found an answer (if not the answer) after a lot of research. It appears this is a Laravel bug (https://github.com/laravel/passport/issues/440). The solution is to add OAuthServerException to the $dontReport array in app/Exceptions/Handler.php:
class Handler extends ExceptionHandler
{
protected $dontReport = [
...
\League\OAuth2\Server\Exception\OAuthServerException::class,
];
}
This will avoid trying to log user information, thereby avoid the deadloop.
I have faced this in localhost. in my case, I have used xampp server and facing this issue
after creating a virtual host like "testlarave.test" then solve the error
I've tried to exclude requests from another localhost server (http://localhost:8080/order/placeorder) to another one localhost server (http://localhost:8000)
I don't want to disable all csrf protection by removing
\App\Http\Middleware\VerifyCsrfToken::class in Illuminate\Foundation\Http\Kernel.php
I've tried to modify app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'http://localhost:8080/*',
'http://localhost:8080',
'/order/placeorder/*',
'http://localhost:8080/order/placeorder'
];
and I also tried this way
private $openRoutes = [
'http://localhost:8080/*',
'http://localhost:8080',
'/order/placeorder/*',
'http://localhost:8080/order/placeorder'
];
public function handle($request, Closure $next)
{
//add this condition
foreach($this->openRoutes as $route) {
if ($request->is($route)) {
return $next($request);
}
}
return parent::handle($request, $next);
}
But I still got this error
TokenMismatchException in VerifyCsrfToken.php
Can anyone suggest me what should I do and what I've done wrong?
The exceptions are routes within your own application that are excluded, not the URLs of servers that are requesting it. You will never put localhost, http, or any domain in these exceptions in normal circumstances. If you wish for a request by an external server to be accepted, I would disable CSRF protection for the routes it is accessing (because you want a cross-site request, that's what CSRF prevents).
For example, if you want any external server to be able to send a POST request to /order/placeorder, you would simply add that route to the exclusion. You also need to add any other route you want it to be able to access. If there are a lot, there are other more manageable ways to do this with middleware as well.
To authenticate the server making the request, it should send a token to verify itself. You can create a static token for this purpose (like an API key), or possibly use an OAuth implementation of some sort with access/refresh tokens - there is a package for Laravel for this that makes it easy.