I'm still very noobish with Larvel, so i can't figure out how to do this:
I'm stuck at the login system. I'm trying to understand how i can prevent inactive users to use the application.
Edit
With inactive users i mean those users which has the record field 'active' on 0. So i need to implement a check which verifies the mail and the password and also the active field
I've made the tables and the auth system, and as written in the documentation, I've placed this method inside my AuthController:
public function authenticate()
{
dd('hi');
}
Well it completely gets ignored. It seems like it never gets triggered. Am I missing something?
My controller looks like the original Laravel's 5.2 AuthController except for the method before. No other changes has been made, since according to the documentation no other changes are mentioned...
My tests:
I've also searched for a method called authenticate. But no methods where found (using php storm). So my question is:
If it's a trait, isn't supposed to have that method declared? So i can override it just by declaring a method with the same name?
Important files:
Routes.php
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
Route::get('/', 'HomeController#index');
Route::auth();
Route::get('/home', 'HomeController#index');
SOLVED
If you type php artisan route:list you see when login by post method, user post their info account to login action in LoginController. So, I've solved the problem by override login method in Auth\LoginController as below:
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
$credentials['active'] = 1;
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('/');
}
}
Firstly you need to remove the Auth routes but before that run php artisan route:list in your terminal. Copy the auth related routes and stick them in your routes file.
Change the controller in each of the routes to point to your own AuthController in your application.
Next, include the following 2 traits into your controller (top of the page) plus 2 classes;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
and then include the traits in the top of your class;
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
as it stands, the auth functionality should still work as normal.
Then, alter the login method to accept an active flag. Add the following to your new controller;
/**
* [login description]
* #param Request $request [description]
* #return [type] [description]
*/
public function login( Request $request, $guard )
{
$this->validateLogin( $request );
$throttles = $this->isUsingThrottlesLoginsTrait();
if ( $throttles && $lockedOut = $this->hasTooManyLoginAttempts( $request ) ) {
$this->fireLockoutEvent( $request );
return $this->sendLockoutResponse( $request );
}
$credentials = $this->getCredentials( $request );
if ( Auth::guard( $guard )->attempt( [ 'active' => 1 ] + $credentials, $request->has( 'remember' ) ) ) {
return $this->handleUserWasAuthenticated( $request, $throttles, $guard );
}
// check to see if they can login without the active flag
if( Auth::guard( $guard )->attempt( $credentials ) ) {
// log them back out and send them back
Auth::guard( $guard )->logout();
return redirect()->back()
->withInput( $request->only( $this->loginUsername(), 'remember' ) )
->withErrors([
'active' => 'Your account is currently not active',
]);
}
if ( $throttles && ! $lockedOut ) {
$this->incrementLoginAttempts( $request );
}
return $this->sendFailedLoginResponse( $request );
}
using the traits in your own controller should give you more flexibility in the future.
regards
To prevent guest users (not authenticated) to access certain routes, you should protect those routes in your routes.php:
// All the routes inside the group are accessible for logged in users only
Route::group(array('before' => 'auth'), function() {
Route::get('myroute', 'Controller#action');
});
// For routes accessible for everybody
Route::get('login', function(){
return View::make('login');
});
Solved
I've managed to do this by myself. Not sure that is the proper way, but anyway it works.
I've decided to create a middleware that after the successful login, checks that the user 'active' field is 1. If not it redirects to a specific page.
public function handle($request, Closure $next)
{
// If the user is not logged in
if(!Auth::user()){
return redirect('login');
}
// If the user is inactive
if(!Auth::user()->active){
return redirect('inactive');
}
return $next($request);
}
Related
So I'm trying to make a laravel API for a escorts-like site, anyway, i use Passport for authentification and the register part works but the login one doesnt, and i dont know why, i'll let the passportAuthController down as code and a ss of the database
class passportAuthController extends Controller
{
/**
* handle user registration request
*/
public function registerUserExample(RegisterUserRequest $request){
///TODO: TEST THE CRUD FEATURES IMPLEMENTED IN THE USER CONTROLLER AFTER U CHECK LOGIN FEATURE
$attributes = $request -> validated();
$user = User::create($attributes);
$access_token_example = $user->createToken('RegisterToken')->accessToken;
//return the access token we generated in the above step
return response()->json(['token'=>$access_token_example],200);
}
/**
* login user to our application
*/
public function loginUserExample(Request $request){
$login_credentials=[
'email'=>$request->email,
'password'=>$request->password,
];
if(auth()->attempt($login_credentials)){
//generate the token for the user
$user_login_token= auth()->user()->createToken('LoginToken')->accessToken;
//now return this token on success login attempt
return response()->json(['token' => $user_login_token], 200);
}
else{
//wrong login credentials, return, user not authorised to our system, return error code 401
return response()->json(['error' => 'UnAuthorised Access'], 401);
}
}
/**
* This method returns authenticated user details
*/
// index function
public function authenticatedUserDetails(){
//returns details
return response()->json(['authenticated-user' => auth()->user()], 200);
}
}
The request as well:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'name'=>'required|max:255|min:3',
'email'=>'required|email',
'password'=>'required|min:7|max:255',
'gender'=>'required|min:4|max:6',
'interest'=>'required|min:4|max:6',
'Country'=>'required|max:255',
'County'=>'required|max:255',
'City'=>'required|max:255',
'birthday'=>'required|date'
];
}
}
and the ss of the database:
and the routes (api.php):
<?php
use App\Http\Controllers\passportAuthController;
use App\Http\Controllers\UserController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
//routes/api.php
//login & register routes
Route::post('register',[passportAuthController::class,'registerUserExample']);
Route::post('login',[passportAuthController::class,'loginUserExample']);
//CRUD and search routes
Route::post('storeUser',[UserController::class,'store']);
Route::get('showAll',[UserController::class, 'index']);
Route::put('updateUser/{id}',[UserController::class,'update']);
Route::delete('delete/{id}', [UserController::class,'deleteUser']);
Route::get('search/{name}',[UserController::class,'search']);
//add this middleware to ensure that every request is authenticated
Route::middleware('auth:api')->group(function(){
Route::get('user', [passportAuthController::class,'authenticatedUserDetails']);
});
Your password in users table is not encrypted.
The reason is this line
$attributes = $request->validated();
$user = User::create($attributes);
You have not encrypted your password and the method auth()->attempt($login_credentials) uses compares the encrypted password request with stored encrypted password in your db.
You can use bcrpyt() to encrypt your password, laravel comes with bcrypt() as a helper function.
Change to this in your registerUserExample(RegisterUserRequest $request)
$attributes = $request->validated();
foreach($attributes as $key => $attribute){
if($key == 'password') {
$attributes[$key] = bcrypt($attribute);
}
}
$user = User::create($attributes);
so if you see the response is mean that wrong login credentials, return, user not authorised to our system, return error code 401 ,
So with a little observation you will know that your code work fine but your logic is not good ,
So the answer simply is because the password insert in your database is note crypted and laravel passport when they are trying to make login they use a function of check ,
so if you want your code work your password must be crypted in the register exemple
$user->password = hash::make($request->password);
Or
$user->password = Crypt::encrypt($request->password);
Conclusion you can't make authentification with laravel passport if your password not crypted
The attempt method accepts an array of key / value pairs as its first argument. The password value will be hashed. The other values in the array will be used to find the user in your database table. So,
You try this
public function loginUserExample(Request $request){
$user = User::where('account', $request->account)
->where('password', $request->password)
->first();
if($user) {
Auth::loginUsingId($user->id);
// -- OR -- //
Auth::login($user);
return redirect()->route('home');
} else {
return redirect()->back()->withInput();
}
}
I just added this route group to web.php:
Route::middleware('auth')->group(function() {
Route::get('profile' , [ProfileController::class, 'index'])->name('profile');
});
As you can see I have used the middleware auth so if I'm not logged in, it should be showing me the login page.
And I had also defined authentication routes manually:
Route::prefix('auth')->namespace('Auth')->middleware('guest')->group(function() {
Route::get('login' , [LoginController::class, 'showLogin'])->name('auth.login');
Route::post('login' , [LoginController::class, 'login']);
Route::get('register' , [RegisterController::class, 'showRegister'])->name('auth.register');
Route::post('register' , [RegisterController::class, 'register']);
});
So the route name of the login page is auth.login and I just added it to Authenticate middleware which is set to auth at kernel.php:
'auth' => \App\Http\Middleware\Authenticate::class,
Authenticate.php:
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('auth.login');
}
}
Now, the problem is whenever it should showing me the login page when not signed in, I get this error message:
Symfony\Component\Routing\Exception\RouteNotFoundException Route
[login] not defined.
So what's going wrong here, how can I fix this error?
In app\Exceptions\Handler.php
use Illuminate\Auth\AuthenticationException;
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest(route('auth.login'));
}
Just change this
Route::get('login' , [LoginController::class, 'showLogin'])->name('auth.login');
to this
Route::get('login' , [LoginController::class, 'showLogin'])->name('login');
This sounds ridiculous but true. The reason is when you call login you call it with auth.login but Laravel knows only login. I had a similar issue and able to sort it by defining another route(GET).
Laravel uses login route somewhere for example in \app\Http\Handler.php therefore it's necessary to have login route. You can change your route name as Abdulla Nilam said before or If you don't want to change it, add this:
Route::get('/login/redirect', function () {
return redirect(route('auth.login'));
})->name('login');
( Laravel 8 ) In App\Http\Middleware\Authenticate.php
<?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()) {
return route('auth.login');
}
}
}
you can easily change "login" to "auth.login" !
problem
I'm looking for a way to protect users from access routes which do not belong to them, example admin cannot access user area and simple user cannot access admin area
Hi, i've a laravel 5.3 app and it has two types of users
Admin
Simple User
i'm trying to prevent admin from accessing simple user routes and vice-versa, I search a lot and found one solution of creating a middleware
what i've done
<?php
namespace App\Http\Middleware;
use Auth;
use Closure;
class UserRole
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( Auth::check()) // user logged
{
$request_url = $request->getRequestUri();
if( Auth::user()->user_type == 1 ) // simple user
{
// checking if simple user is visiting routes // starts with /user in the url
if (strpos($request_url, "/user") === 0)
{
return $next($request);
}
else
{
return redirect('/');
}
}
// checking if admin is visiting routes // starts with /admin in the url
else if( Auth::user()->user_type == 2 ) // admin
{
if (strpos($request_url, "/admin") === 0)
{
return $next($request);
}
else
{
return redirect('/');
}
}
else
{
return redirect('/');
}
}
return redirect('/');
}
}
unfortunately both are able to access each others restricted areas. I'm unable to find a better way to protect user from accessing routes which they don't have access too.
If you want to accomplish that using middleware you need to do following -
Create two middlewares, one for admin and one for simple user.
Then create two route group in your routes file i.e. routes/web.php
Protect one route group with admin middleware, and put all of your admin related routes in that group. Protect another route group
with
simple-user middleware and put all of your admin related routes in that group.
Route::group(['middleware' => ['auth', 'admin']], function() {
// put all your admin routes here
});
Route::group(['middleware' => ['auth', 'simple-user']], function() {
// put all your simple user routes here
});
You can also accomplish that using role and permission. Here is a package that can satisfy your needs.
https://packagist.org/packages/zizaco/entrust
I'm actually implementing 2-factor auth into the project. What I did was
Auth::user()->google2fa_passed = 1;
In fact it doesn't really store, when navigate to another page, the value is missing.
I also don't want to keep in another session, because when user logout (or users remove the session cookie from their browser), then will show a login page, and go through the 2 factor auth again.
Any idea how to save 1 more attribute to user session?
When you use Auth::user() it give you the Eloquent model of the authenticate user.
If you want to store data in session you need to use the Session facade or session() helper.
You can find more information about session in the documentation.
PS: Old version of the documentation is better (http://laravel.com/docs/5.0/session).
Eventually, I use session to store.
After key in the 6-digits code, store a flag into session
\Session::put('totp_passed', 1);
In app/Http/Middleware/Authenticate.php, remove the 2FA session if session expired
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
// remove the 2-factor auth if the user session expired
\Session::forget('totp_passed'); // <------- add this line
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->route('auth.login');
}
}
return $next($request);
}
Then create another middleware, e.g. app/Http/Middleware/TwoFactorAuth.php
namespace App\Http\Middleware;
use Closure;
class TwoFactorAuth
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!\Session::has('totp_passed')) {
return redirect()->route('auth.2fa');
}
return $next($request);
}
}
In app/Http/Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'2fa' => \App\Http\Middleware\TwoFactorAuth::class, // <------ add this line
];
How to use
Route::group(['middleware' => 'auth'], function () {
// must be login first only can access this page
Route::get('2fa', ['as' => 'auth.2fa', 'uses' => 'Auth\AuthController#get2FactorAuthentication']);
Route::post('2fa', ['uses' => 'Auth\AuthController#post2FactorAuthentication']);
// add 2-factor auth middleware
Route::group(['middleware' => '2fa'], function () {
// all routes that required login
});
});
I've started learning Laravel 5.1 and so far I'm liking it! But there is one thing I don't get yet..
In my previous project I had 2 specific controllers (eg: "normal", "extended") which , after a successfull login, were called based on the Users user_group from the database.
If "Foo.Bar" enters his valid credentials and has the group normal he is redirected to NormalControler. Since I wasn't using any framework I restricted access to the other group by setting a $_SESSION with the group and checking it. So if another group tried to access that controller he got redirected.
How would this be achievable in Laravel 5? So far I have a controller which is callable without an Authentication and one restricted by this code in routes.php :
// All routes in the group are protected, only authed user are allowed to access them
Route::group(array('before' => 'auth'), function() {
// TO-DO : Seperate Controller access
});
And the login looks like this :
public function performLogin()
{
$logindata = array(
'username' => Input::get('user_name'),
'password' => Input::get('user_pass')
);
if( Auth::attempt( $logindata ) ){
// return \Redirect::to( check group and access this controller based on it);
}
else {
// TO-DO : Redirect back and show error message
dd('Login failed!');
}
}
----- EDIT -----
I've run the artisan command and made this middleware as you suggested :
namespace App\Http\Middleware;
use Closure;
use Request;
class GroupPermissions
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $group)
{
// Check User Group Permissions
if( $request->user()->group === $group ){
// Continue the request
return $next($request);
}
// Redirect
return redirect('restricted');
}
}
and edited this line into Kernel.php into $routeMiddleware :
'group.perm' => \App\Http\Middleware\GroupPermissions::class
I think this is done right so far, correct me if I'm wrong! Could I then do something like this to restrict the controllers?
Route::group(array('before' => 'auth'), function() {
Route::group( ['middleware' => 'group.perm', 'group' => 'normal'], function(){
Route::get('/normal/index', 'DummyNormalController#index');
});
Route::group( ['middleware' => 'group.perm', 'group' => 'extended'], function(){
Route::get('/extended/index', 'DummyExtendedController#index');
});
});
Ok, here is what you might do. Once user is logged in, you would check his credentials, get his user_group and decide what controller he should be redirected to.
if( Auth::attempt( $logindata ) ){
$user = Auth::user();
if ($user->inGroup('normal')) {
return redirect()->route('normal_controllers_named_route');
}
return redirect()->route('extended_controllers_named_route');
}
return redirect()->back()->withFlashMessage('don\'t get me wrong');
This will handle right routing after logging in.
The next portion where you need to protect you routes from unwanted user groups may be achieved with middlewares.
do an artisan command php artisan make:middleware ShouldBeInGroup
go to app/http/Kernel.php and add your new middleware to the routeMiddleware array. Key of the item might be anything you like. Let's call in inGroup. So: 'inGroup' => 'App\Http\Middleware\ShouldBeInGroup'
Now, in your controller, in constructor, you are able to call this middleware
$this->middleware('inGroup:extended'); //we also passing the name of the group
at lastly, work on the our middleware. Open newly created ShouldBeInGroup class and edit the handle method.
public function handle($request, Closure $next, $groupName)
{
if (Auth::check() && Auth::user()->inGroup($groupName)) {
return $next($request);
}
return redirect('/');
}
And finally you should work on inGroup method, that should return true of false. I assume that you have user_group field your users table. Then in your User eloquent model add the method
public function inGroup($groupName) {
return $this->user_group == $groupName;
}
Edit
if you want to use this middleware in your routes, you can do the following
Route::group(array('before' => 'auth'), function() {
Route::get('/normal/index', ['middleware' => 'group.perm:normal', 'uses' =>'DummyNormalController#index']);
}
But generally it's better to put all your middlewares into your Controller's constructor
public function __construct(){
$this->middleware('group.perm:normal'); // you can also pass in second argument to limit the methods this middleware is applied to : ['only' => ['store', 'update']];
}
And also on this note, Laravel provides built in auth middleware that you can use
public function __construct(){
$this->middleware('auth');
$this->middleware('group.perm:normal');
}
so then your routes would become much cleaner, just:
Route::get('normal/index', 'DummyNormalController#index');
I think the best way to do that is using middlewares. See the doc here
You can easily create a middleware using the following artisan command:
php artisan make:middleware ExtendedMiddleware
If you can't or don't want to use artisan, you need to create a class in The App/Http/Middleware folder.
In this class you'll need the following method to handle the request. In the method you can check for the user group.
public function handle($request, Closure $next)
{
// check user group
if( user_group_ok )
return $next($request); // Continue the request
return redirect('restricted'); // Redidrect
}
You can then use this middleware in your route.php file:
Route::group(['middleware' => 'auth'], function()
{
// Logged-in user with the extended group
Route::group(['middleware' => 'extended'], function()
{
// Restricted routes here
});
// Normal routes here
});
You can create a Middleware called : PermissionFilter
In PermissionFilter, you check if requesting user is in the group or not.
I can't provide a demo for now, but if you want I can make a demo later.
L5 middleware: http://laravel.com/docs/5.1/middleware