agielks/yii2-jwt

JWT based on Icobucci version 4.1

Installs: 1 439

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

Type:yii2-extension

1.0.0 2022-07-13 05:18 UTC

This package is auto-updated.

Last update: 2024-10-19 11:05:35 UTC


README

This extension provides the JWT integration for the Yii framework 2.0 (requires PHP 8.0+). It includes basic HTTP authentication support.

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

Table of contents

  1. Installation
  2. Dependencies
  3. Basic usage
    1. Create token
    2. Parse token from string
    3. Validate token
  4. Login Example

Instalation

Package is available on Packagist, you can install it using Composer.

composer require agielks/yii2-jwt ~1.0

or add to the require section of your composer.json file.

"agielks/yii2-jwt": "~1.0"

Dependencies

Basic Usage

Add jwt component to your configuration file,

'components' => [
    'jwt' => [
        'class' => \agielks\yii2\jwt\Jwt::class,
        // 'singer' => new \Lcobucci\JWT\Signer\Hmac\Sha256(),
        'signer' => 'HS256',
        // 'key' => \Lcobucci\JWT\Signer\Key\InMemory::plainText('my-key'),
        'key' => 'my-key', ,
    ],
],

Important: If you don't provide the signer and the key it will use unsecured signer

Configure the authenticator behavior as follows.

namespace app\controllers;

class SiteController extends \yii\rest\Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => \agielks\yii2\jwt\JwtBearerAuth::class,
        ];

        return $behaviors;
    }
}

Also you can use it with CompositeAuth reffer to a doc.

Create Token

/* @var $jwt \agielks\yii2\jwt\Jwt */

$now = new DateTimeImmutable();
$jwt = Yii::$app->get('jwt');

$token = $jwt
    ->builder()
    // Configures the issuer (iss claim)
    ->issuedBy('http://example.com')
    // Configures the audience (aud claim)
    ->permittedFor('http://example.org')
    // Configures the id (jti claim)
    ->identifiedBy('62cbfaca6bf7e')
    // Configures the time that the token was issue (iat claim)
    ->issuedAt($now)
    // Configures the time that the token can be used (nbf claim) required for StrictValidAt constraint
    ->canOnlyBeUsedAfter($now)
    // Configures the expiration time of the token (exp claim)
    ->expiresAt($now->modify('+1 hour'))
    // Configures a new claim, called "uid"
    ->withClaim('uid', '62cbfaca6bf7e')
    // Configures a new header, called "foo"
    ->withHeader('foo', 'bar')
    // Builds a new token
    ->getToken($jwt->signer(), $jwt->key());

// Retrieves all headers
$token->headers()->all();

// Retrives typ from headers
$token->headers()->get('typ');

// Print typ from headers
print_r($token->headers()->get('typ'));

// Retrieves all claims
$token->claims()->all();

// Retrieves jti from claims
$token->claims()->get('jti');

// Print jti from claims
print_r($token->claims()->get('jti'));

Parse Token From String

/* @var $jwt \agielks\yii2\jwt\Jwt */

$now = new DateTimeImmutable();
$jwt = Yii::$app->get('jwt');

$token = $jwt
    ->builder()
    // ...
    ->expiresAt($now->modify('+1 hour'))
    ->getToken($jwt->signer(), $jwt->key())
    ->toString();

// Parse without validation
$data = $jwt->config()->parser()->parse($token);

// Parse with validation
$data = $jwt->load($token);

// Print all headers
print_r($data->headers()->all());

// Print all claims
print_r($data->claims()->all());

// Validate token
var_dump($data->isExpired($now));
var_dump($data->isExpired($now->modify('+2 hour')));

Validate Token

You can configure your own validation with simple configuration in your component

use \agielks\yii2\jwt\Jwt;
use \Lcobucci\JWT\Signer\Hmac\Sha256;
use \Lcobucci\JWT\Signer\Key\InMemory;
use \Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use \Lcobucci\JWT\Validation\Constraint\SignedWith;
use \Lcobucci\JWT\Validation\Constraint\IdentifiedBy;
use \Lcobucci\Clock\SystemClock;

'components' => [
    'jwt' => [
        'class' => Jwt::class,
        'signer' => new Sha256(),
        'key'   => InMemory::plainText('my-key'),
        'constraints' => [
            new LooseValidAt(SystemClock::fromSystemTimezone()),
            new SignedWith(
                new Sha256(),
                InMemory::plainText('my-key')
            ),
            new IdentifiedBy('my-identity'),
        ],
    ],
],

Login Example

Basic scheme

  1. Client send credentials. For example, login + password
  2. App validate the credentials
  3. If credentials is valid client receive token
  4. Client store token for the future requests

Step by step usage

  1. Install component
composer require agielks/yii2-jwt ~1.0
  1. Update your components configuration
'components' => [
    // other components here...
    'jwt' => [
        'class' => \agielks\yii2\jwt\Jwt::class,
        // 'singer' => new \Lcobucci\JWT\Signer\Hmac\Sha256(),
        'signer' => 'HS256',
        // 'key' => \Lcobucci\JWT\Signer\Key\InMemory::plainText('my-key'),
        'key' => 'my-key', ,
    ],
    // ...
],
  1. Change method User::findIdentityByAccessToken()
/**
 * {@inheritdoc}
 * @param \Lcobucci\JWT\Token $token
 */
public static function findIdentityByAccessToken($token, $type = null)
{
   return static::findOne(['id' => $token->claims()->get('uid')]);
}

If you want to use auth_key as key, update method as follows

/**
 * {@inheritdoc}
 * @param \Lcobucci\JWT\Token $token
 */
public static function findIdentityByAccessToken($token, $type = null)
{
   return static::findOne(['auth_key' => $token->claims()->get('auth_key')]);
}
  1. Create controller
use agielks\yii2\jwt\JwtBearerAuth;
// Use your own login form
use common\models\LoginForm;
use DateTimeImmutable;
use Yii;
use yii\base\InvalidConfigException;
use yii\filters\Cors;
use yii\rest\Controller;
use yii\web\Response;

/**
 * Class SiteController
 */
class SiteController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_JSON;
        $behaviors['corsFilter'] = ['class' => Cors::class];
        $behaviors['authenticator'] = [
            'class' => JwtBearerAuth::class,
            'optional' => [
                'login',
            ],
        ];

        return $behaviors;
    }

    /**
     * {@inheritdoc}
     */
    protected function verbs()
    {
        return [
            'login' => ['OPTIONS', 'POST'],
        ];
    }

    /**
     * @return array|LoginForm
     * @throws InvalidConfigException
     */
    public function actionLogin()
    {
        $model = new LoginForm();

        if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) {
            /* @var $jwt \agielks\yii2\jwt\Jwt */

            $now = new DateTimeImmutable();
            $jwt = Yii::$app->get('jwt');
            $user = $model->getUser();

            return $jwt
                ->builder()
                // Configures the issuer (iss claim)
                ->issuedBy('http://example.com')
                // Configures the audience (aud claim)
                ->permittedFor('http://example.org')
                // Configures the id (jti claim)
                ->identifiedBy($user->id)
                // Configures the time that the token was issue (iat claim)
                ->issuedAt($now)
                // Configures the time that the token can be used (nbf claim)
                ->canOnlyBeUsedAfter($now)
                // Configures the expiration time of the token (exp claim)
                ->expiresAt($now->modify('+1 hour'))
                // Configures a new claim, called "uid"
                ->withClaim('uid', $user->id)
                // Configures a new claim, called "auth_key"
                ->withClaim('auth_key', $user->auth_key)
                // Returns a signed token to be used
                ->getToken($jwt->signer(), $jwt->key())
                // Convert token to string
                ->toString();
        }

        $model->validate();
        return $model;
    }

    /**
     * Test authentication
     */
    public function actionTest()
    {
        return ['auth' => 'success'];
    }
}