I'm trying to implement IPN in my Laravel project, a post request from my website is working thanks to "csrf",
i try to implement it like written here:
https://developer.paypal.com/docs/api-basics/notifications/ipn/ht-ipn/
in my routes/web.php:
Route::post('i', [IController::class, 'y'])->name('i');
and in IController whats written on the side:
public function y()
{
error_log('function y called');
// STEP 1: read POST data
// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
// Instead, read raw POST data from the input stream.
$raw_post_data = file_get_contents('php://input');............
The error_log doesn't show up, means the function is never entered.
Question: How can i force Laravel to except POSTS From Paypal?
You can Exclude URIs From CSRF Protection in laravel.
Goto App\Http\Middleware\VerifyCsrfToken and add url which you want to exclude csrf token.
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'i',
];
}
As document says
Sometimes you may wish to exclude a set of URIs from CSRF protection.
For example, if you are using Stripe to process payments and are
utilizing their webhook system, you will need to exclude your Stripe
webhook handler route from CSRF protection since Stripe will not know
what CSRF token to send to your routes.
Typically, you should place these kinds of routes outside of the web
middleware group that the App\Providers\RouteServiceProvider applies
to all routes in the routes/web.php file. However, you may also
exclude the routes by adding their URIs to the $except property of the
VerifyCsrfToken middleware:
Ref:https://laravel.com/docs/8.x/csrf#csrf-excluding-uris
I am using subdomain routing heavily within my project, as it's a multi-tenant application with each tenant having their own subdomain.
As a result, all of my routes are wrapped in:
Route::domain('{tenant}.'.config('app.base_url'))->group(function () {
// My routes here!
});
To use the route() helper within my code, I need to pass it all of the route parameters associated with it. Every single route has tenant associated with it, so I constantly found myself repeating code and writing route('my-route-name', ['tenant' => $request->route('tenant')]);
I've created a middleware with the following code in it:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use URL;
class SetTenantFromRequest
{
public function handle(Request $request, Closure $next)
{
list($subdomain) = explode('.', $request->getHost(), 2);
URL::defaults(['tenant' => $subdomain]);
return $next($request);
}
}
And placed it in the HTTP Kernel.php file like so:
protected $middleware = [
TrustProxies::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
WebHeaders::class,
SetLanguage::class,
SetTenantFromRequest::class,
];
// Removed for brevity...
protected $middlewarePriority = [
StartSession::class,
ShareErrorsFromSession::class,
SetTenantFromRequest::class,
Authenticate::class,
ThrottleRequests::class,
AuthenticateSession::class,
SubstituteBindings::class,
Authorize::class,
];
I had to add it to the $middlewarePriority array as it needs to run before the Authenticate middleware. Since Authenticate calls return route('login');, I need that URL parameter available beforehand.
Now with doing all of this, I still get the following error thrown from the Authenticate middleware: Missing required parameters for [Route: login] [URI: login].
If I run ddd(URL::getDefaultParameters()); within the Authenticate middleware, it prints an empty array. However if I run the same ddd(...) within the SetTenantFromRequest middleware, it shows the tenant in there as I expect it to be.
Does anyone have an idea on how I can solve this problem?
Moving this from the global middleware stack to the web middleware stack alleviates the issue.
This forces me to add the tenant parameter to the route(...) call within my Authenticate middleware, but it does allow me to forgo it everywhere else.
I would like to have a Slim middleware to check authentication on all requests but some specific ones (for example login page).
I prepared the first AuthenticationMiddleware middleware to check all pages:
$app->add(new \App\Middleware\AuthenticationMiddleware($container));
Then I create another one AnonymousMiddleware that it is supposed to add a variable to set the exception to authentication checks:
$app->group('',function() use ($app){
$app->get('/','LogicController:index');
})->add(new AnonymousMiddleware($container));
The problem is that routes middleware (AnonymousMiddleware) is applied AFTER the general middleware (AuthenticationMiddleware);
I tried to use determineRouteBeforeAppMiddleware but it doesn't change the result.
I know I would set a route middleware for all authenticated routes but sounds a bit dangerous if I forget it, so, I would prefer to set which router are not under authentication then keep the check all other routes.
you need to chose different approach
you need to wrap all routes which should be "protected via Auth middleware" and exclude = not wrap routes which should not be handled by this middleware
you can add a group where all your routes will live and only login (and logout) route will be outside of this group ;)
something like
$app->group("/api/v1", function() {
// all your protected routes definitions here
})
->add(AuthenticationMiddleware::class)); // wrap by middleware
$app->post('/login', function(){});
$app->add(new MiddlewareForAllRoutes()); // middlewares for all routes
I am using Laravel 5.1 for my project. I am trying to secure Routes and make sure only logged in user can access certain routes. I am aware about middlewares but I am wondering if anyone post an example or a link explaining about middleware and how to protect a page using middleware.
Thanks
To build on the answer given by Joe Rose, you can also specify the middleware in your controller rather than in your routes.php file.
E.g you could have your routes set out like
Route::get('/example', 'ExampleController#index');
Route::post('/example/post', 'ExampleController#post');
Route::resource('blog', 'BlogController');
And then inside your controller reference it like so:
class ExampleController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
//....
If you're looking for more info, check out the link to the docs Joe gave, and also this blog post which explains what middleware is really well and how to create your own if you need to.
You are correct about using middleware. The included Auth middleware is what you should use, as long as you are also using the included Auth controller to authenticate users. You would write your route like this:
Route::get('/page', array(
'uses' => 'Controller#method',
'middleware'=>'auth'
));
(The above example is using a GET request, but it could other request types, like POST for example).
This will use the default behavior of the middleware which checks to see if the user is logged in (authenticated). You can also extend or overwrite the built-in functions to allow you to direct the application on where to send the user if they are or are not logged in, etc. Laravel's official documentation is a good starting point: link
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'
]);
});