the auth_key of yii2 is'nt interference in cookie base - authentication

I have problem with auth_key , I have login form and it's work correctly without remember me and with remember me , but I read yii document , in that document wrote about remember me work with id and auth_key for create cookie to stay user in long time , i check the framework code and in there have three parameters (id, auth_key, expire_time()) i save auth_key in user table and it's code here
public function generateAuthKey()
{
$this->auth_key = Yii::$app->security->generateRandomString();
}
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
public function getAuthKey()
{
return $this->auth_key;
}
but i have problem , it's if a user login in site and i go to the user table and change the auth_key field , and now if user refresh the page it must be throw out the site because it's auth key is changed , but the user stay login in site , where is problem ?

The main use of auth_key is to authenticate the user by cookie (user don't have to put login data again). When you choose to be remembered at Login, this is how you are remembered. The system has to identify and login you somehow. It won't log out user if u change it.

You can try to change the key yourself in the "ValidateAutney" method, but this will be a bad practice, it is better to set the session time.

'session' => [
'class' => 'yii\mongodb\Session',
'writeCallback' => function($session)
{
return [
'user_id' => Yii::$app->user->id,
'agent' => Yii::$app->request->getUserAgent(),
'ip' => Yii::$app->request->getUserIP(),
'auth_key' => Yii::$app->security->generateRandomString(),
];
}
],
public function getAuthKey()
{
Yii::$app->session->open();
$query = new Query();
$query->select(['auth_key'])
->from('cache')
->where(['id'=> Yii::$app->session->id ]);
$row = $query->one();
return $row['auth_key'];
}

Related

CakePHP 2 user database/password migration for Cakephp 4

My customer has program which is made with CakePHP 2 (not my project) and he wants to upgrade it to newest CakePHP.
I noticed that there was tutorial for Custom Password Hasher (legacy / sha1 + blowfish) but it has been deprecated since 4.0 and should use authentication plugin.
How i can make that kind of procedure without touching core code which check if password needs rehash and then create new hash on database?
On loadAuthenticator i cannot put different hasher for that.
Yes but old valid user gives on login "Authentication\Authenticator\Result Object ( [_status:protected] => FAILURE_IDENTITY_NOT_FOUND [_data:protected] => [_errors:protected] => Array ( [Password] => Array ( ) ) )"
and newly created user with new has Authentication\Authenticator\Result Object ( [_status:protected] => SUCCESS etc..
I have made that check with that tutorial but how it can be valid if that not "exists"? SQL-query returns 1 row with old users email.
public function login() {
$this->viewBuilder()->setLayout('login');
if($this->request->is(['post','put'])) {
$result = $this->Authentication->getResult();
// regardless of POST or GET, redirect if user is logged in
if ($result->isValid()) {
$authService = $this->Authentication->getAuthenticationService();
// Assuming you are using the `Password` identifier.
if ($authService->identifiers()->get('Password')->needsPasswordRehash()) {
// Rehash happens on save.
$user = $this->Users->get($this->Authentication->getIdentityData('id'));
$user->password = $this->request->getData('password');
$this->Users->save($user);
}
// Redirect to a logged in page
return $this->redirect([
'controller' => 'Pages',
'action' => 'display',
'home'
]);
}
}
}
It will never goes inside "result->isvalid()" with old user.

Yii2 Authentication not working as expected

I am working on a micro service. It has basically login and registration. I followed the Yii2 official guide. But now i am facing an issue. When i try to send request to the endpoints which are protected ( Only users with access_token can make request ) It works but very strange it checks all the rows in the database and if access_token is matches any rows in the database then it allows the request. But what i want - I am trying to get users information, if i pass the token i want only the information which belongs to current user ( Whose token is in request ) .
I am doing this in my UserController -
public function behaviors() {
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
$behaviors['authenticator']['only'] = ['view'];
return $behaviors;
}
And in User model have implemented this method -
public static function findIdentityByAccessToken($token, $type = null) {
return static::findOne(['access_token' => $token]);
}
Where i am doing wrong?
If the access_token in the request matches the access_token value for any of the users in the database, then, like you say, the authentication filter will forward the request to the corresponding action on your controller.
At this point the application user component points to the user that was found, you can use the user's component id to recover the record matching the request's user from the database.
// Get the user record
$current_user = Yii::$app->user->identity;
// Do something with the user record
return [
'username' => $current_user->username,
'last_update' => $current_user->updated_at
...
];

How do I save a password hash so I can match an already hashed password?

I am implementing a "remember me" feature (CakePHP 3) that stores username and a hashed password in a cookie. My problem is that I am not successful in matching the hashed password in the cookie with the hashed password in the database.
I am using the DefaultPasswordHasher to generate both the database hash and the cookie hash. I initially thought these would match, but this doesn't seem to be the case.
My second thought was that the DefaultPasswordHasher would have a function that could verify that both hashes came from the same password, but its check() function takes a plain text password: https://api.cakephp.org/3.3/class-Cake.Auth.DefaultPasswordHasher.html
Similar questions online all seem to apply to older versions of Cake or are inconclusive.
From the User entity:
protected function _setPassword($password)
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher)->hash($password);
}
}
From the UsersController login() function
// Remember login?
if ($this->request->getData()['remember_me'] == "true") {
// Hash the user's password
$savedUser = [
'password' => (new DefaultPasswordHasher)->hash($this->request->getData()['password']),
'username' => $this->request->getData()['username']
];
// Save login for 1 month
$result = $this->Cookie->write('cookie_name', $savedUser, true, '1 month');
}
From the AppController initialize() function:
// Load logged in user
$loggedInUserID = $this->Auth->user('id');
// If not logged in try cookie
if( !$loggedInUserID && $cookie = $this->Cookie->read('cookie_name') ) {
error_log("Attempting to log in from cookie. Username: ".$cookie['username']);
$user = $this->Users->find('all', [
'conditions' => [
'username' => $cookie['username'],
'password' => $cookie['password'],
'is_deleted' => 0
]
])
->first();
// If a user was found, try to login
if ($user) {
if($this->Auth->login($user)) {
error_log("Successfully logged in from cookie. Username: ".$cookie['username']);
$loggedInUserID = $this->Auth->user('id');
} else {
error_log("Couldn't log in from cookie. Username: ".$cookie['username']);
$this->redirect('/users/logout'); // destroy session & cookie
}
}
}
(I kept my temporary error log messages for clarity.)
The cookie seems to be saved correctly, but its hash does not match the database, which means a user matching the cookie is never found.
Is the problem that the two hashes should match? Or should I be using a function to match the two hashes with each other?
First things first, you may want to have a look at the new authentication plugin, it ships with a cookie authenticator out of the box!
https://book.cakephp.org/authentication/1.1/en/
That being said, your premise is wrong, the fact that the two hashes do not match is by design, and the correct behavior, and there is no method that could match them.
What you should do, is store a unique identifier in your cookie, let's say the username, alongside a hash that is built from a second credential, a hash which you need to be able to build again when trying to login via the cookie data. Let's say for example a hash of the username + the hashed password, the hashed password because you need that exact value again to recreate the plain value in order to compare it against the hash stored in the cookie, ie you cannot (and should not anyways) use the plain password, as it wouldn't be available anymore on subsequent requests.
Then when using the cookie data for logging in, use the unique identifier (username) to query the user, and compare the plain value (username + hashed password) to the cookie hash via the password hasher's check() method.
With your given example that could look something like this (disclaimer, this is untested example code):
$user = $this->Users->get($this->Auth->user('id'));
$savedUser = [
'username' => $user->username,
'token' => (new DefaultPasswordHasher())->hash(
$user->username . ':' . $user->password
),
];
// ...
That queries and uses the currently logged in user, so you have to do this after invoking $this->Auth->identify()!
// ...
$user = $this->Users
->find()
->where([
'username' => $cookie['username'],
'is_deleted' => 0,
])
->first();
if ($user) {
$plain = $user->username . ':' . $user->password;
if ((new DefaultPasswordHasher())->check($plain, $cookie['token'])) {
// the hash matches, log the user in
$user = $user->toArray();
unset($user['password']); // but do not put the password in the session
$this->Auth->setUser($user);
} else {
// the hash doesn't match, do something else
}
}
Note the use of setUser(), there is no login() method in the CakePHP 3.x AuthComponent, the login() method is from CakePHP 2.x! Also ideally you'd handle all this in a custom authentication object, and an event handler for the Auth.afterIdentify event, and keep the logic out of the controller.
See also
Cookbook > Controllers > Components > AuthComponent > Manually Logging Users In
Cookbook > Controllers > Components > AuthComponent > Creating Custom Authentication Objects

zfcUser getState in another module

how could i getState from zfcUser
in view/index.phtml i get it from $this->zfcUserIdentity()->getState();
but now i need to get this value ( state for this user who is logged in ), in other module /controller (this is my costum module controller)
so i need to get State from:
zfcUser/Entity/User
to
myModule/Controller
i watch this https://github.com/ZF-Commons/ZfcUser/wiki/How-to-check-if-the-user-is-logged-in but this solutons is not helpful
and this helps too, for me:
$sm = $this->getServiceLocator();
$auth = $sm->get('zfcuserauthservice');
if ($auth->hasIdentity()) {
$user_edit = $auth->getIdentity()->getPrem();
}
The state is a property from the user itself. So if you get the user throught the identification service, you can grab the state from there.
public function myFooAction()
{
if ($this->zfcUserAuthentication()->hasIdentity()) {
$user = $this->zfcUserAuthentication()->getIdentity();
$state = $user->getState();
}
}
Mind that when the user is not logged in, the if condition is false. Also the state can be null or any arbitrary value, so do not expect that every user returns a valid state (in other words, check the returned value!)
follow this code, I had same problem then I have manage how to use identity of logged in user via zfcUser
in other modules controller at topside,
use Zend\EventManager\EventManagerInterface;
then create this two function in the sameclass,
public function setEventManager(EventManagerInterface $events)
{
parent::setEventManager($events);
$controller = $this;
$events->attach('dispatch', function ($e) use ($controller) {
if (is_callable(array($controller, 'checkUserIdentity')))
{
call_user_func(array($controller, 'checkUserIdentity'));
}
}, 100);
}
public function checkUserIdentity()
{
if ($this->zfcUserAuthentication()->hasIdentity()) {
echo "<pre>"; print_r($this->zfcUserAuthentication()->getIdentity());die;
}
}
it will give this kind of output
Admin\Entity\User Object
(
[id:protected] => 2
[username:protected] =>
[email:protected] => rajat.modi#softwebsolutions.com
[displayName:protected] =>
[password:protected] => $2y$14$2WxYLE0DV0mH7txIRm7GkeVJB3mhD4FmnHmxmrkOXtUFL7S9PqWy6
[state:protected] =>
)
That's it you will automatically get Identity whether user is logged in if not then it will redirect to login page.
Hope this will helps

How to make entry of user when password is wrong

In yii i am creating login module. I want to give access to users only if username and passwords are correct and need to store this user's id into 'Success' table. But when only username is correct and password is wrong,i want to store that user's id into 'attempt' table in order to give only 3 chance to him for entering correct password. But when i am implementing this,when password is wrong entry doesnt get enterd into attempt table. i had created ActionLogin() method as-
public function actionLogin()
{
$model=new LoginForm;
$command = Yii::app()->db->createCommand();
// if it is ajax validation request
if(isset($_POST['ajax']) && $_POST['ajax']==='login-form')
{
echo CActiveForm::validate($model);
Yii::app()->end();
}
// collect user input data
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
// validate user input and redirect to the previous page if valid
if ($model->validate())
{
if($model->login()) {
$command->insert('attempt', array(
'id'=>Yii::app()->user->getId(),
));
$this->redirect(Yii::app()->user->returnUrl);
}
if(!$model->login())
{
$command->insert('attempt', array(
'id'=>Yii::app()->user->getId(),
));
}
}
}
// display the login form
$this->render('login',array('model'=>$model));
}
What changes i need to do in order to make entry of user's id into attempt table when password is wrong. Please help me.
As stated in the comments:
A user that is visiting a Yii based website doesn't have a user id assigned, only logged in users have a userId assigned (which makes sense...).
The best approach would be to do IP based checks on the login attempts. You can retrieve a user his IP by the following piece of code:
Yii::app()->request->userHostAddress
Also credits for this answer to #Stu and #Nikos-Tsirakis