JWT integration for Yii 2

Since author of sizeg/ yii2-jwt package is not maintaining it anymore I’ve decided to make a fork of it and keep it clean and up-to-date.

What’s changed in the fork?

  • JwtHttpBearerAuth extends yii\filters\auth\HttpBearerAuth now and properly handles authenticating failures.
  • Jwt component handles all signers provided by lcobucci/jwt package (including RSA and ECDSA).
  • Public key can be provided either as file path or Yii alias.
  • Validation claims can be provided to be tested against.
  • More tests for Travis.
  • Minimum Yii version raised to 2.0.14.
  • Minimum PHP version raised to 7.1.
4 Likes

Nice! Is it registered at https://www.yiiframework.com/extensions?

Yes It Is! :slight_smile:

2 Likes

Would help with this please, the JWT is generated, but I’m using it I get 401 error message. I tried to trace it with xdebug, the findIdentityByAccessToken is been called. The test is done with PostMan.

user model:

		public static function generateJwt ($email, $user_id, $registrationStamp) {

		$jti = $registrationStamp * 4 / 3 + $user_id;
		$uid = $user_id;
		$key = \Yii::$app->jwt->key . $user_id;
		$signer = new Sha512();

		//todo: check the expiry time/--------------------------------------/
		$token = \Yii::$app->jwt->getBuilder()
			->setIssuer(\Yii::$app->params[ 'hostInfo' ])// Configures the issuer (iss claim)
			->setAudience(\Yii::$app->params[ 'hostInfo' ])// Configures the audience (aud claim)
			->setId($jti, true)// Configures the id (jti claim), replicating as a header item
			->setIssuedAt(time())// Configures the time that the token was issue (iat claim)
			->setExpiration(time() + 3600 * 7)// Configures the expiration time of the token (exp claim)
			->set('email', $email)
			->set('uid', $uid)// Configures a new claim, called "uid"
			->sign($signer, $key)// creates a signature using "testing" as key
			->getToken();
		return $token;
	} 
		public static function findIdentityByAccessToken ($token, $type = null) {
		//throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
		foreach ( self::$users as $user ) {
			if ( $user[ 'id' ] === (string) $token->getClaim('uid') ) {
				return new static($user);
			}
		}
		return false;
	}
		public function login ($email, $password) {
		if ( $this->validatePassword($password) AND $this->isUserActive() ) {
			//\Yii::$app->user->login(self::findByEmail($this->email));
			$token = (string) Token::generateJwt($this->email, $this->id, $this->created_at);
			return $token;
		}
		return false;
	}

Api/UserController

		public function behaviors () {
		$behaviors = parent::behaviors();

		$behaviors[ 'authenticator' ] = [
			'class' => \bizley\jwt\JwtHttpBearerAuth::class,
			'only'  => ['test'],
		];
		return $behaviors;
	}
public function actionTest () {
		return ['Test action'];
	}

I’m not sure what is this login() method of yours doing. Is it proper? Could you provide full User model?

The login() method is to validate username & password.
The user model is here https://pastebin.com/a3BXk1CJ (too long to post it here)

I can not find anywhere definition of this static $users variable called in line 77. Is it missing or am I blind?

You right, but method findIdentityByAccessToken in called, I checked this by xdebug.
But how to pass the $user value here, should I make new query? and why there is foreach loop?

Thanks

Oh, I understand now where did you get this code from - it’s the example from sizeg/yii2-jwt original package (I’m not providing such examples). As you can read there sizeg mentions using basic Yii 2 template in that example. The variable comes from here.

I see, my bad, $users is for the none DB users. My code I got from https://github.com/sizeg/yii2-jwt#installation
The users are in the DB, so how to do it?

Thanks,

Well, since all depends on your implementation I can only give you some clues how to do it. Take a look at the tests for extension, this might give you general idea.

Based on that I changed my code, the login method moved to api/UserController

		public function actionLogin () {
		$email = \Yii::$app->request->post('email');
		$password = \Yii::$app->request->post('password');

		if ( !$email or !$password )
			return ['result' => 'error', 'message' => 'Email or password error!'];

		//find the user
		$user = User::findByEmail($email); 
		if ( $user ) {
			//if password valid & account is active
			if ( $user->validatePassword($password) AND $user->isUserActive() ) {
				$token = Token::generateJwt($user->id, $user->created_at);
				User::$token = (string) $token;
				\Yii::$app->request->headers->set('Authorization', "Bearer ".(string)$token);
				return ['result' => 'success', 'message' => (string) $token];
			}
		}
		return ['result' => 'error', 'message' => 'Email or password error!'];
	}

and in the common/User model I changed this:

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

and the method to generate the token:

		public static function generateJwt ($user_id, $registrationStamp) {

		$jti = "1234";//$registrationStamp * 4 / 2 + $user_id;
		$uid = $user_id;
		$key = \Yii::$app->jwt->key;
		$signer = new Sha512();

		//todo: check the expiry time/--------------------------------------/
		$token = \Yii::$app->jwt->getBuilder()
			->setIssuer(\Yii::$app->params[ 'hostInfo' ])// Configures the issuer (iss claim)
			->setAudience(\Yii::$app->params[ 'hostInfo' ])// Configures the audience (aud claim)
			->setId($jti, true)// Configures the id (jti claim), replicating as a header item
			->setIssuedAt(time())// Configures the time that the token was issue (iat claim)
			->setExpiration(time() + 3600 * 7)// Configures the expiration time of the token (exp claim)
			->setSubject($uid)// Configures a new claim, called "uid"
			->sign($signer, $key)// creates a signature using "testing" as key
			->getToken();
		return $token;
	}

But same thing, error 401, maybe I need custom auth?!
I’m using api module to serve the frontend (vue) and the backend.

You are assigning $token to User::$token which is lost immediately in next call (when you try to authenticate). Do you remember that PHP is stateless?

I would like to recommend you to analyze the examples you’ve been given so far to understand how it all works.

I know the the concept, but I don’t know to make with Yii. I tried many things that I wrote any to make works