Code Monkey home page Code Monkey logo

jwtrefreshtokenbundle's Introduction

JWTRefreshTokenBundle

Scrutinizer Code Quality Run Tests Code Coverage Latest Stable Version Total Downloads License StyleCI

The purpose of this bundle is manage refresh tokens with JWT (Json Web Tokens) in an easy way. This bundles uses LexikJWTAuthenticationBundle. Supports Doctrine ORM/ODM.

Prerequisites

This bundle requires PHP 7.4 or later and Symfony 4.4, 5.4, or 6.0+.

For support with older Symfony versions, please use the 0.12 release.

Protip: Though the bundle doesn't force you to do so, it is highly recommended to use HTTPS.

Installation

Step 1: Download the Bundle

You must also install either the Doctrine ORM or MongoDB ODM, these packages are not installed automatically with this bundle. Failing to do so may trigger errors on installation.

If using Symfony 4.4, you will also need to install the symfony/security-guard package, it is only required for the legacy authentication API and is not compatible with Symfony 6.0.

With Doctrine's ORM

composer require doctrine/orm doctrine/doctrine-bundle gesdinet/jwt-refresh-token-bundle

With Doctrine's MongoDB ODM

composer require doctrine/mongodb-odm doctrine/mongodb-odm-bundle gesdinet/jwt-refresh-token-bundle

Or, manually edit your project's composer.json file to add the required packages:

{
  "require": {
    "doctrine/doctrine-bundle": "^2.0",
    "doctrine/mongodb-odm": "^2.0",
    "doctrine/mongodb-odm-bundle": "^4.0",
    "doctrine/orm": "^2.7",
    "gesdinet/jwt-refresh-token-bundle": "^1.0"
  }
}

Alternatively, a custom persistence layer can be used.

For that purpose, you must:

Step 2: Enable the Bundle

Symfony Flex Application

For an application using Symfony Flex the bundle should be automatically registered, but if not you will need to add it to your config/bundles.php file.

<?php

return [
    //...
    Gesdinet\JWTRefreshTokenBundle\GesdinetJWTRefreshTokenBundle::class => ['all' => true],
];

Step 3: Configure the Bundle

Symfony Flex Application

For an application using Symfony Flex, a recipe should have been applied to your application. If not, you will need to make the following changes:

  1. Configure the refresh token class. Create the config/packages/gesdinet_jwt_refresh_token.yaml file with the below contents:
gesdinet_jwt_refresh_token:
    refresh_token_class: App\Entity\RefreshToken # This is the class name of the refresh token, you will need to adjust this to match the class your application will use
  1. Create the object class.

If you are using the Doctrine ORM, the below contents should be placed at src/Entity/RefreshToken.php (use annotations OR attributes):

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as BaseRefreshToken;

/**
 * @ORM\Entity
 * @ORM\Table("refresh_tokens")
 */
#[ORM\Entity]
#[ORM\Table(name: 'refresh_tokens')]
class RefreshToken extends BaseRefreshToken
{
}

If you are using the Doctrine MongoDB ODM, the below contents should be placed at src/Document/RefreshToken.php (remember to update the refresh_token_class configuration above to match):

<?php

namespace App\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken as BaseRefreshToken;

/**
 * @ODM\Document(collection="refresh_tokens")
 */
class RefreshToken extends BaseRefreshToken
{
}

Step 4 (Symfony 5.4+)

Define the refresh token route

Open your routing configuration file and add the following route to it:

# config/routes.yaml
api_refresh_token:
    path: /api/token/refresh
# ...

Configure the authenticator

To enable the authenticator, you should add it to your API firewall(s) alongside the json_login and jwt authenticators.

The complete firewall configuration should look similar to the following:

# config/packages/security.yaml
security:
    # this config is only required on Symfony 5.4, you can leave it out on Symfony 6
    enable_authenticator_manager: true

    firewalls:
        api:
            pattern: ^/api
            stateless: true
            entry_point: jwt
            json_login:
                check_path: /api/login # or, if you have defined a route for your login path, the route name you used
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
            jwt: ~
            refresh_jwt:
                check_path: /api/token/refresh # or, you may use the `api_refresh_token` route name
                # or if you have more than one user provider
                # provider: user_provider_name
    # ...

    access_control:
        # ...
        - { path: ^/api/(login|token/refresh), roles: PUBLIC_ACCESS }
        # ...
# ...

Step 4 (Symfony 4.4)

Define the refresh token route

Open your routing configuration file and add the following route to it:

# config/routes.yaml
api_refresh_token:
    path:       /api/token/refresh
    controller: gesdinet.jwtrefreshtoken::refresh
# ...

Configure the security firewall

Add the below to your security configuration file:

# config/packages/security.yaml
security:
    firewalls:
        # put it before all your other firewall API entries
        refresh:
            pattern:  ^/api/token/refresh
            stateless: true
            anonymous: true
            # or if you have more than one user provider
            #provider: user_provider_name
    # ...

    access_control:
        # ...
        - { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        # ...
# ...

Step 5: Update your database schema

You will need to add the table for the refresh tokens to your application's database.

With migrations:

# If using the MakerBundle:
php bin/console make:migration
# Without the MakerBundle:
php bin/console doctrine:migrations:diff

php bin/console doctrine:migrations:migrate

Without migrations (NOT RECOMMENDED):

php bin/console doctrine:schema:update --force

Usage

The below options can be configured through the bundle's configuration in the config/packages/gesdinet_jwt_refresh_token.yaml file (make sure to create it if it does not already exist).

Token TTL

You can define the refresh token TTL, this value is set in seconds and defaults to 1 month. You can change this value adding this line to your config:

gesdinet_jwt_refresh_token:
    ttl: 2592000

Update Token TTL

You can configure the bundle to refresh the TTL on a refresh token when it is used, by default this feature is disabled. You can change this value adding this line to your config:

gesdinet_jwt_refresh_token:
    ttl_update: true

Config Firewall Name

NOTE This setting is deprecated and is not used with the refresh_jwt authenticator

You can define Firewall name. Default value is api. You can change this value adding this line to your config:

gesdinet_jwt_refresh_token:
    firewall: api

Refresh Token Parameter Name

You can define the parameter name for the refresh token when it is read from the request, the default value is refresh_token. You can change this value adding this line to your config:

gesdinet_jwt_refresh_token:
    token_parameter_name: refreshToken

Return Expiration Timestamp

If set to true, the expiration Unix timestamp will be added to the response.

gesdinet_jwt_refresh_token:
    return_expiration: true

The default parameter name is refresh_token_expiration. You can change the parameter name by adding this line to your config and changing it:

gesdinet_jwt_refresh_token:
    return_expiration_parameter_name: refresh_token_expiration

Set The User Provider

Symfony 5.4+

You can define a user provider to use for the authenticator its configuration.

Note, if your application has multiple user providers, you MUST configure this value for either the firewall or the provider.

# app/config/security.yml or config/packages/security.yaml
security:
    firewalls:
        api:
            pattern: ^/api
            stateless: true
            entry_point: jwt
            json_login: ~
            jwt: ~
            refresh_jwt:
                check_path: /api/token/refresh
                provider: user_provider_service_id

By default, when a user provider is not specified, then the user provider for the firewall is used instead.

Symfony 4.4

NOTE This setting is deprecated and is not used with the refresh_jwt authenticator

You can define your own user provider, by default the gesdinet.jwtrefreshtoken.user_provider service is used. You can change this value by adding this line to your config:

gesdinet_jwt_refresh_token:
    user_provider: user_provider_service_id

For example, if you are using FOSUserBundle, user_provider must be set to fos_user.user_provider.username_email.

For Doctrine ORM UserProvider, user_provider must be set to security.user.provider.concrete.<your_user_provider_name_in_security_yaml>.

For example, in your config/packages/security.yaml file:

security:
    # ...
    providers:
        app_user_provider:
            # ...
    firewalls:
    # ...
# ...

then your user_provider_service_id is security.user.provider.concrete.app_user_provider.

Set The User Checker

Symfony 5.4+

You can define a user checker to use for the firewall as part of the firewall configuration:

# app/config/security.yml or config/packages/security.yaml
security:
    firewalls:
        api_token_refresh:
            pattern: ^/api/token/refresh
            stateless: true
            user_checker: user_checker_service_id
            refresh_jwt: ~

Symfony 4.4

NOTE This setting is deprecated and is not used with the refresh_jwt authenticator

You can define your own user checker, by default the security.user_checker service is used. You can change this value by adding this line to your config:

gesdinet_jwt_refresh_token:
    user_checker: user_checker_service_id

You will probably want to use a custom user provider along with your user checker to ensure that the checker receives the right type of user.

Single Use Tokens

You can configure the refresh token so it can only be consumed once. If set to true and the refresh token is consumed, a new refresh token will be provided.

To enable this behavior add this line to your config:

gesdinet_jwt_refresh_token:
    single_use: true

Set the refresh token in a cookie

By default, the refresh token is returned in the body of a JSON response. You can use the following configuration to set it in a HttpOnly cookie instead. The refresh token is automatically extracted from the cookie during refresh.

To allow users to logout when using cookies, you need to configure the LogoutEvent to trigger on a specific route, and call that route during logout.

gesdinet_jwt_refresh_token:
    cookie:
      enabled: true
      same_site: lax               # default value
      path: /                      # default value
      domain: null                 # default value
      http_only: true              # default value
      secure: true                 # default value
      partitioned: false           # default value
      remove_token_from_body: true # default value

Invalidate refresh token on logout

This bundle automatically registers an EventListener which triggers on LogoutEvents from a specific firewall (default: api).

The LogoutEventListener automatically invalidates the given refresh token and, if enabled, unsets the cookie. If no refresh token is supplied, an error is returned and the cookie remains untouched. If the supplied refresh token is (already) invalid, the cookie is unset.

All you have to do is make sure the LogoutEvent triggers on a specific route, and call that route during logout:

# in security.yaml
security:
    firewalls:
        api:
            logout:
                path: api_token_invalidate
# in routes.yaml
api_token_invalidate:
    path: /api/token/invalidate

If you want to configure the LogoutEvent to trigger on a different firewall, the name of the firewall has to be configured:

# in security.yaml
security:
    firewalls:
        myfirewall:
            logout:
                path: api_token_invalidate
# in routes.yaml
api_token_invalidate:
    path: /api/token/invalidate
# in gesdinet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
    logout_firewall: myfirewall

Doctrine Manager Type

By default, the bundle will try to set the appropriate Doctrine object manager for your application using the following logic to define the manager type:

  • If the manager_type configuration key is set to "mongodb", the MongoDB ODM is used
  • If the manager_type configuration key is set to "orm" (default), and the ORM is not installed but the MongoDB ODM is installed, the MongoDB ODM is used
  • By default, the manager_type is "orm" and the ORM is used

You can customize the manager type using the manager_type configuration:

gesdinet_jwt_refresh_token:
    manager_type: mongodb

Use another object manager

You can configure the bundle to use any object manager using the object_manager configuration. Note, an explicitly defined object_manager configuration will override any automatic configuration based on the manager_type.

gesdinet_jwt_refresh_token:
    object_manager: my.specific.entity_manager.id

Use another class for refresh tokens

You can define your own refresh token class for your project by creating a class extending from the classes provided by this bundle. This also allows you to customize the refresh token, i.e. to add extra data to the token.

When using the Doctrine ORM, create a class extending Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken in your application:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken;

/**
 * This class extends Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken to have another table name.
 *
 * @ORM\Table("jwt_refresh_token")
 */
class JwtRefreshToken extends RefreshToken
{
}

When using the Doctrine MongoDB ODM, create a class extending Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken in your application:

<?php

namespace App\Document;

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken;

/**
 * This class extends Gesdinet\JWTRefreshTokenBundle\Document\RefreshToken to have another collection name.
 *
 * @MongoDB\Document(collection="jwt_refresh_token")
 */
class JwtRefreshToken extends RefreshToken
{
}

Then declare this class adding this line to your config.yml file:

gesdinet_jwt_refresh_token:
    refresh_token_class: App\Entity\JwtRefreshToken

NOTE If using another object manager, it is recommended your object class extends from Gesdinet\JWTRefreshTokenBundle\Model\AbstractRefreshToken which implements all required methods from Gesdinet\JWTRefreshTokenBundle\Model\RefreshTokenInterface.

Disable automatic Doctrine mappings

NOTE: This setting is deprecated and is no longer used

On some occasions, you may not want to have default Doctrine mappings of object manager enabled as you use neither ORM nor ODM but i.e. using DoctrineBundle for DBAL.

To disable dynamic Doctrine mapping add this line to your config:

gesdinet_jwt_refresh_token:
    doctrine_mappings: false

Generating Tokens

When you authenticate through /api/login_check with user/password credentials, LexikJWTAuthenticationBundle now returns a JWT Token and a Refresh Token data.

{
  "token": "eyxxxGciOiJSUzI1NiIsInR5cCI6IkpXUyJ9.eyJleHAiOjE0NDI0MDM3NTgsImVtYWlsIjoid2VibWFzdGVyQGdlc2RpbmV0LmNvbSIsImlhdCI6IjE0NDI0MDM3MzgifQ.bo5pre_v0moCXVOZOj-s85gVnBLzdSdsltPn3XrkmJaE8eaBo_zcU2pnjs4dUc9hhwNZK8PL6SmSNcQuTUj4OMK7sUDfXr62a05Ds-UgQP8B2Kpc-ZOmSts_vhgo6xJNCy8Oub9-pRA_78WzUUxt294w0IArrNlgQAGewk65RSMThOif9G6L7HzBM4ajFZ-kMDypz2zVQea1kry-m-XXKNDbERCSHnMeV3rANN48SX645_WEvwaHy0agChR4hTnThzLof2bShA7j7HmnSPpODxQszS5ZBHdMgTvYhlcWJmwYswCWCTPl3lsqVq_UOFI5_4arpSNlUwZsichqxXVAHX5idZqCWtoaqAbvNQe2IpinYajoXw-MlYKvcN2TLUF_8sy529olLUagf4FCpCO6JFxovv0E7ll9tUOVvx9LlannqV8976q5XCOoXszKonZSH7DhsBlW5Emjv7PailbARZ-hfl4YlamyY2QbnxAswYycfoxqJxbbIKYGA8dlebdvMyC7m9VATnasTuKeEKS3mP5iyDgWALBHNYXm1FM-12zHBdN3PbOgxmy_OBGvk05thYFEf2WVmyedtFHy4TGlI0-otUTAf2swQAXWhKtkLWzokWWF7l5iNzam1kkEgql5EOztXHDZpmdKVHWBVNvN3J5ivPjjJBm6sGusf-radcw",
  "refresh_token": "xxx00a7a9e970f9bbe076e05743e00648908c38366c551a8cdf524ba424fc3e520988f6320a54989bbe85931ffe1bfcc63e33fd8b45d58564039943bfbd8dxxx"
}

The refresh token is persisted as a RefreshTokenInterface object. After that, when your JWT valid token expires, if you want to get a new one you can proceed in two ways:

  • Send you user credentials again to /api/login_check. This generates another JWT with another Refresh Token.
  • Ask to renew valid JWT with our refresh token. Make a POST call to /api/token/refresh url with refresh token as payload. In this way, you can always get a valid JWT without asking for user credentials. But you must check if the refresh token is still valid. Your refresh token will not change but its TTL will increase.

Note that when a refresh token is consumed and the config option single_use is set to true the token will no longer be valid.

curl -X POST -d refresh_token="xxxx4b54b0076d2fcc5a51a6e60c0fb83b0bc90b47e2c886accb70850795fb311973c9d101fa0111f12eec739db063ec09d7dd79331e3148f5fc6e9cb362xxxx" 'http://xxxx/token/refresh'

This call returns a new valid JWT token renewing valid datetime of your refresh token.

Useful Commands

Revoke all invalid tokens

If you want to revoke all invalid (datetime expired) refresh tokens you can execute:

php bin/console gesdinet:jwt:clear

The command optionally accepts a date argument which will delete all tokens older than the given time. This can be any value that can be parsed by the DateTime class.

php bin/console gesdinet:jwt:clear 2015-08-08

We recommend executing this command as a cronjob to remove invalid refresh tokens on an interval.

Revoke a token

If you want to revoke a single token you can use this command:

php bin/console gesdinet:jwt:revoke TOKEN

Events

Token Refreshed

When a token is refreshed, the gesdinet.refresh_token event is dispatched with a Gesdinet\JWTRefreshTokenBundle\Event\RefreshEvent object.

Refresh Token Failure

NOTE This event is only available when using the refresh_jwt authenticator with Symfony 5.4+.

When there is a failure authenticating the refresh token, the gesdinet.refresh_token_failure event is dispatched with a Gesdinet\JWTRefreshTokenBundle\Event\RefreshAuthenticationFailureEvent object.

Refresh Token Not Found

NOTE This event is only available when using the refresh_jwt authenticator with Symfony 5.4+.

When there is a failure authenticating the refresh token, the gesdinet.refresh_token_not_found event is dispatched with a Gesdinet\JWTRefreshTokenBundle\Event\RefreshTokenNotFoundEvent object.

Token Extractor

The bundle provides a Gesdinet\JWTRefreshTokenBundle\Request\Extractor\ExtractorInterface to define classes which can read the refresh token from the request.

By default, the Gesdinet\JWTRefreshTokenBundle\Request\Extractor\ChainExtractor is used which allows checking multiple aspects of the request for a token. The first token found will be used.

You can create a custom extractor by adding a class to your application implementing the interface. For example, to add an extractor checking for a "X-Refresh-Token" header:

<?php

namespace App\Request\Extractor;

use Gesdinet\JWTRefreshTokenBundle\Request\Extractor\ExtractorInterface;
use Symfony\Component\HttpFoundation\Request;

final class HeaderExtractor implements ExtractorInterface
{
    public function getRefreshToken(Request $request, string $parameter): ?string
    {
        return $request->headers->get('X-Refresh-Token');
    }
}

This bundle handles automatically configuring ExtractorInterface objects and will automatically set the gesdinet_jwt_refresh_token.request_extractor container tag when your application uses autoconfiguration (autoconfigure: true in your services.yaml file). If autoconfiguration is not in use, you will need to manually configure the tag:

services:
    App\Request\Extractor\HeaderExtractor:
        tags:
            - { name: gesdinet_jwt_refresh_token.request_extractor }

Prioritizing Extractors

The gesdinet_jwt_refresh_token.request_extractor container tag supports prioritizing extractors, you can use this to set the preferred order for your extractors by adding a priority attribute. The higher the number, the sooner the extractor will be run.

services:
    App\Request\Extractor\HeaderExtractor:
        tags:
            - { name: gesdinet_jwt_refresh_token.request_extractor, priority: 25 }

jwtrefreshtokenbundle's People

Contributors

cezarystepkowski avatar chalasr avatar chosroes avatar chris53897 avatar derzkiy avatar digitalkaoz avatar dragosprotung avatar elgovanni avatar fastnloud avatar fd6130 avatar gregurco avatar heer0 avatar igorut avatar jayfrown avatar jdecool avatar juliangut avatar kix avatar markitosgv avatar mbabker avatar misaert avatar norkunas avatar paulchubatyy avatar piotrkreft avatar razachohan avatar ricardodevries avatar sandermarechal avatar sh41 avatar sylvainmetayer avatar webignition avatar wouter-toppy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jwtrefreshtokenbundle's Issues

It's not possible to override the default RefreshToken class

I want to extend the default implementation of RefreshToken in order to add some attributes. If I configure my class following the documentation I got the following error:

[Doctrine\DBAL\Exception\InvalidFieldNameException]
An exception occurred while executing 'SELECT r0_.id AS id_0, r0_.refresh_token AS refresh_token_1, r0_.username AS username_2, r0_.valid AS valid_3, c1_.custom_attr AS
impersonator_4 FROM acme_jwt_refresh_token c1_ LIMIT 1':
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'r0_.id' in 'field list'



[Doctrine\DBAL\Driver\PDOException]
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'r0_.id' in 'field list'



[PDOException]
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'r0_.id' in 'field list'

It happens both on refresh_token endpoint or when executing a query like bin/console doctrine:query:dql "SELECT rf FROM Acme:RefreshToken\RefreshToken rf" --max-result=1

In order to fix it the bundle must have an @MappedSuperclass class.

I will open a pull-request with the fix soon.

Aditional info:

config.yml

gesdinet_jwt_refresh_token:
    refresh_token_entity: 'Acme\Model\RefreshToken\RefreshToken'

RefreshToken.php

<?php

declare(strict_types = 1);

namespace Acme\Model\RefreshToken;

use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as BaseRefreshToken;

class RefreshToken extends BaseRefreshToken
{
    /**
     * @var string
     */
    protected $customAttr;

    public function customAttr() : ?string
    {
        return $this->customAttr;
    }

    public function setcustomAttr(string $customAttr) : void
    {
        $this->customAttr = $customAttr;
    }
}

Doctrine mapping:

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
  xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd">

  <entity
    name="Acme\Model\RefreshToken\RefreshToken"
    table="acme_jwt_refresh_token"
    repository-class="Gesdinet\JWTRefreshTokenBundle\Entity\RefreshTokenRepository">

    <field name="customAttr" column="custom_attr" type="string" length="255" nullable="true" />

</entity>
</doctrine-mapping>

Does not support multiple custom providers for SF4

Can we implement multiple-custom providers support for this bundle?

something like

        refresh:
            pattern:  ^/api/token/refresh
            stateless: true
            anonymous: true
            provider: custom_admin

        refresh:
            pattern:  ^/api/client/token/refresh
            stateless: true
            anonymous: true
            provider: custom_client

        refresh:
            pattern:  ^/api/supplier/token/refresh
            stateless: true
            anonymous: true
            provider: custom_supplier

solved by using chain provider in config
user_provider: security.user.provider.concrete.chain_provider

Header based refreshToken

Hi all,

Please add another condition which checks the header of request for a refresh token:

    public static function getRefreshToken(Request $request)
    {
        $refreshTokenString = null;
        if (false !== strpos($request->getContentType(), 'json')) {
            $content = $request->getContent();
            $params = !empty($content) ? json_decode($content, true) : array();
            $refreshTokenString = isset($params['refresh_token']) ? trim($params['refresh_token']) : null;
        } elseif (null !== $request->get('refresh_token')) {
            $refreshTokenString = $request->get('refresh_token');
        } elseif (null !== $request->request->get('refresh_token')) {
            $refreshTokenString = $request->request->get('refresh_token');
        } elseif (null !== $request->headers->get('authorization')) { // TODO: header check
            $refreshTokenString = $request->headers->get('authorization');
            $refreshTokenString = str_replace("Basic ", "", $refreshTokenString);
        }

        return $refreshTokenString;
    }

This will allow the users of @juztcode/angular-auth use this bundle with Angular without pain.

Refresh token never returned after custom token generation.

I generate jwt token using "lexik_jwt_authentication.encoder" service. In JWTRefreshTokenBundle's readme file I see that LexikJWTAuthenticationBundle now should returns a JWT Token and a Refresh Token data.

        $token = $this->get('lexik_jwt_authentication.encoder')
            ->encode([
                'username' => $user->getUsername(),
                'exp'      => (new \DateTime('+40 seconds'))->getTimestamp(),
            ]);

$token variable does not contains refresh token. In database, refresh_tokens table does not contains any token.

/api/token/refresh doesn't update the expiration date

I've installed the Bundle following the instructions but the /api/token/refresh doesn't update the expiration date into the column valid of the database table refresh_tokens.

It returns the token and refresh_token json correctly but the date isn't updated.

I'm I doing something wrong?

How to set user_provider to Doctrine user provider?

Hello,
as per api-platform's recommedations, I am using Doctrine user provider in my API.

So, my User entity looks like this:

// src/Api/Entity/User.php
namespace App\Api\Entity;
class User implements UserInterface, \Serializable {...}

Now I have no idea how to configure user_provider. I tried to set it like this:

# config/packages/gesindet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
    user_provider: app.api.entity.user

But I'm getting this error:
Type error: Argument 1 passed to ...\RefreshTokenProvider::setCustomUserProvider() must implement interface ...\UserProviderInterface, null given

Do you have any idea how do I set user_provider path properly, please?

Not working with LexikJWTAuthenticationBundle 1.7.0

Since LexikJWTAuthenticationBundle 1.7.0
See lexik/LexikJWTAuthenticationBundle#200

This bundle is not working anymore.

An Exception was thrown while handling: Catchable Fatal Error: Argument 1 passed to Gesdinet\JWTRefreshTokenBundle\Request\RequestRefreshToken::getRefreshToken() must be an instance of Symfony\Component\HttpFoundation\Request, null given, called in gesdinet\jwt-refresh-token-bundle\EventListener\AttachRefreshTokenOnSuccessListener.php on line 44 and defined

Add endpoint for clients who wants to revoke tokens

API clients can logout deleting its refresh_token. But this only works in client side, i mean, if someone has got refresh_token could do a login again.

The idea is to set an endpoint like /api/token/revoke which accepts a POST with refresh_token to revoke access.

Extra

Add another endpoint to revoke all tokens related to a user.

To call both endpoints user needs to be logged, of course. In other words, this endpoint has to be under JWT firewall.

Multiple sessions

I think is a good enhancement to make this bundle can handle many refresh tokens for different sessions. Do you have any idea how can implement this?

Generate refresh token inside a controller

Hi,
i need to generate a new refresh token when user changes it's username because in that case old refresh token becomes invalid. I can generate JWT token with $token = $this->get('lexik_jwt_authentication.jwt_manager')->create($user);, but can't manage to do the same with refresh token. Am i missing something, or there is no way to do it?

The refreshToken service doesn't work

Hi.

I'm try to implement the last release that has support for symfony 4, but I'm get this error

Controller "gesdinet.jwtrefreshtoken" cannot be fetched from the container because it is private. Did you forget to tag the service with "controller.service_arguments"?

It is about services, have you updated the services and implemented the autowire? and of course make the services public.

JWTRefreshTokenBundle usage of refresh token as a service Issue

I currently installed JWTRefreshTokenBundle and it throws and error as:
Compile Error: Cannot use Gesdinet\JWTRefreshTokenBundle\Service\RefreshToken as RefreshToken because the name is already in use.

I think the issue occurred in the latest pull. I already used the bundle, but at that time it worked fine. I am using Symfony3.4

What's happened if users swap their usernames ?

This bundle "links" user table and refresh token table with username field.
What's happened if users swap their usernames ?

Maybe I miss some codes but I see a real security issue.
Why do you not use relationship with foreign key between user table and refresh token table ?

Best regards.

Error on Symfony 3.1

Attempting to get Token ,the credentials are successful but then getting this failure:

The class 'Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken' was not found in the chain configured namespaces AppBundle\Entity

Setup:
routing.yml

gesdinet_jwt_refresh_token:
    path:     /api/v1/token/refresh
    defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }


security.yml

         gettoken:
             pattern:  ^/api/v1/getToken$
             stateless: true
             gfreeau_get_jwt:
                 # this is the default config
                 username_parameter: username
                 password_parameter: password
                 post_only: true
                 authentication_provider: security.authentication.provider.dao
                 user_checker: security.user_checker
                 success_handler: lexik_jwt_authentication.handler.authentication_success
                 failure_handler: lexik_jwt_authentication.handler.authentication_failure
         refresh:
             pattern:  ^/api/v1/token/refresh
             stateless: true
             anonymous: true
      - { path: ^/api/v1/getToken, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api/v1/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api, role: ROLE_ADMIN }

parameters.yml
jwt_private_key_path: "%kernel.root_dir%/var/jwt/private.pem" # ssh private key path
jwt_public_key_path: "%kernel.root_dir%/var/jwt/public.pem" # ssh public key path
jwt_key_pass_phrase: 'redacted' # ssh key pass phrase
jwt_cookie_name: 'jwt_token'
jwt_refresh_token_cookie_name: 'jwt_refresh_token'
jwt_token_ttl: 86400

Stack Trace
Trace


in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php at line 37  -
     */
    public static function classNotFoundInNamespaces($className, $namespaces)
    {
        return new self("The class '" . $className . "' was not found in the ".
            "chain configured namespaces " . implode(", ", $namespaces));
    }
at MappingException ::classNotFoundInNamespaces ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken', array('AppBundle\Entity')) 
in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php at line 112   +
at MappingDriverChain ->loadMetadataForClass ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken', object(ClassMetadata)) 
in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php at line 151   +
at ClassMetadataFactory ->doLoadMetadata (object(ClassMetadata), null, false, array()) 
in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php at line 332   +
at AbstractClassMetadataFactory ->loadMetadata ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php at line 78   +
at ClassMetadataFactory ->loadMetadata ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php at line 216   +
at AbstractClassMetadataFactory ->getMetadataFor ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 281   +
at EntityManager ->getClassMetadata ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/doctrine/orm/lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php at line 44   +
at DefaultRepositoryFactory ->getRepository (object(EntityManager), 'Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 698   +
at EntityManager ->getRepository ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in var/cache/dev/appDevDebugProjectContainer.php at line 6660   +
at DoctrineORMEntityManager_0000000064894caa000000010173686ed16a19f69a8f3c00cb83b6d77e875531 ->getRepository ('Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in vendor/gesdinet/jwt-refresh-token-bundle/Doctrine/RefreshTokenManager.php at line 33   +
at RefreshTokenManager ->__construct (object(DoctrineORMEntityManager_0000000064894caa000000010173686ed16a19f69a8f3c00cb83b6d77e875531), 'Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken') 
in var/cache/dev/appDevDebugProjectContainer.php at line 2053   +
at appDevDebugProjectContainer ->getGesdinet_Jwtrefreshtoken_RefreshTokenManagerService () 
in vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php at line 274   +
at Container ->get ('gesdinet.jwtrefreshtoken.refresh_token_manager') 
in var/cache/dev/appDevDebugProjectContainer.php at line 2066   +
at appDevDebugProjectContainer ->getGesdinet_Jwtrefreshtoken_SendTokenService () 
in vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php at line 274   +
at Container ->get ('gesdinet.jwtrefreshtoken.send_token') 
in var/cache/dev/classes.php at line 2264   +
at ContainerAwareEventDispatcher ->lazyLoad ('lexik_jwt_authentication.on_authentication_success') 
in var/cache/dev/classes.php at line 2233   +
at ContainerAwareEventDispatcher ->getListeners ('lexik_jwt_authentication.on_authentication_success') 
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php at line 241   +
at TraceableEventDispatcher ->preProcess ('lexik_jwt_authentication.on_authentication_success') 
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php at line 131   +
at TraceableEventDispatcher ->dispatch ('lexik_jwt_authentication.on_authentication_success', object(AuthenticationSuccessEvent)) 
in vendor/lexik/jwt-authentication-bundle/Security/Http/Authentication/AuthenticationSuccessHandler.php at line 51   +
at AuthenticationSuccessHandler ->onAuthenticationSuccess (object(Request), object(UsernamePasswordToken)) 
in vendor/gfreeau/get-jwt-bundle/Gfreeau/Bundle/GetJWTBundle/Security/Firewall/GetJWTListener.php at line 143   +
at GetJWTListener ->onSuccess (object(GetResponseEvent), object(Request), object(UsernamePasswordToken)) 
in vendor/gfreeau/get-jwt-bundle/Gfreeau/Bundle/GetJWTBundle/Security/Firewall/GetJWTListener.php at line 117   +
at GetJWTListener ->handle (object(GetResponseEvent)) 
in var/cache/dev/classes.php at line 3093   +
at Firewall ->onKernelRequest (object(GetResponseEvent), 'kernel.request', object(TraceableEventDispatcher))
at call_user_func (array(object(Firewall), 'onKernelRequest'), object(GetResponseEvent), 'kernel.request', object(TraceableEventDispatcher)) 
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php at line 61   +
at WrappedListener ->__invoke (object(GetResponseEvent), 'kernel.request', object(ContainerAwareEventDispatcher))
at call_user_func (object(WrappedListener), object(GetResponseEvent), 'kernel.request', object(ContainerAwareEventDispatcher)) 
in var/cache/dev/classes.php at line 2167   +
at EventDispatcher ->doDispatch (array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.request', object(GetResponseEvent)) 
in var/cache/dev/classes.php at line 2082   +
at EventDispatcher ->dispatch ('kernel.request', object(GetResponseEvent)) 
in vendor/symfony/symfony/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php at line 136   +
at TraceableEventDispatcher ->dispatch ('kernel.request', object(GetResponseEvent)) 
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 129   +
at HttpKernel ->handleRaw (object(Request), '1') 
in vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php at line 68   +
at HttpKernel ->handle (object(Request), '1', true) 
in var/bootstrap.php.cache at line 1447   +
at Kernel ->handle (object(Request)) 
in web/app.php at line 29   +

Fatal error when refresh token is experied

Hi. When I try to renew a token which is expired :

Catchable Fatal Error: Object of class Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken could not be converted to string

=> vendor/gesdinet/jwt-refresh-token-bundle/Service/RefreshToken.php at line 70

I guess toString method should be implemented.

Security issue: Loadable users are always authenticated

The current implementation to refresh JWT tokens is not safe. When a user presents a refresh token, the only check that occurs is whether the user provider can load the user. If a user is loadable, it is granted a new access token.

But the authenticator should also check if the loaded user is still allowed to access the application. For example, users that implement the AdvancedUserInterface should be checked whether their account is enabled, is not locked and is not expired. Other means of authentication may have other requirements (e.g. for OAuth accounts, check whether the correct permissions are still present).

Currently, this bundle will happily give out new access tokens to users who have been disabled, expired, etc.

I think there should be some way to hook into the authenticator to provide this logic. Or perhaps the authenticator in this bundle should delegate authentication to some other authentication provider.

Currently I am using a compiler pass to overwrite the authenticator provided in this bundle with my own. That's a bit of a hackish solution.

Event before RefreshToken is persisted

Hi all,

I think it can be usefull to have an Event dispatched before the RefreshToke is persisted to add some data inthe Entity.
My current usecase is that I want to store the userAgent of the user's device when s/he connnect for the first time. Currently I do that listening to lexik_jwt_authentication.on_authentication_success adding my data and persisting again the RefreshToken. But that makes 2 DB operations where only one is necessary.

Having an event in the Gesdinet\JWTRefreshTokenBundle\Doctrine\RefreshTokenManager::save or AttachRefreshTokenOnSuccessListener::attachRefreshToken should allow to add some data to the Entity before persist.

Regards

Symfony 4

Will be support for Symfony 4 added? lexik/LexikJWTAuthenticationBundle supports it.

Add encoder for refresh_token

I am using this bundle and it works perfectly and is easy to installe (great work guys ! Thanks !)

Anyway, i've noticed that tokens are actually stored in plain text in the db. I think this could be a weakness in any app if there is an fraudulous access on database.

I did not found any information on how to enable an encoding of the refresh tokens. Does anybody have an information on this ?

I would also suggest to add a buil-in solution for the bundle to easily add an encoder.

Thanks

no new refresh token on credentials authentication

Scenario:

  1. Auth with credentials for user 1:
    token: abc
    refresh_token: xyz

  2. Get new token with refresh_token:
    token: def
    refresh_token: xyz

  3. token expired, auth with credentials for user 1:
    token: ghi
    refresh_token: xyz

  4. optionally login from another browser with user 1 credentials:
    token: jkl
    refresh_token: xyz

I don't think that in step 3) and/or 4) the same token should be returned. Every credentials authentication should generate a new refresh_token.

Symfony 3.0 compatibility

Thanks for the hard work you put in this bundle. Do you have any plans to make it compatible with Symfony 3.0 in the near future?

Strategy to support Symfony 2.x and/or 3.x

Hi folks,

Recently i've created a new 3.0 branch. Now it works with both Symfony 2.8 and 3.0, but breaks 2.5 - 2.7 support.

Do you think the best option is maintan two branches separately? Or maybe the best option is try to support both in master?

Our principle issue is that interface SimplePreAuthenticatorInterface is moved from Core to Http namespace.

use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;

This interface is used in Gesdinet\JWTRefreshTokenBundle\Security\Authenticator\RefreshTokenAuthenticator

Do you know the best way to "choose" one or another interface depends on Symfony version?

Custom Entity give error

Here is what I got when trying to login

An exception occurred while executing 'SELECT t1.id AS id_2, t1.refresh_token AS refresh_token_3, t1.username AS username_4, t1.valid AS valid_5 FROM jwt_refresh_token t1 WHERE t0.refresh_token = ?' with params ["a94eca429273a21f39fc2374fb3984b9b392910a17afeb02e95627066e1d0dd983048a562780456d5b186148fa27acbda27db4821ab18d313314ac3a9107b025"]:

Cannot use another table name

I have custom entity which extends from the RefreshToken class. When updating the schema, I get two tables: refresh_tokens and myname_refresh_tokens.

Expand Refresh Token TTL on every calls

Hi,

I would like the user session expire XXX minutes after the last action on the application by the user.

If I understand correctly, ttl_update: true seems to expend the refresh token ttl only when we call the token/refresh service?

Is it possible to expend the refresh token ttl automatically on every call of any webservices?

Thanks

Seb

Possible to Use Chained User Provider ?

Is it possible to use a chained user provider? E.g. A couple of default users in memory and some from FosUserBundle ?

Not a huge deal as I can just load those in as fixtures, but I was just curious because the user provider needs to be defined as a service ID it seems.

JWTRefreshTokenBundle change user_identity_field

I use jwt token for auth and I had to change user_identity_field to email. And after that when I try call /api/token/refresh I have 401 status code. Because for refresh token entity in username property saved username data from user

my config

lexik_jwt_authentication:
private_key_path: '%jwt_private_key_path%'
public_key_path:  '%jwt_public_key_path%'
pass_phrase:      '%jwt_key_pass_phrase%'
token_ttl:        '%jwt_token_ttl%'
user_identity_field: email

gesdinet_jwt_refresh_token:
ttl: '%jwt_refresh_token_ttl%'
ttl_update: true
user_provider: security.user.provider.concrete.chain_provider

and my security

security:
encoders:
    AppBundle\Entity\User:
        algorithm: bcrypt

    AppBundle\Entity\Admin:
        algorithm: bcrypt

providers:
    chain_provider:
        chain:
            providers: [admins, entity_provider]

    admins:
        entity:
            class: AppBundle:Admin
            property: email

    entity_provider:
        entity:
            class: AppBundle:User
            property: email

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    refresh:
        pattern:  ^/api/token/refresh
        stateless: true
        anonymous: true

    api_admin:
        pattern:   ^/api/admin
        stateless: true
        anonymous: false
        provider: chain_provider
        guard:
            authenticators:
                - app.jwt_token_authenticator

    login:
        pattern:  ^/api/login
        stateless: true
        anonymous: true
        form_login:
            check_path: /api/login_check
            require_previous_session: false
            username_parameter: _email
            password_parameter: _password
            success_handler: custom
            failure_handler: lexik_jwt_authentication.handler.authentication_failure

now /api/token/refresh I have response

{
  "code": 401,
  "message": "Bad credentials"
}

because \Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken have username data from user, but in my config for lexik_jwt_authentication I changed it

user_identity_field: email

How to apply user_identity_field: email to refresh token ?

Throws error 500: Call to a member function xxxx on string

Hi, thanks for your excellent work.

I have a listener when ask for the JWT token: JWTCreatedListener.
But when i request to refresh the token it gives to me this error:

{ "error": { "code": 500, "message": "Internal Server Error", "exception": [ { "message": "Call to a member function getRole() on string", ...

I have this method getRole() for some tasks.
This is the Listener code:

` public function onJWTCreated(JWTCreatedEvent $event)
{
$request = $this->requestStack->getCurrentRequest();

    // Work of roles
    $roles = $event->getUser()->getRoles();
    $role_length = count($roles); 
    $role_list = array();
    for ($i=0; $i <$role_length ; $i++) { 
    array_push($role_list,$roles[$i]->getRole());
    }


    $payload       = $event->getData();
    $payload['ip'] = $request->getClientIp();
    $payload['roles'] = $role_list;

    $event->setData($payload);
}`

Any suggest? Thanks

Neither the property email nor ... exist and have public access in class Symfony Core User

Hi,

After installing and configuring JWTRefreshTokenBundle successfully, I have this error when I try to do the curl request:

curl -X POST -d refresh_token="xxxx4b54b0076d2fcc5a51a6e60c0fb83b0bc90b47e2c886accb70850795fb311973c9d101fa0111f12eec739db063ec09d7dd79331e3148f5fc6e9cb362xxxx" 'http://xxxx/token/refresh'

The error:

Neither the property "email" nor one of the methods "getEmail()", "email()", "ismail()", "hasEmail()", "__get()" exist and have public access in class "Symfony\\Component\\Security\\Core\\User\\User"

I used FOSUserBundle with LexikJWTAuthenticationBundle.
Maybe I have to configure the user provider of this bundle but I didn't find the configuration.

My config:

# app/config/security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
        Symfony\Component\Security\Core\User\User:
            algorithm: bcrypt
            cost: 12

    ...

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
        in_memory:
            memory:
                users:
                    xxxx: { password: xxxx, roles: 'ROLE_ADMIN' }

    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        default:
            pattern: ^/api/doc
            provider: in_memory
            anonymous: ~
            http_basic:
                realm: "xxxxx"
                provider: in_memory

        authenticate:
            pattern:  ^/v1/authenticate
            stateless: true
            anonymous: true
            form_login:
                check_path:               /v1/authenticate
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        refresh:
            pattern:  ^/v1/token/refresh
            stateless: true
            anonymous: true

        api:
            pattern:   ^/v1
            provider: fos_userbundle
            stateless: true
            anonymous: true
            lexik_jwt: ~

    access_control:
        - { path: ^/api/doc, roles: ROLE_ADMIN }
        - { path: ^v1/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# app/config/routing.yml

NelmioApiDocBundle:
    resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
    prefix:   /api/doc

fos_user:
    resource: "@FOSUserBundle/Resources/config/routing/all.xml"

api_login_check:
    path: /v1/authenticate

gesdinet_jwt_refresh_token:
    path:     /v1/token/refresh
    defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }

home:
    prefix:   /
    resource: AppBundle\Controller\HomeController
...

"code": 401, "message": "Invalid credentials"

After installing the bundle and configuring it as indicated in Readme, whenever I run token refresh with the refresh token i get this error

C:\Users\hp 250>curl -X POST -d refresh_token="9e44afc3b1e45aba95018f29df22fb92e
b3d5416a17289fa674b9bbe106595c5a3426e72c5ca9b93975d9bc070b5cd105663c27ae7a206fbb
be0d1b370d78855" "http://127.0.0.1/xxx/web/app_dev.php/api/token/ref
resh"
{"code":401,"message":"Invalid credentials"}


{
"code": 401,
"message": "Invalid credentials"
}


here is my parameters.yml


parameters:
database_host: 127.0.0.1
database_port: null
database_name: symfony
database_user: root
database_password: null
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: null
mailer_password: null
secret: ThisTokenIsNotSoSecretChangeIt

jwt_private_key_path: '%kernel.root_dir%\var\jwt\private.pem'   # ssh private key path
jwt_public_key_path:  '%kernel.root_dir%\var\jwt\public.pem'    # ssh public key path
jwt_key_pass_phrase:  'happyapi'                           # ssh key pass phrase if present
jwt_token_ttl:        3600  #seconds

And here is my config.yml


imports:

- { resource: parameters.yml }

- { resource: security.yml }
- { resource: services.yml }
- { resource: "@KasualJobsBundle/Resources/config/services.yml" }

Put parameters here that don't need to change on each machine where the app is deployed

http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration

parameters:
locale: en

framework:
#esi: ~
#translator: { fallbacks: ["%locale%"] }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
# handler_id set to null will use default session handler from php.ini
handler_id: ~
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
fragments: ~
http_method_override: true
assets: ~

Twig Configuration

twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"

Doctrine Configuration

doctrine:
dbal:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"

orm:
    auto_generate_proxy_classes: "%kernel.debug%"
    naming_strategy: doctrine.orm.naming_strategy.underscore
    auto_mapping: true

Swiftmailer Configuration

swiftmailer:
transport: "%mailer_transport%"
host: "%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
spool: { type: memory }

lexik_jwt_authentication

lexik_jwt_authentication:
private_key_path: %jwt_private_key_path%
public_key_path: %jwt_public_key_path%
pass_phrase: %jwt_key_pass_phrase%
token_ttl: %jwt_token_ttl%


And here is my security.yml


To get started with security, check out the documentation:

http://symfony.com/doc/current/book/security.html

security:

encoders:
    KasualJobsBundle\Entity\User: plaintext
    Symfony\Component\Security\Core\User\User: plaintext

providers:
    in_memory:
         memory:
             users:
                 [email protected]:
                     password: 123
                     roles: 'ROLE_ADMIN'


    my_db_provider:
        entity:
            class: KasualJobsBundle:User
            property: email

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    login_admin:
        pattern:  ^/api/admin/login
        stateless: true
        anonymous: true
        provider: in_memory
        form_login:
            check_path: /api/admin/login_check
            require_previous_session: false
            username_parameter: _username
            password_parameter: _password
            success_handler: lexik_jwt_authentication.handler.authentication_success
            failure_handler: lexik_jwt_authentication.handler.authentication_failure

    api_admin:
        pattern:   ^/api/admin
        stateless: true
        lexik_jwt:
            authorization_header:
                enabled: true
                prefix:  Bearer
            query_parameter:
                enabled: true
                name:    bearer

    login:
        pattern:  ^/api/login
        stateless: true
        anonymous: true
        provider: in_memory
        form_login:
            check_path: /api/login_check
            require_previous_session: false
            username_parameter: _username
            password_parameter: _password
            success_handler: lexik_jwt_authentication.handler.authentication_success
            failure_handler: lexik_jwt_authentication.handler.authentication_failure
    api:
        pattern:   ^/api
        stateless: true
        lexik_jwt:
            authorization_header: # check token in Authorization Header
                enabled: true
                prefix:  Bearer
            query_parameter:  # check token in query string parameter
                enabled: true
                name:    bearer

    refresh:
        pattern:  ^/api/token/refresh
        stateless: true
        anonymous: true



access_control:
    - { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/admin, roles: [ ROLE_ADMIN ]}
    - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

And here are my routes


api:
path: /api
defaults: { _controller: KasualJobsBundle:Default:secureResource }

api_login:
path: /api/login
defaults: { _controller: KasualJobsBundle:Default:index }

api_login_check:
path: /api/login_check

gesdinet_jwt_refresh_token:
path: /api/token/refresh
defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }


refresh token with additional fields in payload

Hello,
I'm new in symfony and and try to set HWTRefreshTokenBundle with JTW Lexik.
In jwt I have own Entity

namespace SharedBundle\Security;

final class User implements \Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface
{
    private $username;
    private $roles;
    private $name;
    
    public function __construct($username, array $roles, $email, $name)
    {
        $this->username = $username;
        $this->roles = $roles;
        $this->email = $email;
        $this->name = $name;
    }
    
    public static function createFromPayload($username, array $payload)
    {
        return new self(
            $username,
            $payload['roles'], // Added by default
            $payload['email'],  // Custom
            $payload['name']
        );
    }

 /**
     * {@inheritdoc}
     */
    public function getUsername()
    {
        return $this->username;
    }
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return $this->name;
    }
    /**
     * {@inheritdoc}
     */
    public function getRoles()
    {
        return $this->roles;
    }
 ...

In listener I add custom data:

public function onJWTCreated(JWTCreatedEvent $event) {
    $request = $this->requestStack->getCurrentRequest();

    $payload = $event->getData();
    $user = $event->getUser();
    
    $payload['name'] = $user->getName();
    $payload['email'] = $user->getEmail();
    $payload['ip'] = $request->getClientIp();

    $event->setData($payload);
  }

But when I try to refresh token I get error:

[Mon May 29 22:07:36 2017] 127.0.0.1:56078 [200]: /api/token/refresh
[Mon May 29 22:13:52 2017] PHP Fatal error:  Call to undefined method Symfony\Component\Security\Core\User\User::getName() in .../application/be/src/SharedBundle/EventListener/JWTCreatedListener.php on line 33
Segmentation fault

When I throw email and name from payload token is created but without this data, and return only default role ROLE_USER without ROLE_ADMIN.

I'tried to use refresh_token_entity: SharedBundle\Security\User but without result :/
How can I implemet additional fields in payload?

Refresh Token Unique and Secure

Hi,

Function that generate refresh token is this

bin2hex(openssl_random_pseudo_bytes(64));

I think is a good way to create a secure random string. But maybe its not unique. Because of this I ask for username when you need to refresh token.

Do you think is possible if we have so many database users that two valid refresh tokens be repeated?

Multiple providers

Can not configure multiple providers and use different urls to access each by using the chain and the same username in different providers it ends using always first success provider.

I think it will require to redesign bundle to be able to provide configuration and different urls for different providers

Question: How to use this bundle [BestPractice]

Hello folks,

i found out that a the ttl of JWTToken is not expanded, when it is used in api calls. This would have been the best solution in my opinion. But now i saw this bundle at i still cant find a solution, where the client can be "dumb".

This is what i understood:

  1. the client does an api call with a JWTToken.
  2. the client needs to check if the call was valid. If it was invalid, due to an expired JWTToken, it has to save to somehow cache the request data, ask for a new JWTToken via the RefreshToken and then do the origin api call again.

In this scenario, the client contains to much intelligence. Did i miss something or got something completely wrong?

Kind regards
Max

JWTRefreshTokenBundle change user_identity_field

I use jwt token for auth and I had to change user_identity_field to email. And after that when I try call /api/token/refresh I have 401 status code. Because for refresh token entity in username property saved username data from user

my config

lexik_jwt_authentication:
private_key_path: '%jwt_private_key_path%'
public_key_path:  '%jwt_public_key_path%'
pass_phrase:      '%jwt_key_pass_phrase%'
token_ttl:        '%jwt_token_ttl%'
user_identity_field: email

gesdinet_jwt_refresh_token:
ttl: '%jwt_refresh_token_ttl%'
ttl_update: true
user_provider: security.user.provider.concrete.chain_provider

and my security

security:
encoders:
    AppBundle\Entity\User:
        algorithm: bcrypt

    AppBundle\Entity\Admin:
        algorithm: bcrypt

providers:
    chain_provider:
        chain:
            providers: [admins, entity_provider]

    admins:
        entity:
            class: AppBundle:Admin
            property: email

    entity_provider:
        entity:
            class: AppBundle:User
            property: email

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    refresh:
        pattern:  ^/api/token/refresh
        stateless: true
        anonymous: true

    api_admin:
        pattern:   ^/api/admin
        stateless: true
        anonymous: false
        provider: chain_provider
        guard:
            authenticators:
                - app.jwt_token_authenticator

    login:
        pattern:  ^/api/login
        stateless: true
        anonymous: true
        form_login:
            check_path: /api/login_check
            require_previous_session: false
            username_parameter: _email
            password_parameter: _password
            success_handler: custom
            failure_handler: lexik_jwt_authentication.handler.authentication_failure

now /api/token/refresh I have response

{
  "code": 401,
  "message": "Bad credentials"
}

because \Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken have username data from user, but in my config for lexik_jwt_authentication I changed it

user_identity_field: email

How to apply user_identity_field: email to refresh token ?

Can we customize the parameter "refresh_token"

Can we customize the parameter "refresh_token" when calling the route to refresh the JWT , I would like it to be camelCase as the rest of the API I m building.

Same question about the response, to return "token" and "refreshToken" instead of "refresh_token"

Thanks
Vincent

Events to Customize Response Data?

In Lexik you can customize the data response using event listeners. The way the refresh token is added breaks our REST API model and I need to be able to customize the response as I can with Lexik. Are there events I can subscribe to in order to make sure the refresh token is placed in the right part of the response instead of just appended to it?

Thank you!

Installation issue with Mongodb

Hi,
I'm using mongodb, when i tried to install latest version ((>=v0.5.1), I am getting following error.

Script cache:clear returned with error code 255
!!
!!  Fatal error: Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "DoctrineOrmMappingsPass" from namespace "Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler".
!!  Did you forget a "use" statement for another namespace? in /var/www/html/vendor/gesdinet/jwt-refresh-token-bundle/DependencyInjection/Compiler/DoctrineMappingsCompilerPass.php:55
!!  Stack trace:
!!  #0 /var/www/html/vendor/gesdinet/jwt-refresh-token-bundle/DependencyInjection/Compiler/DoctrineMappingsCompilerPass.php(32): Gesdinet\JWTRefreshTokenBundle\DependencyInjection\Compiler\DoctrineMappingsCompilerPass->getORMCompilerPass(Array)
!!  #1 /var/www/html/vendor/symfony/dependency-injection/Compiler/Compiler.php(95): Gesdinet\JWTRefreshTokenBundle\DependencyInjection\Compiler\DoctrineMappingsCompilerPass->process(Object(Symfony\Component\DependencyInjection\ContainerBuilder))
!!  #2 /var/www/html/vendor/symfony/dependency-injection/ContainerBuilder.php(736): Symfony\Component\DependencyInjection\Compiler\Compiler->compile(Object(Sy in /var/www/html/vendor/gesdinet/jwt-refresh-token-bundle/DependencyInjection/Compiler/DoctrineMappingsCompilerPass.php on line 55
!!
Script @auto-scripts was called via post-update-cmd

I don't have orm installed, instead I am using mongodb odm.

Why database access at all, use JWT

Instead of generating random token and storing it to database, think about using another JWT as a refresh token.
Refresh token generated this way will have the expiry information in it, so no database access is needed for validation, except for blacklisting which could be optional.

Allow distinct entity manager instead of default

Hi,

If you want to use this bundle with another entity manager instead of default, this fails.

Then you need to override in your app/config/services.yml gesdinet.jwtrefreshtoken.refresh_token_manager service, injecting your custom entity_manager like this:

gesdinet.jwtrefreshtoken.refresh_token_manager:
  class: Gesdinet\JWTRefreshTokenBundle\Doctrine\RefreshTokenManager
  arguments: [@doctrine.orm.custom_entity_manager, %gesdinet.jwtrefreshtoken.refresh_token.class%]

Another example: if you are using FOSUserBundle you maybe want to inject fos_user.entity_manager service like this:

gesdinet.jwtrefreshtoken.refresh_token_manager:
  class: Gesdinet\JWTRefreshTokenBundle\Doctrine\RefreshTokenManager
  arguments: [@fos_user.entity_manager, %gesdinet.jwtrefreshtoken.refresh_token.class%]

To do:

  • Allow in bundle config to change entity manager that will be injected to RefreshTokenManager service.

refresh_token i am getting Forbidden 403

Hi,

I am getting forbidden 403 while doing the refresh_token

curl -X POST http://localhost:8083/api/token/refresh --data refresh_token=9be6a1654513bab76c3c0da96c4cb8c01abaad8d4a3b53a7f1e3ebe89caf034fbeb60f562876216720b173e01649a362f3711233f994aa11a722a82fff15d399

routing.yml

gesdinet_jwt_refresh_token:
path: /api/token/refresh
defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }

security.yml

firewalls:
    refresh:
        pattern:  ^/api/token/refresh
        stateless: true
        anonymous: true

    - { path: ^/api/token/refresh, role: IS_AUTHENTICATED_ANONYMOUSLY }

config.yml

gesdinet_jwt_refresh_token:
user_provider: fos_user.user_provider.username_email
ttl_update: false
firewall: api

I am using the refresh token.

Thanks

Remove FOSUserBundle dependency

Hi friends,

I think this bundle can't depends on FOSUserBundle.

I think is better to rename entity UserRefreshToken to RefreshToken, and save username in this entity as string.

The entity then will have 4 fields:

id, refresh_token, username, valid

With username we could revoke every refresh_token, for example if a user request to close sessions.

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.