I'm trying using Laravel login authentication using different guards but I found that there is some repetition within the store method in the Login Controller (the only difference is the guard used, all other logic are the same). I just can't find a way to shorten them into some other logic such as method(function) which can be reusable. So if there is possible way, Please help me out.
public function store(Request $request)
{
$request->validate([
'username' => 'required',
'password' => 'required'
]);
if (Auth::guard('instructor')->attempt(['email' => $request->username, 'password' => $request->password])) {
if (auth('instructor')->user()->status === Instructor::HAS_DEACTIVATED) {
$request->session()->flush();
Auth::guard('instructor')->logout();
return redirect('login')->with(
'error',
'Your Account has being deactivated . Please Contact your Administrator!');
}
return redirect(route('instructor.dashboard'));
}
if (Auth::guard('student')->attempt(['email' => $request->username, 'password' => $request->password])) {
if (auth('student')->user()->status === Student::HAS_DEACTIVATED) {
$request->session()->flush();
Auth::guard('student')->logout();
return redirect('login')->with(
'error',
'Your Account has being deactivated . Please Contact your Administrator!');
}
return redirect(route('student.dashboard'));
}
return back()->with('error', 'Credentials provided do not match any record.');
}
You can create a separate function and then call that function.
public static function deleteSession($gaurd)
{
$request->session()->flush();
Auth::guard($gaurd)->logout();
return redirect('login')->with(
'error',
'Your Account has being deactivated . Please Contact your Administrator!');
}
Then in your store function call deleteSession statically.
if (auth('instructor')->user()->status === Instructor::HAS_DEACTIVATED)
{
self::deleteSession('instructor'); //change gaurd according to your need
}
This is probably not the best way to go about this, I'm sure there is a better solution, and #Aqib Javaed's seems to be it, but here's one way to shorten the code a bit that I could manage. It's not perfect but does the job done.
public function store(Request $request)
{
$request->validate([
'username' => 'required',
'password' => 'required'
]);
if (Auth::guard('instructor')->attempt(['email' => $request->username, 'password' => $request->password])) {
$userType = 'instructor';
} elseif (Auth::guard('student')->attempt(['email' => $request->username, 'password' => $request->password])) {
$userType = 'student';
} else {
return back()->with('error', 'Credentials provided do not match any record.');
}
$modelName = ucwords($userType); //turn the user type to its corresponding model name
if (auth('$userType')->user()->status === $modelName::HAS_DEACTIVATED) {
$request->session()->flush();
Auth::guard('$userType')->logout();
return redirect('login')->with(
'error',
'Your Account has being deactivated . Please Contact your Administrator!');
}
return redirect(route("$userType.dashboard"));
}
Related
I am new to Laravel and have been fairly successful in implementing user authentication. Now to move on to the next step I must allow only users whose status in active to login. For that I have added a
status TINYINT
column in my mysql users table.
I found this in the Laravel Documentation:
Specifying Additional Conditions
If you wish, you may also add extra conditions to the authentication
query in addition to the user's e-mail and password. For example, we
may verify that user is marked as "active":
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// The user is active, not suspended, and exists.
}
Can someone please point out where I need to put this chunk. Am thoroughly confused and need some pointers.
Thanks
Have this on your LoginController:
protected function credentials(Request $request)
{
return ['username' => $request->{$this->username()}, 'password' => $request->password, 'status' => 1];
}
You just take user status and check user status is true or false. You can take status using Auth::User()->status from auth session. Try this.
if(Auth::attempt(['email'=>$request->email,'password'=>$request->password])){
$userStatus = Auth::User()->status;
if($userStatus=='1') {
return redirect()->intended(url('/dashboard'));
}else{
Auth::logout();
Session::flush();
return redirect(url('login'))->withInput()->with('errorMsg','You are temporary blocked. please contact to admin');
}
}
else {
return redirect(url('login'))->withInput()->with('errorMsg','Incorrect username or password. Please try again.');
}
Just simply put this code in your App\Auth\LoginController or elsewhere where you have your LoginController located.
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
with this code you are overriding default authenticate function
Add this to your LoginController:
protected function credentials(Request $request)
{
return [$this->username() => $request->{$this->username()}, 'password' => $request->password, 'active' => 1];
}
Add below method in
app\Http\Controllers\Auth\LoginController.php
and it would extend
AuthenticatesUsers trait
validateLogin method. So basically, it would check for your active clause as well.
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => [
'required',
Rule::exists('users')->where(function ($query) {
$query->where('active', 1);
}),
],
'password' => 'required'
]);
}
OR
Place your required code in app\Http\Controllers\Auth\LoginController.php
public function authenticate(Request $request)
{
if (Auth::attempt(['email' => $request->email, 'password' => $request->password, 'active' => 1])) {
// The user is active, not suspended, and exists.
}
}
You can override authenticated() method in your App\Http\Controllers\Auth\LoginController.php like so:
protected function authenticated(Request $request, $user)
{
if(!$user->active) {
Auth::logout();
abort(403);
};
}
Do note it's quick but not very "Laravely" solution. Sloppy.
In my laravel app, at the start I had decided to create my own custom login controller, rather than use the base one.
public function postSignin(Request $request, AppMailer $mailer) {
$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 those details.');
}
if (Auth::attempt(['email' => $request->input('email'), 'password' => $request->input('password'), 'verified' => 0]))
{
$mailer->sendEmailConfirmationTo(Auth::user());
Auth::logout();
return redirect()->back()->with('info', 'Email verification required.');
}
Auth::user()->last_login = new DateTime();
Auth::user()->save();
return redirect()->back()->with('info', 'You are now signed in.');
}
And now I want to edit this so that users can also login with their usernames and not just their emails, using the same field. However, the attempt method is confusing. It seems to expect an email even after I switch the values around.
The Auth documentation isn't very helpful in this case either. It asks me to add this:
public function username()
{
return 'username';
}
in the login controller, but obviously this is for a default setup.
You can use the 'FILTER_VALIDATE_EMAIL' checker.
$username = $request->get('username');
$password = $request->get('password');
$remember_me = $request->get('remember_me','1');
$field = filter_var($username,FILTER_VALIDATE_EMAIL)? 'email': 'username';
if(Auth::attempt([$field => $username,'password' => $password],$remember_me)){
//Auth successful here
}
Meaning FILTER_VALIDATE_EMAIL do check the string whether it is in email format or not.
I hope this sample code helps you.
-ken
Now I've followed the Laravel documentation on how to allow usernames during authentication, but it takes away the ability to use the email. I want to allow users to use their username or email to login. How do I go about this?
I've added this code to the LoginController as per Laravel's Documentation and it only allows username for login. I want it to accept username or email for login.
public function username () {
return 'username';
}
I think a simpler way is to just override the username method in LoginController:
public function username()
{
$login = request()->input('login');
$field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
request()->merge([$field => $login]);
return $field;
}
Follow instructions from this link: https://laravel.com/docs/5.4/authentication#authenticating-users
Then you can check for the user input like this
$username = $request->username; //the input field has name='username' in form
if(filter_var($username, FILTER_VALIDATE_EMAIL)) {
//user sent their email
Auth::attempt(['email' => $username, 'password' => $password]);
} else {
//they sent their username instead
Auth::attempt(['username' => $username, 'password' => $password]);
}
//was any of those correct ?
if ( Auth::check() ) {
//send them where they are going
return redirect()->intended('dashboard');
}
//Nope, something wrong during authentication
return redirect()->back()->withErrors([
'credentials' => 'Please, check your credentials'
]);
This is just a sample. THere are countless various approaches you can take to accomplish the same.
Open your LoginController.php file.
Add this reference
use Illuminate\Http\Request;
And override the credentials method
protected function credentials(Request $request)
{
$field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
? 'email'
: 'username';
return [
$field => $request->get($this->username()),
'password' => $request->password,
];
}
Successfully tested in Laravel 5.7.11
You need to override protected function attemptLogin(Request $request) method from \Illuminate\Foundation\Auth\AuthenticatesUsers Trait in your LoginController
i.e. in my LoginController class
protected function attemptLogin(Request $request) {
$identity = $request->get("usernameOrEmail");
$password = $request->get("password");
return \Auth::attempt([
filter_var($identity, FILTER_VALIDATE_EMAIL) ? 'email' : 'username' => $identity,
'password' => $password
]);
}
Your LoginController class should use Trait \Illuminate\Foundation\Auth\AuthenticatesUsers in order to override attemptLogin method i.e.
class LoginController extends Controller {
use \Illuminate\Foundation\Auth\AuthenticatesUsers;
.......
.......
}
I think its even more simple, just override the method from AuthenticatesUsers traits, credentials method in your LoginController. Here I have implemented to login with either email or phone. You can change it to fit your needs.
LoginController.php
protected function credentials(Request $request)
{
if(is_numeric($request->get('email'))){
return ['phone'=>$request->get('email'),'password'=>$request->get('password')];
}
return $request->only($this->username(), 'password');
}
This is the way I do it:
// get value of input from form (email or username in the same input)
$email_or_username = $request->input('email_or_username');
// check if $email_or_username is an email
if(filter_var($email_or_username, FILTER_VALIDATE_EMAIL)) { // user sent his email
// check if user email exists in database
$user_email = User::where('email', '=', $request->input('email_or_username'))->first();
if ($user_email) { // email exists in database
if (Auth::attempt(['email' => $email_or_username, 'password' => $request->input('password')])) {
// success
} else {
// error password
}
} else {
// error: user not found
}
} else { // user sent his username
// check if username exists in database
$username = User::where('name', '=', $request->input('email_or_username'))->first();
if ($username) { // username exists in database
if (Auth::attempt(['name' => $email_or_username, 'password' => $request->input('password')])) {
// success
} else {
// error password
}
} else {
// error: user not found
}
}
I believe there is a shorter way to do that, but for me this works and is easy to understand.
public function username()
{
//return ‘identity’;
$login = request()->input('identity');
$field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'phone';
request()->merge([$field => $login]);
return $field;
}
protected function validateLogin(Request $request)
{
$messages = [
'identity.required' => 'Email or username cannot be empty',
'email.exists' => 'Email or username already registered',
'phone.exists' => 'Phone No is already registered',
'password.required' => 'Password cannot be empty',
];
$request->validate([
'identity' => 'required|string',
'password' => 'required|string',
'email' => 'string|exists:users',
'phone' => 'numeric|exists:users',
], $messages);
}
https://dev.to/pramanadiputra/laravel-how-to-let-user-login-with-email-or-username-j2h
This solution of "Rabah G" works for me in Laravel 5.2. I modified a litle but is the same
$loginType = request()->input('useroremail');
$this->username = filter_var($loginType, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
request()->merge([$this->username => $loginType]);
return property_exists($this, 'username') ? $this->username : 'email';
Thanks, this is the solution I got thanks to yours.
protected function credentials(Request $request) {
$login = request()->input('email');
// Check whether username or email is being used
$field = filter_var($login, FILTER_VALIDATE_EMAIL) ? 'email' : 'user_name';
return [
$field => $request->get('email'),
'password' => $request->password,
'verified' => 1
];
}
hi i am building a social network with Laravel but even though i log in it always says that the password is wrong but it is correct i think the problem is in the following part of code,
please show me the error?
public function getsignin()
{
return view('auth.signin');
}
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','Whoops! Try again please...');
}
return redirect()->route('home')->with('info','Welcome!');
}
Try to name your parameters like this:
if( !Auth::attempt(['email' => $request->email, 'password' => $request->password], $request->has('remember'))) {
...
}
if(!Auth::attempt(['email' => $request->email, 'password' => $request->password, $request->has('remember'))]) {
...
}
When attempting, your parameter must be one array, with key=>value pairs of attributes you want to be accepted
This is pretty standard login function and validation that works nicely. But I also want to check that the user is active. I have set up a column in my users table with 'active' set to either 0 or 1.
public function post_login()
{
$input = Input::all();
$rules = array(
'email' => 'required|email',
'password' => 'required',
);
$validation = Validator::make($input, $rules);
if ($validation->fails())
{
return Redirect::to_route('login_user')
->with_errors($validation->errors)->with_input();
}
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
);
if (Auth::attempt($credentials))
{
// Set remember me cookie if the user checks the box
$remember = Input::get('remember');
if ( !empty($remember) )
{
Auth::login(Auth::user()->id, true);
}
return Redirect::home();
} else {
return Redirect::to_route('login_user')
->with('login_errors', true);
}
}
I've tried something like this already:
$is_active = Auth::user()->active;
if (!$is_active == 1)
{
echo "Account not activated";
}
But this can only be used within the 'auth attempt' if statement and at that point the users credentials(email and pass) are already validated. So even if the users account if not active at this point they are already logged in.
I need a way to return validation to let them know they still need to activate their account and check if their account is set at the same time their email and pass are being checked.
Filters are the way to go. It's easy and clean to solve this problem, see my example below.
Route::filter('auth', function()
{
if (Auth::guest())
{
if (Request::ajax())
{
return Response::make('Unauthorized', 401);
}
else
{
return Redirect::guest('login');
}
}
else
{
// If the user is not active any more, immidiately log out.
if(Auth::check() && !Auth::user()->active)
{
Auth::logout();
return Redirect::to('/');
}
}
});
Can't you use something like this:
if (Auth::once($credentials))
{
if(!Auth::user()->active) {
Auth::logout();
echo "Account not activated";
}
}
Just make the active field one of the confirmations. You can do this:
$credentials = array(
'username' => $input['email'],
'password' => $input['password'],
'active' => 1
);
if (Auth::attempt($credentials))
{
// User is active and password was correct
}
If you want to specifically tell the user they are not active - you can follow it up with this:
if (Auth::validate(['username' => $input['email'], 'password' => $input['password'], 'active' => 0]))
{
return echo ('you are not active');
}
A better solution might be to create an Auth driver that extends the Eloquent Auth driver already in use and then override the attempt method.
Then change your auth config to use your driver.
Something like:
<?php
class Myauth extends Laravel\Auth\Drivers\Eloquent {
/**
* Attempt to log a user into the application.
*
* #param array $arguments
* #return void
*/
public function attempt($arguments = array())
{
$user = $this->model()->where(function($query) use($arguments)
{
$username = Config::get('auth.username');
$query->where($username, '=', $arguments['username']);
foreach(array_except($arguments, array('username', 'password', 'remember')) as $column => $val)
{
$query->where($column, '=', $val);
}
})->first();
// If the credentials match what is in the database we will just
// log the user into the application and remember them if asked.
$password = $arguments['password'];
$password_field = Config::get('auth.password', 'password');
if ( ! is_null($user) and Hash::check($password, $user->{$password_field}))
{
if ($user->active){
return $this->login($user->get_key(), array_get($arguments, 'remember'));
} else {
Session::flash('authentication', array('message' => 'You must activate your account before you can log in'));
}
}
return false;
}
}
?>
In your login screen, check for Session::get('authentication') and handle accordingly.
Alternatively, allow them to log in but don't let them access any pages other than one that offers a link to resend the activation email.
This is what I do:
if (\Auth::attempt(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']], $request->has('remember'))) {
if (\Auth::once(['EmailWork' => $credentials['EmailWork'], 'password' => $credentials['Password']])) {
if (!\Auth::user()->FlagActive == 'Active') {
\Auth::logout();
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'Active' => 'You are not activated!',
]);
}
}
return redirect('/');
}
return redirect($this->loginPath())
->withInput($request->only('EmailWork', 'RememberToken'))
->withErrors([
'EmailWork' => $this->getFailedLoginMessage(),
]);