Search code examples
sessionyii2postmanbasic-authentication

The most simple basic authentication in Yii2 - implementation of IdentityInterface


I would like to add HTTP basic authentication to my Yii2 application in which the username/password is stored in the Yii configuration file - for exactly one user, no DB authentication.

I have enabled authentication for my controller by adding behaviors function:

public function behaviors()
    {
        return [
            'basicAuth' => [
                'class' => \yii\filters\auth\HttpBasicAuth::className(),
                'auth' => function ($username, $password) {
                    if ($username=='api') {
                        return new SimpleApiUser();
                    } else {
                        return null;
                    }
                },
            ],
        ];
    }

And I was required to create class, that implements IdentityInterface, that is why I have class:

class SimpleApiUser implements IdentityInterface {

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

    public static function findIdentityByAccessToken($token, $type = null)
    {
        return null;
    }

    public function getId()
    {
        return 1;
    }

    public function getAuthKey()
    {
        return 1;
    }

    public function validateAuthKey($authKey)
    {
        return true;
    }
}

That is fine, the application asks for username/password in the case of the first request, but then it managed to store the authentication somehow in some internal session and it does not required repeated authentication for each new request be it made from the browser (which my add sessions) or be it from Postman (which certainly does not keep sessions). Is it possibly to modify user class to require to provide username and password with each new request?


Solution

  • I've tried to play around a bit with your code.

    First, your code is enough to require the basic auth to be part of every request. If the Authorization header is not present in request the yii will return 401 Unauthorized. So your implementation is already doing what you need.

    There are reasons why user is not required to enter username/password each time.

    For web browsers: The credentials are saved for session and the browser will send them automatically in each subsequent request without prompting them again from user.

    For postman: The authorization is stored for request as long as you don't manually remove the authorization settings it will be sent as part of each request. postman

    If you want to force users to manually enter username/password with each request you can extend the HttpBasicAuth component to pretend that the authorization was not part of request for every other request like this

    use Yii;
    use yii\filters\auth\HttpBasicAuth;
    
    class MyHttpBasicAuth extends HttpBasicAuth
    {
        const SESSION_KEY = 'authForced';
        private $session;
    
        public function __construct($config = [])
        {
            parent::__construct($config);
            $this->session = Yii::$app->session;
        }
    
        public function authenticate($user, $request, $response)
        {
    
            if ($this->session->has(self::SESSION_KEY)) {
                $this->session->remove(self::SESSION_KEY);
                return parent::authenticate($user, $request, $response);
            }
    
            $this->session->set(self::SESSION_KEY, true);
            return null;
        }
    }
    

    But this will require sessions to work so it can only be used with browsers. It won't work well for API. For postman this implementation would make every other request fail with 401 Unauthorized. It would fail same way for api that will work with cookies and it would fail each request for api that wouldn't work with cookies.

    Side note: The postman does keep/send cookies so sessions works with postman.