How to disable csrf protection for a route with parameter? - laravel

there is a route like:
Route::post('user/{id}/update','UserController#update');
I want to disable csrf protection for it, but i don't know how to add its uri into except array.

You can add the given code in VerifyCsrfToken file at App/Http/Middleware
protected $except = [
'user/*',
];
or you can disable it on route file
Route::post('user/{id}/update', [
'uses' => 'UserController#update',
'nocsrf' => true,
]);

One of the best solution in recents versions of Laravel is using the withoutMiddleware method:
Route::post('user/{id}/update', 'UserController#update')
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
This works even if you have the route inside a middleware group.
Route::middleware(['web'])->group(function () {
Route::post('user/{id}/update', 'UserController#update')
->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);
});

Related

Laravel localization and routes from Jetstream / Fortify

I have this new Laravel project to work on. We would like to make it available in multiple languages.
I started the project with JetStream. Routes for authentication and such are automatically handled by JetStream / Fortify. I then added https://github.com/mcamara/laravel-localization to handle the localization. it works fine for the routes I created myself :
Route::group(
[
'prefix' => LaravelLocalization::setLocale(),
'middleware' => [ 'localeSessionRedirect', 'localizationRedirect', 'localeViewPath' ]
], function()
{
Route::get('/', function () {
return view('welcome');
});
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
});
But how can I set the group, prefix and middleware on the routes handled by Jetstream and Fortify?
[EDIT]
So after some suggestions from #TEFO, I'm trying to add a middleware to handle setting the locale. Added :
Fortify.php :
'path' => '{lang}',
'middleware' => ['web', 'setLang']
new middleware setLang :
class SetLang {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(\Illuminate\Http\Request $request, Closure $next) {
// $lang = 'en';
// $request->attributes->add(['lang' => 'en']);
$request->route()->setParameter('lang', 'en');
// $request->request->set('lang', 'en');
return $next($request);
}
}
Added the middleware to $routeMiddleware.
I'm receiving this error when trying to reach http://mylaravel/en/login :
ErrorException
Missing required parameters for [Route: login] [URI: {lang}/login]. (View: /var/www/resources/views/auth/login.blade.php)
Finally successfully nailed this. I simply disabled routes from Fortify and Jetstream, copied them over and shoved them inside my grouped prefix routes. Still using https://github.com/mcamara/laravel-localization but it should work anyway you want it - make your own system or whatever, as long as you control the routes you're good to go.
In JetstreamServiceProvider :
public function register() {
Jetstream::ignoreRoutes();
}
In FortifyServiceProvider :
public function register() {
Fortify::ignoreRoutes();
}
And copy over routes from Fortify vendor/laravel/fortify/routes/routes.php and Jetstream vendor/laravel/jetstream/routes/livewire.php (I guess adapt to Inertia if you're working with this) over to your web.php file, inside a route group with the prefix you need.
I faced almost the same problem with the expection that i do not use mcamara/laravel-localization at the moment.
Based on the useful discussion above between #JeremyBelolo and #TEFO, the following solution worked for me:
Added 'path' => '{locale}/my-secret-path' to config/fortify.php. As #JeremyBelolo and #ETO discussed, the support for that was recenlty added.
Added my middleware before \Laravel\Jetstream\Http\Middleware\AuthenticateSession::class to the web $middlewareGroups
Where my middleware set the locale app()->setLocale($locale); and the default {locale} url parameter URL::defaults(['locale' => $locale]); before passing the request deeper into the application.
Considering Jetstream I had to apply the same steps as #JeremyBelolo did, exept I didn't copy the jetsream/livewire routes but used the following inside the route group:
require base_path('vendor/laravel/jetstream/routes/livewire.php');
Now I can access {locale}/my-secret-path/login where {locale} is a supported locale for my site.
UPDATE [Fortify config option changed]:
The path fortify config option changed to prefix. Thus in config/fortify.php the following key should be used:
'prefix' => '{locale}/my-secret-path'
I made a new Laravel Project using Jetstream. I wanted to use multi-language support in my project, but when I used Prefix (en/login, de/login) according to languages in url, I was also having a problem with Route. I solved my problem by following these steps. I hope you will be useful too:
1 - I have included the package on this https://github.com/mcamara/laravel-localization in my project. and followed the instructions sequentially.
2 - I made the Route settings in the "rautes\web.php" file as follows.
Route::group(['prefix' => LaravelLocalization::setLocale(),'middleware' => [
'localeSessionRedirect', 'localizationRedirect','localeViewPath' ]], function(){
/** ADD ALL LOCALIZED ROUTES INSIDE THIS GROUP **/
Route::get('/', function () {return view('welcome');});
Route::middleware(['auth', 'verified'])->get('/dashboard', function () {
return view('back.dashboard');})->name('dashboard');
});
3 - I have included the in app\Http\Middleware\Kernel.php. In middlewareGroups end of web prefix.
protected $middlewareGroups = [
'web' => [....
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
\Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
\Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,]
4 - Fortify Routes, include prefix in vendor\laravel\fortify\routes.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' => config('fortify.middleware', ['web'])], function () {
$enableViews = config('fortify.views', true);
.......
5 - Livewire Routes, include prefix in vendor\laravel\jetstream\routes\livewire.php - Route::group like this:
Route::group(['prefix' => LaravelLocalization::setLocale(),
'middleware' =>config('jetstream.middleware', ['web'])], function () {
if (Jetstream::hasTermsAndPrivacyPolicyFeature()) {
Route::get('/terms-of-service', [TermsOfServiceController::class, 'show'])-
>name('terms.show');
Route::get('/privacy-policy', [PrivacyPolicyController::class, 'show'])-
>name('policy.show');}
6 - If you want to separate backend and frontend, you can add in app\Http\Middleware\Kernel.php end of protected $routeMiddleware with prefix like in this https://github.com/mcamara/laravel-localization.
protected $routeMiddleware = [
........
'localize'=> \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
'localeCookieRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleCookieRedirect::class,
'localeViewPath' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationViewPath::class,
]
7 - And the happy end...

How to disable csrf protection for a route with dynamic parameter?

I have a route which has a dynamic parameter at the end of the URL. In this route, I fetch data which is sent from an external API with the post method. As 419 page expired error occurs when the external API sends post request, I need to disable csrf protection for this route.
Related route:
Route::group(['middleware' => ['auth:student']], function (){
Route::post('Result', 'ExamController#Result')->name('exam.Result');
}
My URL example:
http://localhost.dev/student/Result?Id=N7utfGkwOLebxMWGA5iUC4S23jgRzW
I tried to add this code in VerifyCsrfToken file at App\Http\Middleware:
protected $except = [
'student/Result/*',
];
It doesn't work. But when I try student/*, it works perfectly. However, disabling the csrf protection for all student path is not what I want.
I tried also this way by getting reference on this thread:
Route::post('Result', [
'uses' => 'ExamController#Result',
'nocsrf' => 'true'
])->name('exam.Result');
That didn't work either.
How can I disable csrf protection in this scenario?
You made a typo at App\Http\Middleware, instead of:
protected $except = [
'student/Result/*',
];
You need to use:
protected $except = [
'student/Result',
];
Also, based on documentation you can specify the full url that need to be excepted:
protected $except = [
'http://localhost.dev/student/Result',
];
Be aware, that you don't need to add parameters part (everything after ? sign, e.g. ?Id=N7utfGkwOLebxMWGA5iUC4S23jgRzW) of route here.
Try this one (remove the slash and asterisk):
protected $except = [
'student/Result',
];

How to use multiple method in single route in laravel

I want to use more than one method in a single route using laravel. I'm try this way but when i dd() it's show the plan string.
Route::get('/user',[
'uses' => 'AppController#user',
'as' => 'useraccess',
'roles'=> 'HomeController#useroles',
]);
When i dd() 'roles' option it's show the plan string like this.
"roles" => "HomeController#useroles"
my middleware check the role this way.
$actions=$request->route()->getAction();
$roles=isset($actions['roles'])? $actions['roles'] : null;
The simplest way to accept multiple HTTP methods in a single route is to use the match method, like so:
Route::match(['get', 'post'], '/user', [
'uses' => 'AppController#user',
'as' => 'useraccess',
'roles'=> 'HomeController#useroles',
]);
As for your middleware, to check the HTTP request type, a tidier way would be:
$method = request()->method();
And if you need to check for a specific method:
if (request()->isMethod('post')) {
// do stuff for post methods
}
Here's how you can do multiple methods on a single route:
Route::get('/route', 'RouteController#index');
Route::post('/route', 'RouteController#create');
Route::put('/route', 'RouteController#update');
/* Would be easier to use
* Route::put('/route/{route}', 'RouteController#update');
* Since Laravel gives you the Model of the primary key you've passed
* in to the route.
*/
Route::delete('/route', 'RouteController#destroy');
If you've written your own middleware, you can wrap the routes in a Route::group and apply your middleware to those routes, or individual routes respectively.
Route::middleware(['myMiddleware'])->group(function () {
Route::get('/route', 'RouteController#index');
Route::post('/route', 'RouteController#create');
Route::put('/route', 'RouteController#update');
});
Or
Route::group(['middleware' => 'myMiddleware'], function() {
Route::get('/route', 'RouteController#index');
Route::post('/route', 'RouteController#create');
Route::put('/route', 'RouteController#update');
});
Whichever is easier for you to read.
https://laravel.com/docs/5.6/routing#route-groups

Laravel sub-domain routing set global para for route() helper

I have setup sub-domain routing on my app (using Laravel 5.4) with the following web.php route:
Route::domain('{company}.myapp.localhost')->group(function () {
// Locations
Route::resource('/locations' , 'LocationController');
// Services
Route::resource('/services' , 'ServiceController');
});
However as my show and edit endpoints require an ID to be passed, using a normal route('services.show') helper results in an ErrorException stating Missing required parameters for [Route: services.create] [URI: services/create].
I appreciate this is necessary, but as the company is associated to the user on login (and is in the sub-domain) I don't want to be passing this for every view. I want to set this at a global level.
To avoid repeated queries, I thought about storing this in the session as so (in the :
protected function authenticated(Request $request, $user)
{
$current_company = $user->companies->first();
$company = [
'id' => $current_company->id,
'name' => $current_company->name,
'display_name' => $current_company->display_name
];
$request->session()->put('company', $company);
}
Which is fine, but I wonder if I can pass this to the route as a middleware or something. What's be best solution here?
Recommendation: remove the slash before the resource name.
The resource method will produce the following URIs:
/services/{service}
So, you should define your routes like this:
Route::domain('{company}.myapp.localhost')->group(function () {
// Locations
Route::resource('locations' , 'LocationController');
// Services
Route::resource('services' , 'ServiceController', ['only' => ['index', 'store']]);
Route::get('services');
});
I ran into this exact issue today, I poked around in the source and found a defaults method on the url generator that allows you to set global default route parameters like so:
app('url')->defaults(['yourGlobalRouteParameter' => $value]);
This will merge in whatever value(s) you specify into the global default parameters for the route url generator to use.

How to get route parameters in Laravel Global Middleware?

I have global middleware and i need to get parameters from the routes defined in routes.php. My $request->route() is NULL
You can't. The route has not been matched yet. Route parameters are only available in route middleware.
Think about it: it doesn't make much sense for a global middleware to have access to the route's parameters, since every route has different parameters.
You can however get the URI segments:
$id = $request->segment(2);
Pass it the number (1 based index) of the segment you want.
Another approach is to make your middleware "global" manually.
Way #1
Put it into all $middlewareGroups in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
//...
YourGlobalMiddleware::class,
],
'api' => [
//...
YourGlobalMiddleware::class,
],
];
Way #2
Wrap all your routes into a group and assign your middleware to it:
Route::group(['middleware' => 'your_global_middleware'], function () {
//all your routes
});

Resources