I would like to redirect users back to the page they were viewing before the login page after successful login in Laravel 5. Currently if the user writes a wrong username/password he is redirected to the login page again, with error displayed, and I would like to keep it that way. Also I want this to work when user clicks on login button on a page and also in the situation when user is redirected to login page automatically.
Seems to me that
protected $redirectTo = "/";
is the key thing here, but I don't know how to get it to "redirect to last page which isn't the login page". Any help is appreciated.
$url = URL::previous() != url('login') ? URL::previous() : null;
$previousUrl = old('previousUrl', $url);
Pass $previousUrl to your login view.
In your login view put previousUrl field: <input type="hidden" name="previousUrl" value="{{$previousUrl}}"/>
In your AuthController.php update constructor like that:
public function __construct()
{
$this->redirectTo = app('request')->input('previousUrl') ? : $this->redirectTo;
<...>
}
Not tested, but it should work.
use URL::previous() in your redirect or return redirect()->back(); once you know the login was succesful.
Also, you can save the url in the session and use it later at any time.
Laravel allows you to override the redirectPath() method in the AuthController which normally will use the redirectTo property, but you can set it to something dynamic.
public function redirectPath()
{
return request('redirect');
}
And in the login blade template place a hidden input:
<input type="hidden" name="redirect" value="{{ url()->previous() }}">
Try:
redirect()->intended();
Laravel 5.2
You must store the previous URL when the user enters your login page:
https://laravel.com/docs/5.2/helpers#method-url
Session is an option: https://laravel.com/docs/5.2/helpers#method-session
After that you should fill $redirectTo with that information when user tries to login: https://laravel.com/docs/5.2/authentication#included-authenticating
I wrote an example using a Middleware.
First you need to create one:
php artisan make:middleware BeforeLoginForm
Now we need to override the default route for Auth\AuthController#showLoginForm, so we can add our middleware to that route:
app/Http/routes.php
Route::auth();
$this->get('login', 'Auth\AuthController#showLoginForm')
->middleware('before.loginform');
Define an alias for our Middleware:
app/Http/Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'before.loginform' => \App\Http\Middleware\BeforeLoginForm::class,
];
Our middleware will store the previous URL using the session helper:
app/Http/Middleware/BeforeLoginForm.php
namespace App\Http\Middleware;
use Closure;
class BeforeLoginForm
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
session()->put('before_login_url', url()->previous());
return $next($request);
}
}
The constructor of our AuthController will change the redirectTo property with our previously stored URL:
app/Http/Controllers/Auth/AuthController.php
<?php
namespace App\Http\Controllers\Auth;
class AuthController extends Controller
{
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$redirectTo = session()->get('before_login_url');
if (!empty($redirectTo)) {
$this->redirectTo = $redirectTo;
}
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
For Laravel 5.0 (only what is different)
You should register your route like this:
app/Http/routes.php
Route::get('/', 'WelcomeController#index');
Route::get('home', 'HomeController#index');
Route::controllers([
'auth' => 'Auth\AuthController',
'password' => 'Auth\PasswordController',
]);
Route::get('auth/login', [
'middleware' => 'before.loginform',
'uses' => 'Auth\AuthController#getLogin'
]);
Then modify redirectTo on your AuthController:
app/Http/Controllers/Auth/AuthController.php
public function __construct(Guard $auth, Registrar $registrar)
{
$this->auth = $auth;
$this->registrar = $registrar;
$redirectTo = session()->get('before_login_url');
if (!empty($redirectTo)) {
$this->redirectTo = $redirectTo;
}
$this->middleware('guest', ['except' => 'getLogout']);
}
I could not test that, will do it later.
Pay attention because the value of the property "redirectTo" will influence not only the authentication but also the registration process.
Related
I'm using Laravel 5.4 and its built in Auth API. I've added a column to the users table called is_admin. I want to modify the login process so that login only works if is_admin == 1. I've looked in Illuminate\Foundation\Auth\AuthenticatesUsers.php (Github) and I could probably make a change there but I'd rather not disturb the core if possible. Is there a more elegant way to do this?
I solved this with a solution similar to #Sagar Arora's. In the app\Http\Controllers\Auth\LoginController.php that is created when you run artisan make:auth, I overrode the attemptLogin method from AuthenticatesUsers. This is the code in LoginController.php:
protected function attemptLogin(Request $request)
{
return (auth()->attempt(['email' => $request->email, 'password' => $request->password, 'is_admin' => 1]));
}
Now only users with is_admin == 1 will be logged in.
Just like you mentioned, you should never touch the core file. But if you could see laravel has added the AuthenticatesUsers as trait on LoginController which means you can simply override it by adding the credentials() in LoginController like the following.
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'is_admin' => 1
];
}
This solution may not be elegant, but still working.
Add this to LoginController:
protected function authenticated(Request $request, $user)
{
if ( ! $user->is_admin ) {
Auth::logout();
return redirect('login')->withErrors(['is_admin' => 'Only admin users may log in']);
}
}
Then put this in auth/login.blade.php:
#if($errors->has('is_admin'))
<div class="alert alert-danger" role="alert">{{ $errors->first('is_admin') }}</div>
#endif
Also keep in mind, that you should disable register functionality, because after registering a user still logs in.
You can create your login function as:
So in your login function you can check this by:
if (Auth::attempt(['email' => $email, 'password' => $password, 'is_admin' => 1])) {
// The user is active, not suspended, and exists.
}
You check here manual login process with additional fields:
https://laravel.com/docs/5.4/authentication#authenticating-users
Thanks
I think a better way is to actually allow all users to login. And then limit access via admin middleware. This way you may apply this middleware to as many routes as you like.
Use it by wrapping necessary routes in
Route::group(['middleware' => 'admin'], function() {
// auth protected routes
});
This way you create app\Http\Middleware\RedirectIfNotAdmin:
<?php
namespace App\Http\Middleware;
use Closure;
class RedirectIfNotAdmin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = $request->user();
if ( $user && $user->is_admin ) {
return $next($request);
}
return redirect('/');
}
}
Then in app\Http\Kernel.php you append it to $routeMiddleware property:
protected $routeMiddleware = [
// ... other middlewares
'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class
];
Hello in my project I am using the auto generated password from the admin side.And when the user try to login I am checking that user changed the password or not if password is not changes I want to redirect the user at the changepassword screen. I set changepasword middleware for it but middleware do not call the changepassword redirection Link.
changepasword middleware
use Closure;
use Auth;
class ChangePassword
{
/**
* 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()->isAutoPasswordChanged() )
{
return redirect('/change_password');
}
else
{
return redirect('/tests');
}
}
}
web.php
Route::group(['middleware' => 'auth', 'changepassword'], function () {
Route::resource('/tests', 'TestController');
Route::resource('/clients', 'ClientController');
});
Go to app\Http\Kernel.php
and add this to $routeMiddleware
'change_password' => \App\Http\Middleware\ChangePassword::class,
Then in your routes, replace the middle ware line with
Route::group(['middleware' => ['auth', 'changepassword']], function () {
And I also believe the logic written in ChangePassword is wrong...
It should be
if (!auth()->user()->isAutoPasswordChanged()) {
return redirect(route('auth.change.password.get'));
}
return $next($request);
First, please use the route() function instead of simple string... You will not have to change the url here, if you ever change the route from your web.php
Since you are already using the auth middleware, there is no need for you to do auth()->check().
Secondly, there should be a NOT in the condition. Because if the AutoPassword is NOT changed, only then redirect to the route, otherwise the use should be returned the next request and NOT redirected to /tests
Check registration of middleware app/Http/Kernel.php Doc
If you have a route middleware, compare the name provided in app/Http/Kernel.php
but my guess is:
'middleware' => 'auth', 'changepassword' should maybe changed in 'middleware' => ['auth', 'changepassword']
Debug if middleware itself is called
I have a roles administrator, moderator and member in my laravel application. Application have fronted and backend sections. I want to allow access to backend section only for administrator and moderator. I create SuperUsersMiddleware:
<?php
namespace CMS\Http\Middleware;
use Closure;
class SuperUsersMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (! $request->user()->hasRole('administrator') || ! $request->user()->hasRole('moderator')) {
return redirect('/');
}
return $next($request);
}
}
Register in Kernel.php:
......
protected $routeMiddleware = [
'superusers' => \CMS\Http\Middleware\SuperUsersMiddleware::class,
'administrator' => \CMS\Http\Middleware\AdminMiddleware::class,
'moderator' => \CMS\Http\Middleware\ModeratorMiddleware::class,
'auth' => \CMS\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
'guest' => \CMS\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
.....
and in my backend folder I create Controller.php (all other controllers in backend section extends this controller) and in __construct() function set middleware:
...
public function __construct()
{
$this->middleware('superusers');
}
...
But this doesn't work for me. I also create administrator and moderator middleware and it works separately but I needed both - together. How to do that? I tray:
public function __construct()
{
$this->middleware('administrator');
$this->middleware('moderator');
}
But this also can't work. What is a best practice for this situation?
First off I wouldn't apply any Middleware on your master Controller as then middleware would be applied to everything. You should do this on each individual controller like UserController.
You can apply as many middleware instances to a route/function as you want. I'm not aware of any limitations on that. So I'm not sure why you say this doesn't work:
public function __construct()
{
$this->middleware('administrator');
$this->middleware('moderator');
}
You can apply the different middleware to the routes that require the different levels. You can do this in your routes.php or in your Controllers. If you want to do it in your Controller like you are doing above you would have something like this:
public function __construct()
{
$this->middleware('auth'); //this applies to all actions
$this->middleware('administrator', ['only' => ['adminFunction', 'otherAdminFunction','bothCanAccess']]);
$this->middleware('moderator',['only' => ['moderatorFunction','bothCanAccess']);
}
public function adminfunction()
{
...
}
public function otherAdminfunction()
{
...
}
public function moderatorFunction()
{
...
}
public function bothCanAccess()
{
...
}
So first off the auth middleware will apply to all actions. This means a user has to be logged in to access any function in here. Then you can apply specific middleware to each function. If you need more info on this check out the documentation:
https://laravel.com/docs/5.2/controllers#controller-middleware
To do this in your router you would do something like this:
Route::get('/admin', ['middleware' => ['auth', 'administrator'],'uses'=>'Controller#adminFunction']);
So in this case it will apply the auth middleware first to make sure someone is logged in, then fire the administrator middleware and make sure the user is an admin.
Hopefully that helps.
I am using Laravel 5.
I wanted to redirect a user after a successful registration. I have tried these in my authcontroller but it doesn't work.
protected $loginPath = '/plan';
protected $redirectTo = '/plan';
protected $redirectAfterRegister = 'plan/';
These work on successful login though but not after registration.
I have also tried postRegister to render a view but using a postRegister method overrides the registration process and I don't want to do that. I just want to redirect the user to a page on successful registration.
There are two options to specify where to redirect the user in the
app/Http/Controllers/Auth/RegisterController.php
For a simple URL you can override this property.
protected $redirectTo = '/home';
If you have a more complicated logic than just one static URL, since Laravel 5.3 you can add a method in the same class RegisterController, with name redirectTo():
protected function redirectTo()
{
if (auth()->user()->role_id == 1) {
return '/admin';
}
return '/home';
}
The method behavior will override $redirectTo property value, even if the value is present.
Overriding the postRegister function like you mention should work, you would do this in your AuthController:
public function postRegister(Request $request)
{
$validator = $this->registrar->validator($request->all());
if ($validator->fails())
{
$this->throwValidationException(
$request, $validator
);
}
$this->auth->login($this->registrar->create($request->all()));
// Now you can redirect!
return redirect('/plan');
}
Or something like it. Copy it from the AuthenticatesAndRegistersUsers that is used in the top of your AuthController, here you will find all the functions
For this to work your AuthController should use the 'AuthenticatesAndRegistersUsers' trait, which is there by default.
More info about redirects in case you want to redirect to a named route: http://laravel.com/docs/5.0/responses#redirects
Simply add below line to AuthController class in Auth/AuthController.php
protected $redirectPath= '/plan';
Above redirect path will be used for successful login and successful register.
You can also modify the return of register() in RegisterUsers.php:
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
Auth::guard($this->getGuard())->login($this->create($request->all()));
// Originally the parameter is $this->redirectPath()
return redirect()->to('/plans');
}
Here is laravel 5.4 solution
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
//User register now here you can run any code you want
if (\Request::ajax()){
return response()->json([
'auth' => auth()->check(),
'intended' => $this->redirectPath(),
]);
exit();
}
return redirect($this->redirectPath());
}
keep in mind register() Handle a registration request for the application.
where registered() called when user created.
after run command
php artisan make:auth
laravel create auth controller and resource files for change the route of register user
just go to below path
App\Http\Controllers\Auth\RegisterController
and change the protected $redirectTo param
and you can user this way in LoginController next of RegisterController
for redirect after login
I follow this tutorial : https://www.youtube.com/watch?v=kmJYVhG6UzM Currently I can check in my blade if user is a admin or not like this:
{{ Auth::user()->roles->toArray()[0]['role'] }}
HI ADMIN
#endif
How can I make my route only available for admin user?
You need to create a middleware for your route.
Use: php artisan make:middleware AdminMiddleware.
You will find in your middleware folder a new file with this name.
Put your logic in your middleware, e.g.
public function handle($request, Closure $next)
{
if(Auth::check())
{
return $next($request);
}
else
{
return view('auth.login')->withErrors('You are not logged in');
}
}
Once you have done your logic in your middleware, you can either call it in the route or make the middleware apply to all routes.
If you want to add it to all routes, go to Kernel.php and add it to the $middleware array, e.g.
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\AdminMiddleware',
];
If you want to add it to specific routes only, add it to the $routeMiddleware variable and add the alias to the route. E.g.
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'admin' => 'App\Http\Middleware\AdminMiddleware',
];
You can then add it to a route, as a filter, e.g.
Route::get('admin/profile', ['middleware' => 'admin', function()
{
}]);
For additional info visit the docs:
http://laravel.com/docs/master/middleware
EDIT
An improvement on this would be to use variadic functions which was introduced in PHP 5.6
http://php.net/manual/en/migration56.new-features.php
Instead of having to make a middleware for each permission set you can do the following
PermissionMiddleware
namespace App\Http\Middleware;
use Closure;
use \App\Models\Role;
class PermissionMiddleware
{
// Pass parameters to this middleware
public function handle($request, Closure $next, ...$permitted_roles)
{
//Get a users role
$role = new Role;
$role_name = $role->getUserRoleByName();
foreach($permitted_roles as $permitted_role) {
if($permitted_role == $role_name) {
return $next($request);
}
}
return redirect()->back()->withErrors('You do not have the required permission');
}
}
Notice the ...$permitted_roles
Route::get('admin/profile', ['middleware' => 'PermissionMiddleware:Admin,Marketing', function()
{
}]);
You can now specify as many roles as required for one middleware rather than creating multiple by using middleware parameters
Docs
https://laravel.com/docs/5.3/middleware#middleware-parameters
Let's assume you have a column in your users table with isAdmin name which has a default value of 0 (false)
You can give special access using middleware in laravel like you give access to logged in users using auth middleware in laravel.
Now you need to create a middleware using the command :
php artisan make:middleware AdminMiddleware
In your Kernel.php you need to add this line to protected $routeMiddleware
'admin' => \App\Http\Middleware\AdminMiddleware::class,
In your middleware folder you have the AdminMiddleware file.
In that you need to put your logic
In this case this is how it might look like depending upon you
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin == '1') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/'); // not admin. redirect whereever you like
}
}
Now in your route you have to pass the url using this middleware
Here is how it might look like
Route::get('/iamanadmin', ['middleware' => 'admin', function() {
return view('iamanadmin');
}]);
use middleware and check for admin user.
Route::get('admin', ['middleware' => 'checkadmin', function()
{
}]);
now create middleware and validate admin user.