I'm using entrust for managing role based permissions in Laravel 5.3, and naturally using manual/custom logins for different user types. Is it possible for Auth::attempt() to handle foreign table relationships? Basically, I want to do something like this:
if(Auth::attempt(['email' => $request->email, 'password' => $request->password, hasRole('teacher') => true])) {
return redirect()->intended('teacher/dashboard');
}
But I keep getting the error that hasRole() is an undefined function. I have included use Zizaco\Entrust\Traits\EntrustUserTrait; and use EntrustUserTrait;, which I thought would provide me with access to the function, but apparently not.
I appreciate the fact that hasRole() is a method for user objects, and at the time of checking I don't have a user object but I can't do a role check after attempt has succeeded because then the user has already been logged in, and if the role was wrong I would need to log them out as their credentials are correct, but not for the role; which seems slow and not pragmatic.
How should I go about handling this? Is there an alternative to Auth::attempt that I can use that will not log in the user, but check the user exists with specified input, and then run the role check, and then use another function to start an authenticated session?
If useful my LoginController is here:
<?php
namespace App\Http\Controllers\Teacher;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Validator;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class LoginController extends Controller {
use EntrustUserTrait;
public function showLoginForm() {
return view('teacher/login');
}
public function authenticate(Request $request) {
Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
])->validate();
if(Auth::attempt(['email' => $request->email, 'password' => $request->password, hasRole('teacher') => true])) {
return redirect()->intended('teacher/dashboard');
}
return redirect('admin/login')->with('invalid', 'Invalid username or password');
}
}
Any help or direction would be much appreciated. Thanks.
what you can do is:
if(Auth::attempt(['email' => $request->email, 'password' => $request->password)) {
if($user = Auth::user()->hasRole('teacher')) {
return redirect()->intended('teacher/dashboard');
} else {
return redirect()->intended('teacher/dashboard');
}
}
so this is what I did on login check role
$user = User::whereEmail($request->email)->first();
// I added this is because the customer
// are not suppose to login from here
if(!$user || $user->hasRole(['customer'])){
return $this->sendFailedLoginResponse($request);
}
if ($this->guard()->attempt($credentials, $request->has('remember'))) {
return $this->sendLoginResponse($request);
}
then if you wanna to change the route base on user role you can try to replace the redirectPath method by simply
public function redirectPath()
{
$user = Auth::user();
if($user->hasRole(['admin', 'system_admin']))
return '/backend';
elseif($user->hasRole(['teacher']))
return '/teacher';
}
I ended up manually doing the authentication check and using Auth::login($user) to create the authenticated session. This way the user was only logged in provided they met the role requirements for the specific login portal and controller.
public function authenticate(Request $request) {
Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
])->validate();
$user = User::where('email', $request->email)->first();
if($user) {
if(Hash::check($request->password, $user->password) && $user->hasRole('teacher')) {
Auth::login($user);
return redirect()->intended('teacher/dashboard');
}
}
return redirect('teacher/login')->with('invalid', 'Invalid username or password');
}
Related
When a user logs in I want them to be redirected to their profile page instead of homepage. I have a method in another controller that gets a user profile. Not sure what I need to do since the user profile takes a username variable but when user logs in I'm only asking for email and password.
My route file, but the following method is in a different controller from the authentication controller.
Route::get('/user/{username}', [
'uses' => 'ProfileController#getProfile',
'as' => 'profile.index',
'middleware' => ['auth'],
]);
My following method is in my authentication controller.
public function postSignin(Request $request)
{
$this->validate($request, [
'email' => 'required',
'password' => 'required',
]);
if (!Auth::attempt($request->only(['email', 'password']), $request->has('remember'))) {
return redirect()->back()->with('info' , 'Could not sign you in with that info.');
}
$user= User::where('username', $username)->first();
return redirect()->route('profile.index')
->with('info', 'You are now signed in.')
->with('user', $user);
}
The following is in my profile controller..
public function getProfile($username)
{
$user= User::where('username', $username)->first();
if (!$user){
abort(404);
}
return view('profile.index')
->with('user', $user);
}
To correctly build the route, you need to pass the username here:
$user = User::where('username', $username)->first();
return redirect()->route('profile.index', ['username' => $user->username])
->with('info', 'You are now signed in.')
->with('user', $user);
Get the username from the email provided and pass the $username variable to route:
public function postSignin(Request $request)
{
if (!Auth::attempt($request->only(['email', 'password']),$request->has('remember')))
{
return redirect()->back()->with('info' , 'Could not sign you in with that info.');
}
$username=User::where(['email'=>$request->email])->first()->username;
return redirect()->route('profile.index')->with('username', $username);
}
You can use as like below.
if (!Auth::attempt($request->only(['email', 'password']), $request->has('remember'))) {
return redirect('profile.index')->with('info' , 'Could not sign you in with that info.');
I just wanted to say if the user is not active, don't allow to login. I have made the controller as below, I am not sure what I am missing or what else I have to do here to make this work!
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/home';
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
My thinking was authenticate() method should do the trick!
The below code worked for my case:
protected function getCredentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
for Laravel 5.3 need to add following code to LoginController
protected function credentials(Request $request)
{
return [
'email' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
i think you should create method to check if user passed your credentials, here's my suggestion :
protected function getCredentials(Request $request)
{
return [
'username' => $request->input('email'),
'password' => $request->input('password'),
'active' => true
];
}
and your login method:
public function login(Request $request) {
$this->validate($request,['email' => 'required|email','password' => 'required']);
if (Auth::guard()->attempt($this->getCredentials($request))){
//authentication passed
}
return redirect()->back();
}
hope you get basic idea.
In LoginController.php file write this function
protected function credentials(Request $request) {
$extraFields = [
'user_type'=> 'customer',
'user_entry_status' => 1
];
return array_merge($request->only($this->username(), 'password'), $extraFields);
}
Go to this path :
your-project-folder/vendor/laravel/framework/src/illuminate/Foundation/Auth/AuthenticatesUsers.php
$credentials=$request->only($this->loginUsername(), 'password');
$credentials['status'] = '1';
return $credentials;
Change getCredantials works fine, but it is good practice to let user know, that the account was suspended (credentials are OK, but the account status is not). You can easily override login method in Auth/LoginController.php to your own copy, add your own logic to login process and raise own exception.
in Auth/LoginController.php create login and sendAccountBlocked function
/*load additional classes to LoginController.php*/
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Auth;
public function login(Request $request){
//
$this->validateLogin($request);
//
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
//check user status
if (Auth::user()->user_status == 'A') return $this->sendLoginResponse($request);
// if user_status != 'A' raise exception
else {
$this->guard()->logout();
return $this->sendAccountBlocked($request);
}
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
//
}//
protected function sendAccountBlocked(Request $request){
throw ValidationException::withMessages([
$this->username() => ['Your account was suspended.'],
]);
}
Currently, i'm working a laravel 5 project.
I want to use my custom encryption for passwords, so I made a function, and i try to use it.
First, I override the postLogin function, and I added the new password Encryption.
public function postLogin(Request $request)
{
$email = $request->get('email');
$password = $this->hashPassword($request->get('password'));
$this->validate($request, [
$this->loginUsername() => 'required', 'password' => 'required',
]);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $this->hasTooManyLoginAttempts($request)) {
return $this->sendLockoutResponse($request);
}
$credentials = ['email' => $email, 'password' => $this->hashPassword($password)];
if (Auth::attempt(['email' => $email, 'password' => $this->hashPassword($password)])) {
// Authentication passed...
return $this->handleUserWasAuthenticated($request, $throttles);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
if ($throttles) {
$this->incrementLoginAttempts($request);
}
return redirect($this->loginPath())
->withInput($request->only($this->loginUsername(), 'remember'))
->withErrors([
$this->loginUsername() => $this->getFailedLoginMessage(),
]);
}
As you can see in the code, I called the function hashPassword, and that works, but the problem is that "Auth::attempt" returns false always, despite I have the user in my database, with the right data.
Any solution please?
Thanks a lot
Kind Regards
try keeping the name ok password field other than 'password' such as 'mypass' or sth else.. auth automatically hashes the value you provide with password key.
You have to provide plain text password:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* #return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
}
Here is official reference:
https://laravel.com/docs/5.6/authentication#included-authenticating
I found a solution !!!
I changed "attempt" with "login", and it works well now :D
Thanks guys
Here its my controller for logging in. the register controller works well, it creates an user to database but when i try to log in with it fails it results always false pls help.
namespace App\Http\Controllers;
use Auth;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\User;
class loginController extends Controller
{
public function login(Request $request)
{
$_email = $request->input('emaillogin');
$_password = $request->input('passwordlogin');
if (Auth::attempt(['email' => $_email, 'password' => $_password])) {
Auth::login(Auth::user());
return redirect()->intended('/dashboard');
}
else {
return redirect()->intended('/');
}
}
}
Auth::attempt logs user into your application if passed, no need for second authentication
if (Auth::attempt(['email' => $_email, 'password' => $_password])) {
return redirect()->intended('/dashboard');
} else {
return redirect()->guest('/login');
}
As i said in the comment and i just tested it on my local pc, maybe it's wrong to you but for me it doesn't work if i use plain password save in database, here's my register file
protected function create(array $data)
{
$activation_code = str_random(60);
$user = User::create([
'username' => $data['username'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'activation_code' => $activation_code
]);
if ($user->save()) {
$data = array(
'name' => $user->username,
'code' => $activation_code,
);
\Mail::queue('emails.activate', $data, function($message) use ($user) {
$message->to($user->email)->subject('Thank you for registering. Please activate your account!');
});
}
return $user;
}
you see i have crypted password
'password' => bcrypt($data['password']),
and if i test it just with:
'password' =>$data['password'],
The registration works but authentication fails because password must be encripted you should also remove Auth::login(Auth::user()); that is not necessary
if (Auth::attempt(['email' => $_email, 'password' => $_password])) {
return redirect()->intended('/dashboard');
}
This happens on my site when i don't use bcrypt on password in registration controller and try to login later
Whoops! There were some problems with your input.
These credentials do not match our records.
Also what i have seen from our login form inputs are not emaillogin and passwordlogin they are just email and password
I would like to modify the existing Authorization module provided by Laravel 5, instead of asking for the email it will ask for the username field in the database.
Laravel search the variable $username in the file :
Illuminate\Foundation\Auth\AuthenticatesUsers
public function loginUsername() {
return property_exists($this, 'username') ? $this->username : 'email';
}
As you can see, by default it will be named as 'email'.
However you can override it in your AuthController by adding :
protected $username = 'username';
You do not need to modify the Auth module to do this, simply pass the user's identifier in the attempt. Use the field name in the attempt array as such:
if (Auth::attempt(['username' => $username, 'password' => $password]))
{
return redirect()->intended('dashboard');
}
You can try to check the file Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers just to get the idea.
Then add an override of postLogin on your AuthController:
public function postLogin(Request $request)
{
$this->validate($request, [
'username' => 'required',
'password' => 'required',
]);
$credentials = $request->only('username', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath());
}
return redirect($this->loginPath())
->withInput($request->only('username', 'remember'))
->withErrors([
'username' => 'These credentials do not match our records.',
]);
}
You also need to add use Illuminate\Http\Request; to your AuthController.
you can just override auth username function from LoginController.php in laravel 5.3
public function username(){
return 'username';
}
in controllers\auth\logincontroller add this
protected $username = 'user_name';//user_name field name
then go to Illuminate\Foundation\Auth\AuthenticatesUsers and change
public function username()
{
return 'email';//change this with "return $this->username;"
}
with this method You can have different log in type in different controller for example in another controller controllers\admin_auth\logincontroller
protected $username = 'phone_number';