I'm trying to make authentication with Laravel and MongoDB.
The problem is in my controller. This code does not make authentication :
if(!Input::exists('username') or !Input::exists('password')) {
App::abort(403, 'Forbidden request');
}
$credentials = array(
'username' => Input::get('username', null),
'password' => Input::get('password', null),
);
if(Auth::validate($credentials)) {
return 'Success';
} else {
return 'Failed';
}
But this one just works fine:
if(!Input::exists('username') or !Input::exists('password')) {
App::abort(403, 'Forbidden request');
}
$credentials = array(
'username' => Input::get('username', null),
'password' => Input::get('password', null),
);
$users = User::all();
$found = false;
foreach($users as $user) {
if($user->username == $credentials['username']
and Hash::check($credentials['password'], $user->password)) {
$found = true;
break;
}
}
if($found) {
return 'Success';
} else {
return 'Failed';
}
Both controllers are given the same username and password. First one always prints Failed, but second one gives true results.
The problem is you are using Auth::validate which only checks if the credentials are correct, but does not log the user in. You need to use Auth::attempt for a complete login. See the docs here http://laravel.com/docs/security#authenticating-users .
OK, Always remember that your model has to implement UserInterface.
Related
I have a Laravel 8 project. I want to change magic auth error message. And I did updated my code like this.
'This code has already been used' I replaced this message with this in the context of the code 'You will get a link in your email inbox every time you need to log in or register. Keep in mind that each link can only be used once.'
OLD AuthController.php
public function magicauth(Request $request)
{
$auth = app('firebase.auth');
$email = $request->email;
$oobCode = $request->oobCode;
$exits = User::where('email', $email)->first();
if(!is_null($exits))
{
if(is_null($exits->firebaseUserId))
{
$fUser = $auth->createUser([
'email' => $exits->email,
'emailVerified' => true,
'displayName' => $exits->name,
'password' => ($exits->email . $exits->id),
]);
$firebaseID = $fUser->uid;
$exits->update([
'firebaseUserId' => $firebaseID
]);
}
}
try
{
$result = $auth->signInWithEmailAndOobCode($email, $oobCode);
$firebaseID = $result->firebaseUserId();
$user = User::where('firebaseUserId', $firebaseID)->first();
if(is_null($user))
{
return view('auth.messages', ['message' => 'User not found']);
}
if($user->role_id != 3)
{
return view('auth.messages', ['message' => 'User is not creator']);
}
Auth::login($user);
return redirect()->route('home');
} catch (\Exception $e) {
return view('auth.messages', ['message' => 'This code has already been used.']);
}
return redirect()->route('login');
}
NEW AuthController.php
public function magicauth(Request $request)
{
$auth = app('firebase.auth');
$email = $request->email;
$oobCode = $request->oobCode;
$exits = User::where('email', $email)->first();
if(!is_null($exits))
{
if(is_null($exits->firebaseUserId))
{
$fUser = $auth->createUser([
'email' => $exits->email,
'emailVerified' => true,
'displayName' => $exits->name,
'password' => ($exits->email . $exits->id),
]);
$firebaseID = $fUser->uid;
$exits->update([
'firebaseUserId' => $firebaseID
]);
}
}
try
{
$result = $auth->signInWithEmailAndOobCode($email, $oobCode);
$firebaseID = $result->firebaseUserId();
$user = User::where('firebaseUserId', $firebaseID)->first();
if(is_null($user))
{
return view('auth.messages', ['message' => 'User not found']);
}
if($user->role_id != 3)
{
return view('auth.messages', ['message' => 'User is not creator']);
}
Auth::login($user);
return redirect()->route('home');
} catch (\Exception $e) {
return view('auth.messages', ['message' => 'You will get a link in your email inbox every time you need to log in or register. Keep in mind that each link can only be used once.']);
}
return redirect()->route('login');
}
But when I try now, I see that the message has not changed. How can I fix this?
Please follow below steps:
If you haven't done it yet, delete or rename the old AuthController class, use only new one, with new message.
Make sure routes going to the methods in the new controller
Run composer dump-autoload.
If the problem still persist I'd check whether some kind of cache mechanism is enabled in php, like opcache.
This is my login controller function
use ThrottlesLogins;
protected $maxLoginAttempts = 3;
protected $lockoutTime = 300;
public function login(Request $request)
{
if ($this->hasTooManyLoginAttempts($request))
{
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$validator = Validator::make(Input::all() , ['credential' => 'required|min:2|max:255', 'password' => 'required|string|min:8', ]);
$cred = $request->credential;
$pw = $request->password;
$remember = (Input::has('remember')) ? true : false;
if (filter_var($cred, FILTER_VALIDATE_EMAIL))
{
if (Auth::guard('customers')->attempt(['email' => $cred, 'password' => $pw, 'verified' => 1], $remember))
{
return redirect()->route('front');
}
else
{
return redirect()->route('customer-login-page')->with('error', 'Your credentials do not match');
}
}
else
{
if (Auth::guard('customers')->attempt(['contact' => $cred, 'password' => $pw], $remember))
{
return redirect()->intended(route('front'));
}
else
{
return redirect()->route('customer-login-page')->with('error', 'Your credentials do not match');
}
}
}
protected function hasTooManyLoginAttempts(Request $request)
{
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request), $this->maxLoginAttempts, $this->lockoutTime
);
}
It's not working. I've tried failed login attempts more that 3 times and still not getting throttled. AND
Even when I post the correct credentials, the login and redirect works but when I check the request I get a
302 FOUND error
in the network tab
You need to let the trait know that you are performing a login attempt by calling $this->incrementLoginAttempts($request) (see code). You can place this call right after your existing throttle check:
if ($this->hasTooManyLoginAttempts($request))
{
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$this->incrementLoginAttempts($request);
// other code
Try
use Illuminate\Foundation\Auth\ThrottlesLogins;
Instated of
use ThrottlesLogins;
I am new in php and working on REST API in cakephp3 for my android application.
after setting up php and composer and routing I created login function..
public function login() {
$this->request->allowMethod('post');
$this->loadModel('Users');
$entity = $this->Users->newEntity($this->request->data, ['validate' => 'LoginApi']);
if ($entity->errors()) {
$this->httpStatusCode = 400;
$this->apiResponse['message'] = 'Validation failed.';
foreach ($entity->errors() as $field => $validationMessage) {
$this->apiResponse['error'][$field] = $validationMessage[key($validationMessage)];
}
} else {
$hasher = new DefaultPasswordHasher();
$password = $hasher->hash($entity->password);
$user = $this->Users->find()
->where([
'email' => $entity->email,
'password' => $password
])
->first();
if (empty($user)) {
$this->httpStatusCode = 403;
$this->apiResponse['error'] = 'Invalid email or password.';
return;
}
$payload = ['email' => $user->email, 'name' => $user->name];
$this->apiResponse['token'] = JwtToken::generateToken($payload);
$this->apiResponse['message'] = 'Logged in successfully.';
isset($user);
isset($payload);
}
}
I use 123456 for password and this hasher returns random string every time, but the password which is already saved in database for 123456 is
$2y$10$f7K02jamD7ZeGHLcTkP6Weh6VsthMWHiwqHJmcqbsxuLCKGCQCGCu this.
that is why it gives Invalid password in response.
My question is how to match the exact same string or hashing for request.
thanks in advance.
With reference to this answer
Use this line
password_verify($entity->password, $user->password)
instead of this
$hasher = new DefaultPasswordHasher();
$password = $hasher->hash($entity->password);
you can try this function
public function login()
{
$this->request->allowMethod('post');
$this->loadModel('Users');
$entity = $this->Users->newEntity($this->request->data, ['validate' => 'LoginApi']);
if ($entity->errors()) {
$this->httpStatusCode = 400;
$this->apiResponse['message'] = 'Validation failed.';
foreach ($entity->errors() as $field => $validationMessage) {
$this->apiResponse['error'][$field] = $validationMessage[key($validationMessage)];
}
} else {
$user = $this->Users->find()->where(['email' => $entity->email])->first();
if (count($user)) {
if (password_verify($entity->password, $user->password)) {
$payload = ['email' => $user->email, 'password' => $user->password];
$this->apiResponse['token'] = JwtToken::generateToken($payload);
unset($user->password);
$this->apiResponse['response'] = array($user);
unset($user);
unset($payload);
} else {
$this->httpStatusCode = 403;
$this->apiResponse['error'] = 'Incorrect password';
return;
}
} else {
$this->httpStatusCode = 403;
$this->apiResponse['error'] = 'Email not found';
return;
}
}
}
The general idea would be to hash according to a key you specify.
An advice would be to keep changing the key periodically. You will then need to dehash your save into the clear again using the old key then rehash on new.
I'm not sure if the option is available to you, so you might want to take it with a grain of salt.
Cheers
First of all, CakePHP ships with authentication functionality out of the box, and I'd strongly suggest that you make use of that instead of running your own, given that it sounds as if you're looking for deterministic algorithms, this can very easily backfire.
If you are using CakePHP 3.5+, look into the authentication middleware plugin (currently in RC phase), for earlier CakePHP versions, use the authentication component.
For the sake of completeness, if you were to do this manually, you'd first query the user by its unique identifier (in your case the email address), and then compare the password at PHP level, using the password hashers AbstractPasswordHasher::check() implementation:
$user = $this->Users
->find()
->where([
'email' => $this->request->data('email')
])
->first();
if (!$user ||
$hasher->check($this->request->data('password'), $user->password) !== true
) {
// authentication failed
} else {
// authentication succeeded
}
Ok so what i'm trying todo, do not let login if user has not confirmed his account by email. My login code looks like that:
public function postLogin()
{
$credentials = [
'confirmed' => 0,
'email' => Input::get('email'),
'password' => Input::get('password')
];
$user = Sentinel::authenticate($credentials, false); // Login the user (if possible)
if ($user and $user->banned) {
Sentinel::logout();
$user = null;
}
if ($user) {
return $this->afterLoginActions();
} else {
$this->alertFlash(trans('app.access_denied'));
return Redirect::to('auth/login');
}
}
But i can still login without any errors. Any help? Thanks guys!
Edited: working, but now i dont get flash message if my details are incorect.
Code:
public function postLogin()
{
$credentials = [
'email' => Input::get('email'),
'password' => Input::get('password'),
'confirmed' => 1
];
$user = Sentinel::authenticate($credentials, false); // Login the user (if possible)
if ($user and $user->banned) {
Sentinel::logout();
$this->alertFlash(trans('app.banned'));
$user = null;
}
if ($user->confirmed==1) {
return $this->afterLoginActions();
}
else if ($user->confirmed==0) {
Sentinel::logout();
$this->alertFlash(trans('app.not_active'));
return Redirect::to('auth/login');
} else {
$this->alertFlash(trans('app.access_denied'));
return Redirect::to('auth/login');
}
}
Do you have a column in your table storing the information if this user passed the email confirmation? If you have one, this is what I do it with typical Laravel postLogin method.
public function postLogin(Request $request)
{
$credentialas = (your credential here);
// only check credentials
if ($this->guard()->once($credentials)) {
$currentStatus = $this->guard()->user()->status;
if (intval($currentStatus) === (NOT_CONFIRMED)) {
$this->guard()->logout();
return $this->sendSuspendedResponse($request);
} else {
$this->guard()->login($this->guard()->user());
return $this->sendLoginResponse($request);
}
}
}
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(),
]);