Login user over multiple Requests

Hey,

I have an Yii2 applications, where users able to log in. This works but only for the current request.
The current login is working like that

→ in App-config/user - part enableAutoLogin and enableSession are set to true, identityClass is set to “app\models\User”
→ action site/login instances “app\models\User”, which implements IdentityInterface.
->-> the “app\models\User” authenticates the user and logs the user in, using Yii::$app->user->login($this);
->-> Yii::$app->user->identity has all the stuff I need to work with

But, with the next request, Yii::$app->user->identity is empty, the user seams not logged in.

So my question: is there a build-in function to hold the logged in state of the user, as long as the session is valid?

I thought this happens automatically when session and autolongin are enabled?
Or do I have to implement this myself? For example, set a cookie that says: User is logged in, here are his data?

What should not happen: perform a new authentication for each request.

Thank you.

That’s how it should work by default. The issue sounds like a problem with cookies. May be a localhost domain your browser can’t set cookies for or something similar.

Thanks for the important feedback. So it’s seams, I’m not on an totally wrong way.
The Yii2 app sets 2 cookoies: _csrf token and PHPsession ID. Both comming from localhost. That is correct. The Yii2 app runs on an local docker container.

Should there be an 3rd cookie?

Or should Yii2 math the PHP Session Cookie with the data stored on the servers local filesystem?

Thank you.

//Edit:
After examining the Yii-log, it can be seen that the user-id is port auf the session(variable):
$_SESSION = [
‘__id’ => ‘the-user-id’
but are not automatically taken over as identity.

No if you don’t use “remember me”.

No.

I’d check $_COOKIE contents on each request.

But “remember me” is the feature i like to use. This is the reason, why I set
enableAutoLogin’ => true
The problem was the duration set ==0, to hold the session until the browser is colsed (User, yii\web\User | API Documentation for Yii 2.0 | Yii PHP Framework) But the session ID was changed on ever request. After changeing it to 60000, i get an 3rd cookie: _identity. Seams like this is an important step.

The $_COOKIE holds the PHPSESSION Cookie, where the ID is the same like Yii::$app->session->id but is different to $_SESSION['__id'], which holds the user-id. And (new) an _identity cookie, which holds different things and also the user-id.

After a further request, the cookies exists, but Yii::$app->user->identity is still empty and Yii::$app->user->isGuest is true again.

Summary:
On the first request I set the following User-data aufter authentication and during the login process:

    $this->id = "B0rner" #user-id
    $this->name = "Tom"
    $this->email = "tom@example.com"
    Yii::$app->user->login($this,60000);

The login command results in Yii::$app->user->isGuest= false and Yii::$app->user->identity holds the user-data (id, name, email)

On the next request Yii::$app->user->isGuest = true and Yii::$app->user->identity is not set.

So on the next request $_COOKIE stays the same?

After spending tons of hours with that problem I’m starting building action and model new from scratch with some simple, dummy-like methods, to see how it works and to avoid that the authentication code is the cause.

This is my action:

     public function actionLogin()
     {
         @\Yii::info("isGuest:".serialize( Yii::$app->user->isGuest));
         @\Yii::info("Identitiy: ".serialize(Yii::$app->user->identity));
         $user = new Usercopy;
         $useridentitiy = $user->findIdentityByAccessToken('currentlyNotImportant');
         $user->loginUser($useridentitiy);

         @\Yii::info("Identitiy: ".serialize(Yii::$app->user->identity));
     }

This is my model, from Usercopy.php:

<?php

namespace app\models;

use yii\base\Model;

use Yii;

class Usercopy extends Model implements \yii\web\IdentityInterface
{
    public $id; 
    public $name;

    public static function tableName()
    {
        return 'user';
    }

    /**
     * Finds an identity by the given ID.
     *
     * @param string|int $id the ID to be looked for
     * @return IdentityInterface|null the identity object that matches the given ID.
     */
    public static function findIdentity($id)
    {
        #old: return static::findOne($id);
    }

    /**
     * Finds an identity by the given token.
     *
     * @param string $token the token to be looked for
     * @return IdentityInterface|null the identity object that matches the given token.
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        #old: return static::findOne(['access_token' => $token]);
        return array('ssoid' => "Borner",'name' => 'tom');
    }

    /**
     * @return int|string current user ID
     */
    public function getId()
    {
        #old: return $this->id;
        @\Yii::info("-->getId");
        return $this->id;
    }

    /**
     * @return string|null current user auth key
     */
    public function getAuthKey()
    {
        #old: return $this->auth_key;
    }

    /**
     * @param string $authKey
     * @return bool|null if auth key is valid for current user
     */
    public function validateAuthKey($authKey)
    {
        #old: return $this->getAuthKey() === $authKey;
        @\Yii::info("-->validateAuthKey");
        return true;
    }


    public function loginUser($arrUser)
    {
        $this->id = $arrUser['ssoid'];
        $this->name = $arrUser['name'];
        return Yii::$app->user->login($this,60000);

    }
}

?>

The User-config part is this:

        'user' => [
            'identityClass' => 'app\models\Usercopy',
            'enableAutoLogin' => true,
            'enableSession' => true
        ],

What happens currently:

  • @\Yii::info("isGuest:".serialize( Yii::$app->user->isGuest)); always shows true . My expectation: Guest= false on 2nd request
  • @\Yii::info("Identitiy: ".serialize(Yii::$app->user->identity)); return N before login and the identity after login. My expectation: Show identity in both cases on 2nd request
  • 3 Cookies are set on every request: _csrf, PHPSESSID and _identity
  • the _csrf and PHPSESSID have different values on every request
  • @\Yii::info("-->getId"); - log-info apperas 3 times during one request, triggered from Yii in this line: Yii::$app->user->login($this,60000);

This is my 4th yii application. I never spend so much time in one single problem.
Probably there is something very elementary that I do not understand. Btw: I’m using the Yii docker image.

//Edit: It seams, that Yii::$app->user->login triggers a new PHPSESSID. Without logging the user in, the PHPSESSID is stable. Is this the right behavior?

Yes. Regenerating of session ID on login is expected.

Overall, the case seems to be weird. Let’s do a bisect search dividing the problem into two possible parts:

  1. Environment or code of the framwork.
  2. Application code.

To do that, install plain basic app GitHub - yiisoft/yii2-app-basic: Yii 2.0 Basic Application Template in your environment, Docker etc. There’s login functionality. If it won’t work it’s either framework code (unlikely since it would’ve been reported already) or environment. If it will work, it’s application code. Then you can compare this code to the code from the basic app and look for differences.

So, I build up the login-procedure from scratch an wrote only logging code into the mehtods. Than I added real authentication code in the methods step by step and reading the dokumentation again and again.
Finlay that works!
The reasons why it did not work before were the following:

  • I did not use the Identity Interface correct: I removed the static flag from the findIdentity() method and return falseinstead of null if no Identity was available.
    Because Yii/PHP does not rises an error, I did not notice this mistake.
  • I wasn’t able to match the documentation & the Session<->web/User<->Useridentity construct to my specific sso environment. The docs are abstract and the only examples shows how to work with activeRecord, but not how to implement own authentication-code in Yii2 style. 99% of the Yii2-login-examples on the internet are the same, but I found the last percent! A page, where an Authentication and User-Profile-Handling is implemented without AR. This helps me a lot in understanding the Yii2 doc.

Now I’m at the point where it’s working. However, I still do not understand everything. About in which cases a _identity cookie is set and some other little things.

All in all, many thanks for your help.

B0rner