2
votes

I have added a custom authentication component for a Yii2 RESTful project and it is validating credentials OK but it is not returning the valid User object to \Yii::$app->user

The component looks like this:

public function authenticate($user, $request, $response) {

    $bearerToken = \Yii::$app->getRequest()->getQueryParam('bearer_token');

    $user = Account::findIdentityByAccessToken($bearerToken);

    return $user;
}

And the Account model method looks like this:

public static function findIdentityByAccessToken($token, $userType = null) {

    return static::findOne(['bearer_token' => $token]);
}

I can see $user is the expected record of Account when debugging in the authenticate() method but \Yii::app()->user seems to be a newly instatiated user. \Yii::app()->user->identity is equal to null.

Can anyone see what I'm doing wrong here?

1

1 Answers

2
votes

To login user this is not enough:

Account::findIdentityByAccessToken($bearerToken);

You need to call $user->login($identity) inside authentificate(). See for example how it's implemented in yii\web\User loginByAccessToken():

public function loginByAccessToken($token, $type = null)
{
    /* @var $class IdentityInterface */
    $class = $this->identityClass;
    $identity = $class::findIdentityByAccessToken($token, $type);
    if ($identity && $this->login($identity)) {
        return $identity;
    } else {
        return null;
    }
}

So you can also call it in your custom auth method:

$identity = $user->loginByAccessToken($accessToken, get_class($this));

See for example how it's implemented in yii\filters\auth\QueryParamAuth.

And you also need to return $identity, not $user. Also handling failure is missing in your code. See how it's implemented in built-in auth methods:

More from official docs:

Update:

Nothing forces you to use loginByAccessToken(), I just mentioned it as an example.

Here is an example of custom auth method that I wrote quite a while ago, not sure if it's 100% safe and true, but I hope it can help you to understand these details:

Custom auth method:

<?php

namespace api\components;

use yii\filters\auth\AuthMethod;

class HttpPostAuth extends AuthMethod
{
    /**
     * @see yii\filters\auth\HttpBasicAuth
     */
    public $auth;

    /**
     * @inheritdoc
     */
    public function authenticate($user, $request, $response)
    {
        $username = $request->post('username');
        $password = $request->post('password');

        if ($username !== null && $password !== null) {
            $identity = call_user_func($this->auth, $username, $password);
            if ($identity !== null) {
                $user->switchIdentity($identity);
            } else {
                $this->handleFailure($response);
            }
            return $identity;
        }

        return null;
    }
}

Usage in REST controller:

/**
 * @inheritdoc
 */
public function behaviors()
{
    $behaviors = parent::behaviors();

    $behaviors['authenticator'] = [
        'class' => HttpPostAuth::className(),
        'auth' => function ($username, $password) {
            $user = new User;
            $user->domain_name = $username;

            // This will validate password according with LDAP
            if (!$user->validatePassword($password)) {
                return null;
            }

            return User::find()->username($username)->one();
        },
    ];

    return $behaviors;
}

Specifying $auth callable is also can be found in HttpBasicAuth.