User Authentication in Yii2

1. Introduction

User authentication is a fundamental feature in web applications to verify user identities. Yii2 provides a built-in authentication system using identity classes, login forms, and session-based authentication. In this tutorial, we will cover:

  • Setting up authentication in Yii2
  • Implementing user login and logout
  • Managing authentication using yii\web\User
  • Using IdentityInterface for user authentication
  • Implementing "Remember Me" functionality
  • Securing authentication with hashing

2. Setting Up Authentication

Yii2 uses the User component and an identity class that implements IdentityInterface to handle authentication. By default, Yii2 provides a User model in the common\models\User for advanced templates or app\models\User for basic templates.

User Table Migration

If you don’t already have a user table, create one with the following structure:

CREATE TABLE `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `username` VARCHAR(255) NOT NULL,
    `email` VARCHAR(255) NOT NULL UNIQUE,
    `password_hash` VARCHAR(255) NOT NULL,
    `auth_key` VARCHAR(32) NOT NULL,
    `access_token` VARCHAR(255) DEFAULT NULL,
    `status` TINYINT(1) NOT NULL DEFAULT 1,
    `created_at` INT NOT NULL,
    `updated_at` INT NOT NULL
);

User Model (User.php)

Modify your User model to implement IdentityInterface:

namespace app\models;

use Yii;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;

class User extends ActiveRecord implements IdentityInterface
{
    public static function tableName()
    {
        return 'user';
    }

    public static function findIdentity($id)
    {
        return static::findOne($id);
    }

    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }

    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username]);
    }

    public function getId()
    {
        return $this->id;
    }

    public function getAuthKey()
    {
        return $this->auth_key;
    }

    public function validateAuthKey($authKey)
    {
        return $this->auth_key === $authKey;
    }

    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }
}

3. Implementing Login Functionality

LoginForm Model (LoginForm.php)

namespace app\models;

use Yii;
use yii\base\Model;

class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;

    private $_user = false;

    public function rules()
    {
        return [
            [['username', 'password'], 'required'],
            ['rememberMe', 'boolean'],
            ['password', 'validatePassword'],
        ];
    }

    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }

    public function login()
    {
        if ($this->validate()) {
            return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
        }
        return false;
    }

    protected function getUser()
    {
        if ($this->_user === false) {
            $this->_user = User::findByUsername($this->username);
        }
        return $this->_user;
    }
}

4. Creating Login Action in Controller

Modify your SiteController.php:

namespace app\controllers;

use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;
use app\models\LoginForm;

class SiteController extends Controller
{
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::class,
                'only' => ['logout'],
                'rules' => [
                    [
                        'actions' => ['logout'],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                ],
            ],
        ];
    }

    public function actionLogin()
    {
        if (!Yii::$app->user->isGuest) {
            return $this->goHome();
        }

        $model = new LoginForm();
        if ($model->load(Yii::$app->request->post()) && $model->login()) {
            return $this->goBack();
        }

        return $this->render('login', ['model' => $model]);
    }

    public function actionLogout()
    {
        Yii::$app->user->logout();
        return $this->goHome();
    }
}

5. Creating Login View (views/site/login.php)

use yii\helpers\Html;
use yii\bootstrap\ActiveForm;

$this->title = 'Login';
?>

<div class="site-login">
    <h1><?= Html::encode($this->title) ?></h1>

    <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

        <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
        <?= $form->field($model, 'password')->passwordInput() ?>
        <?= $form->field($model, 'rememberMe')->checkbox() ?>

        <div class="form-group">
            <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
        </div>

    <?php ActiveForm::end(); ?>
</div>

6. Protecting Routes with Authentication

To restrict pages to authenticated users, modify the controllers/SiteController.php file:

public function behaviors()
{
    return [
        'access' => [
            'class' => AccessControl::class,
            'only' => ['dashboard'],
            'rules' => [
                [
                    'actions' => ['dashboard'],
                    'allow' => true,
                    'roles' => ['@'], // Only authenticated users
                ],
            ],
        ],
    ];
}

7. Advanced Authentication with Access Control Filters (ACF)

To protect entire controllers, add AccessControl to behaviors():

public function behaviors()
{
    return [
        'access' => [
            'class' => AccessControl::class,
            'rules' => [
                [
                    'allow' => true,
                    'roles' => ['@'],
                ],
            ],
        ],
    ];
}

8. Password Hashing and Security

To hash passwords securely when creating users:

$user = new User();
$user->username = 'admin';
$user->setPassword('securepassword');
$user->generateAuthKey();
$user->save();

To verify passwords when logging in:

if ($user->validatePassword($password)) {
    // Successful authentication
}

Conclusion

We have successfully implemented authentication in Yii2 using identity classes, login forms, session-based authentication, and access control filters. You can now extend this system to include user roles, social logins, and two-factor authentication.