Too many redirect on laravel middleware - laravel

I have created a custom middleware and i am checking if the password field is null and redirect user to change the password but it give me redirection error, any one can help? Let me add more details i want user to redirect to /change-password if the password field is empty
so here's the whole process.
user verify the email, redirect to /change-password route instead of dashboard if password field in the database is empty other wise we redirect them to dashboard. Users shouldn't access any route until they didn't update the password.
Remember i am using laravel breeze for auth
Middleware code:
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ChangePasswordMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$user = Auth::user();
if (empty($user->password)){
return redirect()->route('patient.password');
} else{
return redirect()->intended(RouteServiceProvider::HOME);
}
return $next($request);
}
}
My Routes:
Route::middleware(['auth', 'verified', 'changepassword'])->group(function (){
Route::get('/change-password', [PatientsController::class, 'passwordView'])->name('patient.password');
Route::get('/dashboard', [PatientsController::class, 'index'])->name('patient.dashboard');
Route::get('pricing', [PatientsController::class, 'pricing'])->name('patient.pricing');
});
changepassword is registered in my kernel.php and it's a custom middleware.
i have tried to create a different group for routes but it still doesn't work, i want changepassword middleware to force use to change the password and other routes shouldn't work until the password field is updated

As mentioned in the comments, the middleware is being called over and over because the password is empty. Hence, the issue of too many redirects. Your routes must ignore the route for /change-password.
Route::middleware(['auth', 'verified', 'changepassword'])->group(function (){
Route::get('/change-password', [PatientsController::class, 'passwordView'])
->name('patient.password')
->withoutMiddleware([\App\Http\Middleware\ChangePasswordMiddleware::class]);
...
...
});
After this your too many redirects problem should go away.
Also, make sure your if/else logic is correct in the handle() method. The else logic looks odd to me.

Try
public function handle(Request $request, Closure $next)
{
$user = Auth::user();
if (empty($user->password)){
abort(302, 'Please change your password', ['Location' => route('patient.password')]);
}
return $next($request);
}
If the password is empty, it automatically redirects to /change-password route.

Related

laravel9 middleware not loaded when frontend and admin are seperated

I am doing the separation of frontend and admin. After several days of studying, It seems ok now. My github: https://github.com/ronrun/laravel-multiauth
Currently there are 4 versions.
There are 3 branches, the branch 9.x_two-tables has two version.
commit: frontend and admin login can login separately
The structure:
app\Http\Controller\Admin\Auth\LoginController.php
app\Http\Controller\Admin\DashboardController.php
app\Http\Controller\Auth\LoginController.php
app\Http\Controller\HomeController.php
commit: more seperation!
app\Admin\Console
app\Admin\Exceptions
app\Admin\Http\Controllers
app\Admin\Http\Middleware
app\Admin\Providers
app\Frontend\Console
app\Frontend\Exceptions
app\Frontend\Http\Controllers
app\Frontend\Http\Middleware
app\Frontend\Providers
app\Models
In short, it's
app\Admin
app\Frontend
app\Models
During the studying process, lots of questions I want to ask. Now all seems working fine. But I still have questions about the final commit: more seperation!
Question: Many files inside app\Frontend not loaded, only app\Admin's files are used.
The two files:
app\Admin\Http\Middleware\Authenticate.php
app\Frontend\Http\Middleware\Authenticate.php
I expected that when I am not logged in, but visite a page should login, the Authenticate's redirectTo() should do the redirect work. But only Admin's Authenticate.php is loaded. I clear the content of Frontend's Authenticate.php, make it compeletly empty, NO ERRORS. But If I clear the content of Admin's Authenticate.php, it says this class cannot found.
I found the RedirectIfAuthenticated.php is the same. Then I delete frontend's middleware folder, still working fine, no errors.
Then I delete app\Frontend\Console, Exceptions, no errors. The login functions of Frontend and Admin both working. But app\Frontend\Providers cannot be deleted, or error shows.
bootstrap\app.php
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Frontend\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Frontend\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Frontend\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Admin\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Admin\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Admin\Exceptions\Handler::class
);
Why does it only loads files inside app\Admin ?
Why no errors, login logout functions still work, when many Frontend files are deleted?
Any suggestions to improve the codes, I'd appreciate it.
To keep the frontend and admin separate, basically need:
separate routes file (say web.php, api.php & admin.php)
separate redirectTo routes in RedirectIfAuthenticated
separate redirectTo routes in Authenticate
To achieve that keep all default middleware & providers under app folder -
|-app
|-Http
|-Middleware
-Authenticate.php
-EncryptCookies.php
-PreventRequestsDuringMaintenance.php
-RedirectIfAuthenticated.php
-TrimStrings.php
-TrustHosts.php
-TrustProxies.php
-VerifyCsrfToken.php
|-Providers
-AppServiceProvider.php
-AuthServiceProvider.php
-BroadcastServiceProvider.php
-EventServiceProvider.php
-RouteServiceProvider.php
|-Frontend
|-Http
|-Controllers
|-FrontendControllers
|-Middleware
|-FrontendSpecificMiddleware (if any)
|-Requests
|-FrontendFormRequests (if any)
|-Languages
|-Other
|-Admin
|-Http
|-Controllers
|-AdminControllers
|-Middleware
|-AdminSpecificMiddleware (if any)
|-Requests
|-AdminFormRequests (if any)
|-Languages
|-Other
|-routes
|-web.php
To load routes, in App\Providers\RouteServiceProvider
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
Route::middleware('web')
->prefix('admin')
->as('admin.')
->group(app_path('Admin/routes/web.php'));
});
To set the redirect route for successful authentication
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #param string|null ...$guards
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
$prefix = $request->route()->getPrefix();
return $prefix == '/admin' ? redirect(route('admin.home') : redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
To set redirect route for unsuccessful authentication
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
$prefix = $request->route()->getPrefix();
return $prefix == '/admin' ? route('admin.login') : route('login);
}
}
}
You can also have App\Frontend\FrontendServiceProvider and App\Admin\AdminServiceProvider if you want and register them under providers array in config/app.php
To summarise, take cue from Domain Driven Design (DDD) approach. You can read about DDD at:
Stitcher - Laravel Beyond Crud - Domain Oriented Laravel
Conciliating Laravel and DDD

Auth and user management in laravel rest api

I'm writing a rest API for a mobile app. I don't know how to auth users and admins in my app.
I have a table named "users" and have a field called "isAdmin" that is 0 or 1.
now when admin sends posts, users can see posts.how do you recommend auth for both of these?
thank you
I recommend you read the documentation about authentication on laravel: https://laravel.com/docs/5.5/authentication
What you have to setup is the following:
Middleware (what routes can the user use and what routes can the admin use)
Edit your model with an isAdmin() function to determine if an user is user or admin
Example of a AdminMiddleware file - create by command line: php artisan make:middleware AdminMiddleware
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::check() && Auth::user()->isAdmin()){
return $next($request);
}
else{
return view('your_view')->withErrors('You are not logged in');
}
}
}
Example of an User Model isAdmin function - create by command line: php artisan make:model User
public function isAdmin(){
if($this->isAdmin == 1){
return true;
} else {
return false;
}
}
Example of your route file
// #TODO: Set routes for user and admin here...
Route::group(['middleware' => ['admin']], function () {
// #TODO: Set admin routes here, only admin can use this routes.
});
You also have to edit your Kernel.php a bit:
protected $routeMiddleware = [
// ... add this line
'admin' => \App\Http\Middleware\AdminMiddleware::class,
];

What is use of middleware in Laravel?

I'm not clear with the concept of middleware in Laravel. What does laravel middleware do? Please provide an example if possible.
Middleware is something that is placed between two requests.
Suppose that you need to make sure that when user access to a specific group of routes he/she is authenticated.
There are two option:
Add in every controller the code to check if user is logged in ( in this example we do not consider a parent controller )
Use a middleware
In the first case you should write in each controller the same code.
With the middleware you have a piece of code that you can re-use in multiple section of your application.
Let's suppose that we want to create a Middleware that need to check if the user is logged in:
namespace App\Http\Middleware;
use Closure;
class UserIsLoggedIn
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!auth()->user()) {
return redirect('home');
}
return $next($request);
}
}
Now with this code we can check our user where we need.
First of all since this is a custom middleware you need to register it in the app/Http/Kernel.php file in the $routeMiddleware property:
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
// ...
'isLoggedIn => \App\Http\Middleware\UserIsLoggedIn::class,
];
Let's assume that you have a group of routes that need to check the user is logged in:
Route::get('admin/profile', function () {
//
})->middleware('isLoggedIn');
Now all the routes in this group will check if the user is logged otherwise he will be redirect to home.
Now assume that you have another controller that need to make sure that the user is logged in, now you can re-use the middleware to do that:
class MyController extend Controller {
function __construct(){
$this->middleware('isLoggedIn');
}
}
So middleware help you to organize the login and re-use pieces of code for specific tasks.
Laravel has a lot of documentation about middleware that you can find here

How to get Larvel5.3 session data or Authentication data from public directory?

I wrote some php function in public directory because I have to use external library.
Then I can't retrieve any session data and authentication data from the controller I have tested with below php script
session_start();
var_dump($_SESSION['user']);
I have initial Session data from AdminMiddlware already
It awesome for using it in Resource->view directories but can not in public.
namespace App\Http\Middleware;
use App\User;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminMiddleware
{
/**
* Handle an incoming request. User must be logged in to do admin check
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = User::find(\Auth::user()->id);
if ((int) $user->is_admin == (int) config('auth.guards.is_admin')) {
$collection = collect(Auth::user());
$thisdata = $collection->toArray();
$request->session()->put('user', $thisdata);
return $next($request);
}
return redirect()->guest('/');
}}
Ok, the simplest way that I can see to get this to work with Laravel is:
(If you haven't done so already) Copy
public/gallery/scripts/filemanager.config.default.json
to
public/gallery/scripts/filemanager.config.json
Then set "fileConnector"(line 25) to "/authenticate-filemanager", e.g
"fileConnector": "/authenticate-filemanager",
This will tell your Filemanager application to load through the route /authenticate-filemanager.
Next, go to public/gallery/connectors/php/application/FmApplication.php and at the bottom change if(!auth()) to if(!auth()->check()) this will tell the application to use the built-in auth in Laravel.
Then you will need to set up the actual route (this is essentially the contents of filemanager.php without the auth() function):
Route::match(['GET', 'POST'], 'authenticate-filemanager', function () {
require_once(public_path('gallery/connectors/php/application/Fm.php'));
require_once(public_path('gallery/connectors/php/application/FmHelper.php'));
$config = [];
$fm = Fm::app()->getInstance($config);
$fm->handleRequest();
});
Because both GET and POST calls are made to the same endpoint match is used. Make sure you don't put this route behind the auth middleware.
Lastly, you just need to to go to app/Http/Middleware/VerifyCsrfToken.php and add 'authenticate-filemanager' to the $except array to disable csrf for the route.
Hope this helps!
Update for RichFilemanger ver. 2.7.6 and Laravel 5.6
I use RichFilemanager in HTML text editor in admin panel. So check for admin user is logged in.
in public/../RichFilemanager/config/filemanager.config.json
"connectorUrl": "/admin/authenticate-filemanager",
in route/web.php
Route::match(['GET', 'POST'], '/admin/authenticate-filemanager', function () {
//Here check is admin or user is authenticated. Can use: auth()->check()
$isAuth = \App\Libraries\Admin\AdminBLL::isAuth();
if(!$isAuth){
return 'Not authenticated';
}
return require_once(public_path('assets/plugins/RichFilemanager/connectors/php/filemanager.php'));
});
As was wrote before by Ross Wilson: Lastly, you just need to to go to app/Http/Middleware/VerifyCsrfToken.php and add 'admin/authenticate-filemanager' to the $except array to disable csrf for the route.
Last one - setup files folder location in public/../RichFilemanager/connectors/php/filemanager.php
$local->setRoot('userfiles/filemanager', true, true);

Sharing same route with unauthenticated and authenticated users in Laravel 5.3

I have a route that where I'm using an auth middleware and it works great.
Route::group(['prefix' => 'v1','middleware' => ['auth:api']], function()
{
Route::resource('user', 'v1\MyController');
});
The problem is that I would also like this route to be accessible to non-authenticated users as well. With the above, I get a 401 Unauthorized error and I can't return any content for unauthenticated users. So how can I authenticate this route (so it passes down the user data) while also allowing the route to proceed even if the user is NOT authenticated?
(I tried doing a conditional Auth check on the router page but it seems the user has gone through authentication yet so it always remains false.)
EDIT: I should also note that I'm using an API route with Password Grant & access tokens.
remove this route from current route group (which applies auth middleware).
then
public function __construct()
{
if (array_key_exists('HTTP_AUTHORIZATION', $_SERVER)) {
$this->middleware('auth:api');
}
}
then
if (Auth::check()) {
// Auth users
} else{
//Guest users
}
I am experiencing the same case.
since the auth middleware only checks for authenticated user, we can use client credentials for the non-authenticated user.
the client credentials have a separated middleware located in Laravel\Passport\Http\Middleware\CheckClientCredentails.
I have created a custom middleware to combine both middleware to allow either one is pass.
here is my custom middleware
namespace Laravel\Passport\Http\Middleware;
use Closure;
use League\OAuth2\Server\ResourceServer;
use Illuminate\Auth\AuthenticationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
class CheckClientCredentials
{
/**
* The Resource Server instance.
*
* #var ResourceServer
*/
private $server;
/**
* Create a new middleware instance.
*
* #param ResourceServer $server
* #return void
*/
public function __construct(ResourceServer $server)
{
$this->server = $server;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$scopes)
{
$psr = (new DiactorosFactory)->createRequest($request);
try{
$psr = $this->server->validateAuthenticatedRequest($psr);
} catch (OAuthServerException $e) {
throw new AuthenticationException;
}
foreach ($scopes as $scope) {
if (!in_array($scope,$psr->getAttribute('oauth_scopes'))) {
throw new AuthenticationException;
}
}
return $next($request);
}
}
Kernal.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.api' => \App\Http\Middleware\APIAuthenticate::class,
....
routes\api.php
Route::group([
'namespace' => 'API',
'middleware' => 'auth.api:api',
], function(){
....
From within an unauthenticated (not assigned the auth:api middleware) route's handler method, try:
Auth::guard("api")->user();
If it's populated, then your unguarded route can treat the access as authenticated. If not, its a random user accessing the route, and can be treated as such.
Dont put those urls whome you want to allow for both guest users and authenticated users in auth middleware. Because auth middleware allow for only authenticated users.
To check for authenticated and unauthenticated user you can use following code in view
#if (Auth::guest())
//For guest users
#else
//for authenticated users
#endif
Edited : In controller use
if (Auth::check()) {
// Auth users
} else{
//Guest users
}
#Yves 's answer is nearly correct. But a small change,
Instead of array_key_exists, we need to check whether key value is not null. Because It always has that key but null value. SO, instead of controller construct check for authorization header like this.
if ($_SERVER['HTTP_AUTHORIZATION']) {
$this->middleware('auth:sanctum');
}
Then you can check for authenticated user like this:
if (auth()->check()) {
// User is logged in and you can access using
// auth()->user()
} else {
// Unauthenticated
}
for sanctum use:
if (Auth::guard('sanctum')->check()) {
// user is logged in
/** #var User $user */ $user = Auth::guard('sanctum')->user();
} else {
// user is not logged in (no auth or invalid token)
}
You can use this example:
#if(Auth::user())
echo "authincatesd user";
#else
echo "unauthorised";
#endif

Resources