Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Authentication

Authentication is checking who is in front of us: a visitor enters a name and a password, the site finds the user and remembers them in the session. In Yii the Yii::$app->user component is responsible for this, and information about a user comes from a class that implements IdentityInterface.

What the project already has

FilePurpose
models/User.phpThe user class; in the project template users are written right in the code
models/LoginForm.phpThe login form model: checks the name and password
views/site/login.phpThe login form view
controllers/SiteController.phpThe login and logout actions

The page /site/login already works with the users admin and demo. To store users in the database it is enough to replace the User class.

The users table

php yii migrate/create create_user_table
public function safeUp()
{
    $this->createTable('user', [
        'id' => $this->primaryKey(),
        'username' => $this->string(64)->notNull()->unique(),
        'password_hash' => $this->string(255)->notNull(),
        'auth_key' => $this->string(32)->notNull(),
    ]);
}

After php yii migrate the user table appears. The password is stored in it not as plain text but as a hash, an irreversibly transformed string.

The user class

Replace the content of models/User.php:

// models/User.php
namespace app\models;
 
use Yii;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
 
class User extends ActiveRecord implements IdentityInterface
{
    public static function findIdentity($id)
    {
        return static::findOne($id);
    }
 
    public static function findIdentityByAccessToken($token, $type = null)
    {
        return null;
    }
 
    public function getId()
    {
        return $this->id;
    }
 
    public function getAuthKey()
    {
        return $this->auth_key;
    }
 
    public function validateAuthKey($authKey)
    {
        return $this->auth_key === $authKey;
    }
 
    public static function findByUsername(string $username): ?self
    {
        return static::findOne(['username' => $username]);
    }
 
    public function validatePassword(string $password): bool
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }
}

The first five methods are required by the interface: with them Yii finds a user by the number from the session and checks the "remember me" key. The last two are called by the ready-made LoginForm. The user class is named in config/web.php: 'user' => ['identityClass' => 'app\models\User'].

Creating a user

$user = new User();
$user->username = 'anna';
$user->password_hash = Yii::$app->security->generatePasswordHash('secret123');
$user->auth_key = Yii::$app->security->generateRandomString();
$user->save();

Run this code once, for example in a temporary action. Now you can log in on the /site/login page as anna with the password secret123.

Login and logout

Login is done by one line, which is already in LoginForm:

Yii::$app->user->login($user, 3600 * 24 * 30);

The second argument is the time in seconds for which the user is remembered after the browser is closed. Logout is Yii::$app->user->logout().

The current user

if (Yii::$app->user->isGuest) {
    return $this->redirect(['site/login']);
}
 
$post->user_id = Yii::$app->user->id;
$name = Yii::$app->user->identity->username;

isGuest is true until the visitor logs in. id is the user number, identity is the User object. This is how the author is recorded when a post is created.

Common mistake

A password must not be stored as plain text and compared with ===: if the database leaks, the passwords of all users are exposed. Store only the result of generatePasswordHash() and check with validatePassword().

Thanks for reading!

Donate to the site — we work hard for you.