I am building an application, where there would be main application users (say support users) and separate client users (the application can have many different clients and each clients can have many users), every client has its own separate database, but the codebase for the entire application would be the same for every client.
What I wanted to achieve is, before calling auth in the main application, I wanted to call a middleware, which would detect a parameter (say db_slug) from request URL and according to that param it will change the DB respectively. And then login them to the client user to their respective DB.
Note: The client users will not be a part of the main DB. Their record would be only in their Client's DB.
But I am failing to do so, as my auth middleware is called first, before my custom middleware, and on accessing auth routes, it is saying unauthenticated, since that specific client user is not a part of my main application.
Note: I am using sanctum auth.
What I tried is, created a middleware called ClientDBMiddleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use DB;
use Config;
class ClientDBMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->has('db_slug')){
$dbSlug = $request->db_slug;
DB::purge('mysql');
Config::set('database.connections.mysql.database', "db_$dbSlug");
}
return $next($request);
}
}
and applied it my api.php auth routes before auth middleware
Route::group(['middleware' => ['clientDB', 'auth:sanctum']], function () {
Route::get('/me', [UserController::class, 'me']);
});
Important Note :
If there is a better approach to achieve my required thing, then that would be highly appreciated, please help me with it.
I am trying to get the source of the url that does the request POST/GET to a route. For example:
Route::post('/do-something', somethingController#getWhoRequesting);
Then as example, on the view I do ajax or a simple form post to that route, which is from localhost:8888/my-web-view. How do I get the url of who is requesting to my /do-something endpoint? I expect on getWhoRequesting(), but I get the '/my-web-view'.
Thanks in advance
You can get full url from requestin controller using request
request()->fullUrl(); or $request->fullUrl();
Updates:
If you want to capture all request then you can create middleware and assign it to all routes
php artisan make:middleware RequestCapture
then file will be look like this
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class RequestCapture
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
//here you can capture all $request for assigned middleware routes
return $next($request);
}
}
Also register this middleware in kernal.php in protected $routeMiddleware
'requestCapture' => \App\Http\Middleware\RequestCapture::class,
then you can assign to routegroup
Route::group(['middleware' => 'requestCapture'], function () {
});
You could use path method of Laravel. The path method returns the request's path information. So, if the incoming request is targeted at http://example.com/foo/bar, the path method will return foo/bar:
$uri = $request->path();
I hope it is something you are looking for ??
I'm building a multi languages site using Laravel 5.
I knew that in Laravel, I can define prefix for route like:
http://domain/en/users/1
http://domain/en/shop/1
And in Middleware, I can get the segment of url path to detect the language and set locale of current Request.
But I can't find anyway to add lang parameter in default for route like folowings:
http://domain/users/1?lang=en
http://domain/shop/1?lang=en
Or are there anyways to hook into route function of Framework to append default parameter ?lang=jainto all route when I call ?
(ja is current locale of application which was set in middleware before )
Thanks !
You can create a middleware that sets up a default 'lang' query parameter if the request doesn't have one. It will work for all the requests to your app, so you can get the lang parameter in every route handler.
Create a middleware LangFilter in the console (while in the project directory) :
php artisan make:middleware LangFilter
Then open up ./app/Http/Kernel.php and add :
\App\Http\Middleware\LangFilter::class
to the $middleware array. Now open up the middleware you created, i.e ./app/Http/Middleware/LangFilter.php and add the checking and setting code :
<?php
namespace App\Http\Middleware;
use Closure;
class LangFilter
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Checks for lang in the parameters.
if($request->input('lang') == null) {
// Adds the default one since it doesn't have one.
$request->merge(array('lang' => 'en'));
}
return $next($request);
}
}
If you want to have this kind of filtering for just a subset of all the routes you have, you need to register the middleware differently in Kernel.php.
UPDATE
For making a helper that generates routes with current locale :
Create a folder app/Support.
Create the helpers file app/Support/helpers.php
Open up helpers.php, and add this code to add the helper :
<?php
function locale_route($name, $parameters = [], $absolute = true) {
return route($name, array_merge($parameters, [
'lang' => App::getLocale()
]), $absolute);
}
?>
Add the helpers file to composer autoload in composer.json:
"autoload" : {
"files" : [
"app/Support/helpers.php"
]
}
Now run in the console :
composer dumpautoload
Now you can call locale_route with the same parameters you give to route to create urls that has the current locale added in query params.
I hope this is what you are looking for. Generating a route with a query string parameter
I'm trying to use the Laravel auth out of the box. The authentication is not the problem but I want to check if the user has confirmed his email address.
How I let have Laravel check if the table value confirmed has the value 1.
In config/auth.php I have set 'driver' => 'database' so if I understand the docs right I can then do manual authentication and I guess I can then check if the user has confirmed his account.
Where does Laravel perform the check for a matching username and password?
If you're using Laravel Auth out of the box, you want to go take a look at the AuthController that has been setup for you.
You'll see that this uses a trait AuthenticatesAndRegistersUsers to add behavior to the controller.
Inside that trait you'll find the method postLogin.
You will want to override this method, by adding your own postLogin to the AuthController. You can copy and paste the method for starters.
Now go take a look at the Laravel docs on authentication. Scroll down to where it talks about "Authenticating A User With Conditions."
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1]))
{
// The user is active, not suspended, and exists.
}
Change the attempt() code in your postLogin method to include the condition, as shown in the example. In your case you would probably want to pass in a condition of 'confirmed' => 1 instead of active, depending on what you call the field in your users table.
That should get you going!
Create a middleware class:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class UserIsConfirmed {
/**
* Create the middleware.
*
* #param \Illuminate\Contracts\Auth\Guard $auth
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->user()->isConfirmed())
{
// User is confirmed
}
else
{
// User is not confirmed
}
return $next($request);
}
}
I don’t know what you want to do in the case a user is or is not confirmed, so I’ll leave the implementation up to you.
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'
]);
});