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

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',
];

Related

Laravel using regular expression for exclude routes

in VerifyCsrfToken class i want to use for example
protected $except = [
'/dashboard/administrator/attachImage',
'/{ANY}/product/payment/checkTransaction'
];
instead of:
protected $except = [
'/dashboard/administrator/attachImage',
'/fa/product/payment/checkTransaction',
'/en/product/payment/checkTransaction',
'/ru/product/payment/checkTransaction',
'/az/product/payment/checkTransaction',
];
how can i use that and my question is laravel cab be support using regular expression on this array?
You can't use a regular expression, but since Laravel uses request's is() method under the hood, you can use asterisk like this:
protected $except = [
'/dashboard/administrator/attachImage',
'/*/product/payment/checkTransaction',
];

How to disable csrf protection for a route with parameter?

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]);
});

Migration to Laravel, Vanity URLs, middleware for all requests, Auth::user and $errors issue

I created a middleware class named PathParser which runs on every request. The purpose is to handle requests for "vanity URL paths" that we allowed users to create in our pre-Laravel vanilla PHP app. For example: A user created a URL path such as: http://example.com/i-love-this-place
What PathParser does is to check for 404 responses, and then look to see if the URL path matched one of our old vanity paths. Like this:
class PathParser
{
public function handle($request, Closure $next, $guard = null)
{
$next_response = $next($request);
$status_code = $next_response->getStatusCode();
if ($status_code === 404) {
$script_url = $request->server("SCRIPT_URL");
$vanity_controller = new VanityController();
$found_vanity_id = Search::findVanityPath($script_url);
if (!empty($found_vanity_id)) {
$next_response = response()->make($vanity_controller->one($found_vanity_id));
}
}
return $next_response;
}
}
Assume the following:
A user has never created a URL path that would conflict with any route
I must support a number of existing (pre-Laravel) vanity URL paths that are out in the wild -- posted to social media and so on.
In Kernel.php, I have the following:
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\PathParser::class,
//\Illuminate\Session\Middleware\StartSession::class,
//\Illuminate\View\Middleware\ShareErrorsFromSession::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
In the $middleware array I tried added StartSession and ShareErrorsFromSession (uncommenting the 2 lines above), and this works partially. But with two major problems:
Auth::user is null, even for requests made to vanity paths by logged in users
$errors no longer gets populated on form submission (such as on the registration and login pages) when a user submits incorrect/invalid information
Is there a way both to check the route on all requests and get ahold of the authenticated user, also preserving $errors?
I have a feeling I don't understand the request lifecycle well enough to succeed. But maybe there is a way?
If it's not possible to do what I require, then use of 302 redirection to a standardized prefixed path (such as http://example.com/vanity/i-love-this-place) is a fine solution. But I'm hoping there is another means.
A couple suggestions:
If you don't need auth/sessions/etc, then you can just handle the Symfony\Component\HttpKernel\Exception\NotFoundHttpException exception inside your application's exception handler.
In app/Exceptions/Handler.php, modify the render() method to look something like:
public function render($request, Exception $e)
{
if ($e instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
// your code that returns a Response
}
return parent::render($request, Exception $e);
}
If you do need auth/sessions/etc, I would suggest creating a "catchall" route at the end of your routes file. For example, as the very last line in your routes/web.php file, put:
Route::any('{catchall}', 'VanityController#handle')->where('catchall', '(.*)');
Then, inside your VanityController, have a handle method that looks like:
public function handle(Request $request, $url)
{
// logic to search and render your vanity page for $url
// if no vanity page was found:
throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
}

Laravel 5 - Remove Parameter From All Request Objects at Controller Level

I have URLs that look like:
http://example.com/api/user?id=45&name=mike&api_token=2348283
http://example.com/api/project?id=5&description=first&api_token=2348283
etc...
In my controllers, I have functions that look like:
public function user_get_endpoint(Request $request){
$request = $request->toArray();
return UserModel::where($request)->get()->toArray();
}
The above will currently break since the $request object contains a property called api_token which does not exist in the user table. I am using the api_token in a middleware to check for authentication.
I can manually unset the api_token property in each of my API functions by using unset($request['api_token'], but I'd like to avoid that if possible.
Is there anyway to do this application wide or at a class or controller level?
Laravel provides add and remove functions to add and remove new properties to the request object respectively.
$request->request->add(['api_token' => 'api_token']); // to add new property to $request
$request->request->remove('api_token'); // to remove property from $request
Perhaps you want global middleware?
First arrange for the middleware to run on all routes:
// routes.php
$app->middleware([
App\Http\Middleware\Apitoken::class
]);
Then define what the middleware should do:
// src/App/Http/Middleware/Apitoken.php
<?php
namespace App\Http\Middleware;
use Closure;
class Apitoken
{
public function handle($request, Closure $next)
{
unset($request['api_token']);
return $next($request);
}
}
Method 1
$request->except(['key1','key2',....])
provides an easy way to skip unwanted keys, similarly
Method 2
$request->only(['key3','key4',....])
provides an easy way to skip all others unwanted keys, I find both reasonably good for almost all scenarios
A solution that works for all HTTP Methods (not only for GET and HEAD) :
$except = ['api_token'];
$request = request();
$cleanup = $request->except($except);
$request->query = new \Symfony\Component\HttpFoundation\ParameterBag($cleanup);
Ideally, you should send your api_token in request headers instead of Uri params.
If you are using Laravel's auth:api Middleware for authentication then you can send api_token in headers as:
$response = $client->request('GET', '/api/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
Then api_token will never come in your Uri params.
As #JanWillem said in the comments you can use except() which will remove the params you pass to it:
public function user_get_endpoint(Request $request){
return UserModel::where($request->except('api_token'))->get();
}
https://laravel.com/docs/5.2/requests#retrieving-input and then scroll down to Retrieving A Portion Of The Input Data
Furthermore, you don't have to use toArray() with the response as Laravel will automatically do this for you.
Hope this helps!

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