yii2: Login fails for non-default yii\web\User class

What steps will reproduce the problem?

  1. Create a new class that inherits from yii\web\User. It will be used as a separate system user login:
namespace app\common;

class AdminUser extends yii\web\User
{
    public $loginUrl = ['admin/syslogin'];
    public $identityCookie = [
        'name' => '_identityAdmin',
        'httpOnly' => true
    ];
    public $authTimeoutParam = '__expireAdmin';
    public $enableAutoLogin = false;
    public $enableSession = true;
}
  1. Add a new application component in web.php that refers to the above mentioned class:
'userAdmin' => [
    'class' => 'app\common\AdminUser',
    'identityClass' => 'app\models\SysUser',
],
  1. Add a new Identity class:
<?php

namespace app\models;

use yii\base\BaseObject;
use yii\base\NotSupportedException;
use yii\web\IdentityInterface;

/**
 * Class SysUser
 *
 * @package app\models
 */
class SysUser extends BaseObject implements IdentityInterface
{
    private static $_access = [
        -1 => [
            'id' => -1,
            'username' => 'admin',
            'password' => '<hashed password>',
        ]
    ];
    public $id;
    public $username;
    public $authKey;
    public $accessToken;

    /**
     * @inheritdoc
     */
    public static function findIdentity($id)
    {
        if (array_key_exists($id, self::$_access)) {
            return new static (['id' => $id, 'username' => self::$_access[$id]['username']]);
        }

        return null;
    }

    /**
     * @inheritdoc
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return new NotSupportedException('Not supported');
    }

    /**
     * Finds user by username
     *
     * @param  string $username
     *
     * @return static|null
     */
    public static function findByUsername($username)
    {
        foreach (self::$_access as $v) {
            if ($v['username'] == $username) {
                return new static (['id' => $v['id'], 'username' => $username]);
            }
        }

        return null;
    }

    /**
     * @inheritdoc
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @inheritdoc
     */
    public function getAuthKey()
    {
        return $this->authKey;
    }

    /**
     * @inheritdoc
     */
    public function validateAuthKey($authKey)
    {
        return $this->authKey === $authKey;
    }

    /**
     * Validates password
     *
     * @param  string $password password to validate
     *
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return \Yii::$app->security->validatePassword($password, self::$_access[$this->id]['password']);
    }
}
  1. Add a new login form for that system user:
<?php

namespace app\models;

use Yii;
use yii\base\Model;

/**
 * LoginForm is the model behind the login form.
 */
class SysLoginForm extends Model
{
    public $username;
    public $password;
    private $_user = false;

    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            [['username', 'password'], 'required'],
            ['password', 'validatePassword'],
        ];
    }

    public function attributeLabels()
    {
        return [
            'username' => 'Username',
            'password' => 'Password',
        ];
    }

    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user) {
                $this->addError('username', 'User does not exist.');
            } else {
                if (!$user->validatePassword($this->password)) {
                    $this->addError($attribute, 'The password is incorrect');
                }
            }
        }
    }

    /**
     * Logs in a user using the provided username and password.
     *
     * @return boolean whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            $user = $this->getUser();
            Yii::$app->session->set('username', $user->username);

            return Yii::$app->user->login($user);
        }

        return false;
    }

    /**
     * Finds user by [[username]]
     *
     * @return SysUser|null
     */
    public function getUser()
    {
        if ($this->_user === false) {
            $this->_user = SysUser::findByUsername($this->username);
        }

        return $this->_user;
    }
}
  1. Add a controller action and view that uses all the above just as the standard login form. I won’t paste any code for brevity reasons.

What is the expected result?

The user should successfully log in.

What do you get instead?

The user although logins successfully, he immediately gets logged out and redirected to the Syslogin form.

Additional info

Q A
Yii version >=2.0.44 (until 2.0.43 it works as expected)
PHP version 7.4.25
Operating system Linux

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 19 (19 by maintainers)

Most upvoted comments

I found the time to deal with this again today, with a clearer head. It was my mistake, if you see the code in my SysLoginForm class, inside the login() method, you will see that I had return Yii::$app->user->login($user); instead of the correct:

return Yii::$app->userAdmin->login($user);

Guys, I’m sorry about the noise and all the trouble I made you get into. Of course it works fine with 2.0.45 and there’s no problem whatsoever.

Sorry.

This doesn’t work somehow. I’m puzzled. Today is not a very good day, we were diagnosed Covid-19 positive. Sorry about this, I will try again the next days.

I got it right now. Commit c94d7049c51343c1512772b321005cfeb83e1746 is the problematic one (issue #18646).