In Laravel 5, How to disable VerifycsrfToken middleware for specific route? - php

I am using Laravel 5 for developing an app. My app is connected with VendHQ API and I am intended to get some data from VendHQ through their webhook. As per their Documentation
When an event happens and triggers a webhook, we’ll send a POST
request to a URL of your choosing. The POST request will be in the
UTF-8 charset, and application/x-www-form-urlencoded encoding.
The problem is, when they try to send a POST request to my Laravel app, no CSRF Token is added in their post request and VerifyCsrfToken middleware is looking for a token and finally it throws a TokenMismatchException.
My question is, how can I avoid this default VerifyCsrfToken Middleware for some specific routes while keeping other post requests active?

In Laravel 5 this has chagned a bit. Now you can simply add the routes you want to exclude from csrftoken verification, in $except array of the class
'VerifyCsrfToken' (\app\Http\Middleware\VerifyCsrfToken.php):
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
// Place your URIs here
];
}
Examples:
1. If you are using a route group:
Route::group(array('prefix' => 'api/v2'), function()
{
Route::post('users/valid','UsersController#valid');
});
Your $except array looks like:
protected $except = ['api/v2/users/valid'];
2. If you are using a simple route
Route::post('users/valid','UsersController#valid');
Your $except array looks like:
protected $except = ['users/valid'];
3. If you want to exclude all routes under main route (users in this case)
Your $except array looks like:
protected $except = ['users/*'];
see: http://laravel.com/docs/master/routing#csrf-excluding-uris

CSRF is enabled by default on all Routes in Laravel 5, you can disable it for specific routes by modifying app/Http/Middleware/VerifyCsrfToken.php
//app/Http/Middleware/VerifyCsrfToken.php
//add an array of Routes to skip CSRF check
private $openRoutes = ['free/route', 'free/too'];
//modify this function
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);
}
source

If you are using version 5.2 then in: app/Http/Middleware/VerifyCsrfToken.php
you can add the route to the attribute: protected $except.
For example:
protected $except = [
'users/get_some_info',
];
After you perform this change, make sure you add the route in your routes.php.

Add your route to App\Http\Middleware\VerifyCsrfToken.php file:
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'route-name-1', 'route-name-2'
];

Related

Laravel CSRF protection exception now working

My Laravel project is in this link
http://localhost/demo/public // laravel project
and I have this external HTML form
http://localhost/attendance
Now I want to send data from the form to Laravel
but I got this error
419
Page Expired
so in my laravel project VerifyCsrfToken Class I wrote this
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'http://localhost/attendance'
];
}
but still, got the same error
419 Page Expired
Laravel resolve for you the baseUrl of your application, there is no need to put the full path, in your case the Middleware should be like below:
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'attendance/*'
];
}
One solution would be to send the data as a GET request instead of a POST one.
Once you put your work online, you would face cross-site protection on the browser.
The URI to be excluded is the one receiving the request so http://localhost/demo/public

Submit posts data from external website into laravel

I have a website that when the post is submitted goes to an external site where laravel is installed. I have a generic form on the website and upon submission i keep getting the 419 error in laravel. I've added the url in VerifyCsrfToken and it still wont work.
/app/Http/Middleware/VerifyCsrfToken.php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'https://pharaohmfg.com/collections/*'
];
}
web.php
Route::get('/', 'PostController#index')->name('home.index');
Route::post('callback', 'PostController#callback')->name('callback.post');
postController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index(Request $request)
{
return redirect()->away('http://pharaohmfg.com');
}
public function callback(Request $request)
{
return $request->all();
}
}
what am i doing wrong? the website is https://pharaohmfg.com/collections/billiard-pool-tables/products/siamun-pool-table
You have a few options that are better than your current self-answer.
Fix Your $except
First, your $except should be the route the form posts to, not the url the form is on. This has the benefit of having whatever else is in the Laravel app be protected by CSRF tokens. Your current solution turns all of them off for everything. This is not ideal, but it will accomplish what you are looking to do.
iframe Your Form
Make the form an iframe so you can have a legit CSRF token in the first place. This is what FaceBook and Twitter often do (or did the last time I look) for their social media buttons (like, share, retweet).
Ajax
Setting up cors headers, you can write some fancy js to fetch the CSRF token from another route, while keeping the form on the external site. The upside is that you could also submit the form using ajax making the existence of the external server obvious. This, like the iframe solution, would allow everything to be served with proper CSRF tokens.
i figured it out,
in app/http/kernel.php i commented out the verifycsrftoken class from being loaded.
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,
],
'api' => [
'throttle:60,1',
'bindings',
],
];

laravel VerifyCsrfToken excepts url with wildcard?

I try to build my own API. I begin so my only model for the moment will be a "User". Here is how I would like to call my API :
HTTP/POST http://example.com/api/user/ # get all the users
HTTP/POST http://example.com/api/user/1 # get the user with id "1"
HTTP/POST http://example.com/api/user/1/delete # delete the user with id "1"
...
So my file routes/web.php looks like this :
<?php
Route::group(['prefix' => 'api'], function() {
Route::group(['prefix' => 'user'], function() {
Route::post('/', 'ApiController#allUsers');
});
});
?>
But it will not works as I do not pass throught Route::resource static method, but with regular Route::post method. So the issue is that VerifyCsrfToken middleware will trigger and try to check for my CSRF token, but as I want my api to be consume in the future by many other advice I prefer to use my own secure system (which will be a public-private key pairs, but now I just want to check for the integrity of the data I distribute through the api, and I will then set the secure algorithm).
The good news is that Laravel is so clean and let you add your exceptions URL in the VerifyCSRFToken array which is shaped like this :
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'api/user',
'api/user/{howCanIManageTheWildCard}',
'api/user/{howCanIManageTheWildCard}/delete',
...
];
}
?>
Question :
You see on the middleware above I have 2 issues :
I will have to set manually all my routes (which at the end can be long)
I do not know if the middleware will be able to handle any wildcard
So can I come with a solution wich could let me do a url wildcard like api/* ? Like this it would be so much easier !
You can exclude URLs with /*
Eg.
instead of api/user you can use api/user/*
read here
Just a suggestion
since you are building an API using laravel you can put all your API routes in api.php routes file instead of web.php routes file, In that case you will not have to pass CSRF Token for post request on API routes.
And all the API routes will be like example.com/api/<route> by default, you will not have to group it.
you can read more about Laravel routing here
happy to help :):):)
Sometimes we use simple URL or Sometimes URL/Route carries some value. so that type of situation you can use these two codes. change code in a file
App\Http\Middleware\VerifyCsrfToken.php and $except =[]
for simple Route
$except =['http://127.0.0.2/oams/public/oams/payment/success']
for dynamic Route
// http://127.0.0.2/oams/public/oams/payment/{RandCode}/success
$except =['http://127.0.0.2/oams/public/oams/payment/*']

How to handle oauth2 and csrf token laravel

I installed Laravel 5.2 and oAuth2 Server Laravel
in my project. I have to use same function for web-site and web-api. For web-site my function is working properly but when I use same function for web-api shown error TokenMismatchException in VerifyCsrfToken.php line 67:.
My Route
/* for web*/
Route::post('admin/user_login', 'Auth\AuthController#authenticate');
/* for mobile api */
Route::group(['prefix'=>'api/','before' => 'oauth'], function()
{
Route::post('/user/login', 'Auth\AuthController#authenticate');
});
When I use this controller for web, this code working fine but when I call API that time shown error. How I can handle this? I have to use oAuth route and web route parallel. Thanks in advance.
you have to disable csrfToken verification for routes starting with api to do that edit your app/Http/Middleware/VerifyCsrfToken.php file and add api/* in the $except array the sample file from laravel app repo is as below
https://github.com/laravel/laravel/blob/5.2/app/Http/Middleware/VerifyCsrfToken.php
just make it something like
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'api/*'
];
}
also you have to remove oauth middleware from authenticate route, because during authentication the token is not available so route goes something like below
Route::group(['prefix'=>'api/'], function()
{
Route::post('/user/login', 'Auth\AuthController#authenticate');
Route::group(['middleware' => 'oauth'], function() {
// routes which needs oauth token verification.
})
});

Prevent Sessions For Routes in Laravel (Custom on-demand session handling)

I am building APIs for my Android app using laravel and default session driver set to REDIS.
I found a good article here http://dor.ky/laravel-prevent-sessions-for-routes-via-a-filter/ which sort of serves the purpose.
However when ever I hit the url it also hits the redis and generates the key which is empty. Now I want avoid creating empty session keys in redis. Ideally it should not hit the redis How can I do that?
Can we customise sessios in a way so that sessions are generated only for specific routes (or disable for specific routes)?
I can explain more with specific use case, please let me know.
Its really easy using the middleware in Laravel 5, I needed any request with an API key not to have a session and I simply did :
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\Middleware\StartSession as BaseStartSession;
class StartSession extends BaseStartSession
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(\Request::has('api_key'))
{
\Config::set('session.driver', 'array');
}
return parent::handle($request, $next);
}
}
Also you will need to extend the SessionServiceProvider as follows:
<?php namespace App\Providers;
use Illuminate\Session\SessionServiceProvider as BaseSessionServiceProvider;
class SessionServiceProvider extends BaseSessionServiceProvider
{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->registerSessionManager();
$this->registerSessionDriver();
$this->app->singleton('App\Http\Middleware\StartSession');
}
}
and place in your config/app.php under providers:
'App\Providers\SessionServiceProvider',
Also you must change it in your kernel file: App/Http/Kernel.php, in the $middlewareGroups section change the default entry, \Illuminate\Session\Middleware\StartSession::class, to your new class \App\Http\Middleware\StartSession::class,.
In Laravel 5, just don't use the StartSession, ShareErrorsFromSession, and VerifyCsrfToken middlewares.
In my application I've moved these three middlewares from the web group to a new stateful group, and then I have included this stateful group on routes which need to know about the session (in addition to web in all cases, in my app at least). The other routes belong to either the web or api groups.
Now when making requests to the routes which are not using the stateful middleware group session cookies are not sent back.
The simplest way to achieve this is to Make your own AppStartSession middleware that subclasses Illuminate\Session\Middleware\StartSession and the replace the class being used in kernel.php. The only method you need to override in your subclass is sessionConfigured() for which you can return false to disable the session or parent::sessionConfigured() to allow it.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\Middleware\StartSession;
class AppStartSession extends StartSession
{
protected function sessionConfigured(){
if(!\Request::has('api_key')){
return false;
}else{
return parent::sessionConfigured();
}
}
}
kernel.php (see *** comment for where the change is done)
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
// *** Replace start session class
// \Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\AppStartSession::class,
// *** Also comment these ones that depend on there always being a session.
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
//\App\Http\Middleware\VerifyCsrfToken::class,
];
/**
* The application's route middleware.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
];
}
Don't fight the framework, embrace it!
Since Laravel 5.2, when middleware groups were introduced, you may disable session for certain routes by defining them outside of the "web" middleware group (which includes the StartSession middleware responsible for session handling). As on latest 5.2.x versions the whole default routes.php file is wrapped with "web" middleware group, you need to make some modification in app/Providers/RouteServiceProvider.php file, as described here.
There appears to be a way to accomplish this using a session reject callback.
Relevant sources...
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/Application.php#L655
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Foundation/Application.php#L660
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Session/Middleware.php#L60
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Session/Middleware.php#L97
I can't find many references to this around the web, but reading more through the source it appears that if the session reject callback returns a truthy value, the session will be forced to use an array driver for the request rather than whatever is configured. Your callback also gets the current request injected so you can do some logic based on the request parameters.
I've only tested this on a local Laravel 4.2 install but it seems to work. You just need to bind a function to session.reject.
First, create a SessionRejectServiceProvider (or something like that)
<?php
use \Illuminate\Support\ServiceProvider;
class SessionRejectServiceProvider extends ServiceProvider {
public function register()
{
$me = $this;
$this->app->bind('session.reject', function($app)use($me){
return function($request)use($me){
return call_user_func_array(array($me, 'reject'), array($request));
};
});
}
// Put the guts of whatever you want to do in here, in this case I've
// disabled sessions for every request that is an Ajax request, you
// could do something else like check the path against a list and
// selectively return true if there's a match.
protected function reject($request)
{
return $request->ajax();
}
}
Then add it to your providers in your app/config/app.php
<?php
return array(
// ... other stuff
'providers' => array(
// ... existing stuff...
'SessionRejectServiceProvider',
),
);
Edit / More Info
The net result is that the reject() method is called on every request to your application, before the session is started. If your reject() method returns true, sessions will be set to the array driver and basically do nothing. You can find a lot of useful info the $request parameter to determine this, here's the API reference for the request object in 4.2.
http://laravel.com/api/4.2/Illuminate/Http/Request.html
I've been trying to accomplish a similar feature.
Our API is stateless except for 1 route - the version 1 cart.
I ended up with setting 'driver' in the app/config/session.php like this ...
'driver' => 'v1/cart' === Request::getDecodedPath() ? 'native' : 'array',
Nothing magic. Initially we though of using a before filter, but that wasn't happening early enough.
It seems a simple way to do things, but I may be missing something.
Putting the switch in the config seems an easy place for other developers to see what the driver is whereas putting it in a service provider is so tucked out of the way, without knowing what service providers are installed and what they interact with, it would be far harder to debug.
Anyway. Hope this is of some use.
As pointed out below ... DO NOT CACHE YOUR CONFIG IF IT IS DYNAMIC.
Which does lead to it being of limited use. As soon as we no longer need to support v1/cart, we will be dropping this route and then be back on a static config.
Laravel default have two routes group called web and api, the api routes group default without session.
So, we can write any route role to routes/api.php, will not use session default.
If not want to use the api prefix url, we can modify app\Providers\RouteServiceProvider add a new group like this:
Route::middleware('api')
->namespace($this->namespace)
->group(base_path('routes/static.php'));
Now you can place any routes into routes/static.php file will not to use session.
Hope helpful.
Laravel 5x
In the App\Providers\RouteServiceProvider file, just copy the mapApiRoutes() method to a new method called mapStaticRoutes(), remove the prefix('api') call, and add "routes/static.php" (you will need to create this file). This will use the same stateless "api" middleware and not have an /api prefix assigned to the routes.
protected function mapStaticRoutes()
{
Route::middleware('api')
->namespace($this->namespace)
->group(base_path('routes/static.php'));
}
Just update the "map()" method to call "$this->mapStaticRoutes();" so that it knows of your new file. And any route added there should now be stateless and it wasn't much work.....
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
// Static Routes (stateless, no /api prefix)
$this->mapStaticRoutes();
}
static.php
// Health Check / Status Route (No Auth)
Route::get('/status', function() {
return response()->json([
'app' => 'My Awesome App',
'status' => 'OK'
]);
});

Categories