markitosgv/JWTRefreshTokenBundle

No identifier/primary key specified for Entity "App\Entity\RefreshToken"

edebertJurisoft opened this issue · 11 comments

Hello,

I have a mistake about property in id in RefreshToken :
No identifier/primary key specified for Entity "App\Entity\RefreshToken" sub class of "Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken". Every Entity must have an identifier/primary key.

This mistake is not there all time.

I try overload id in my Entity RefreshToken. Do you have a solution ?

<?php

namespace App\Entity;

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

/**
 * @ORM\Entity
 * @ORM\Table("refresh_tokens")
 */
class RefreshToken extends BaseRefreshToken
{

   protected $id;


    public function getId()
    {
        return $this->id;
    }
}

Configuration
Php version : 8.0.22
Symfony : 5.4
jwt-refresh-token-bundle : 1.1.1
lexik/jwt-authentication-bundle: 2.16.0
I don't use attributes php8.

Got the same issue here.

It happens when the cache is clearing with the following command:

php bin/console cache:clear

I also tried to override the id attribute and give him annotations to define it as a primary key

#[ ORM\Id, ORM\GeneratedValue, ORM\Column(type:"integer") ] protected $id;

But then I get the following issue:

Duplicate definition of column 'id' on entity 'App\Entity\RefreshToken' in a field or discriminator column mapping.

I guess it means that somehow, somewhere, the id attribute is defined as the primary key of the entity, but the doctrine mapping doesn't get it.
This probably means that we need to define it with the other attributes elsewhere, in the doctrine yaml ?

Configuration :
Php version: 8.1.5
Symfony: 6.1
gesdinet/jwt-refresh-token-bundle: ^v1.1.1
lexik/jwt-authentication-bundle: 2.8

Ok, I've found a solution, I believe it's temporary but it works, I create a custom entity extended on AbstractRefreshToken with all attributes clearly described:

namespace App\Entity;

use App\Repository\RefreshTokenRepository;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Model\AbstractRefreshToken;

#[
    ORM\Entity(repositoryClass: RefreshTokenRepository::class),
    ORM\Table(name: 'jwt_refresh_tokens')
]
class JwtRefreshToken extends AbstractRefreshToken
{
    #[
        ORM\Id,
        ORM\GeneratedValue,
        ORM\Column(type:"integer")
    ]
    protected $id;

    #[
        ORM\Column(type: "string", length: 128, nullable: true)
    ]
    protected $refreshToken;

    #[
        ORM\Column(type:"string", length: 255, nullable: true)
    ]
    protected $username;

    #[
        ORM\Column(type:"datetime", nullable: true)
    ]
    protected $valid;
}

I also binded the entity with a repository that implements the RefreshTokenRepositoryInterface and add to it the needed methods.
And to change in the gesdinet_jwt_refresh_token.yml:

gesdinet_jwt_refresh_token:
  refresh_token_class: App\Entity\JwtRefreshToken

the same issue here!

Could someone help me somehow here :/

it's my exception

{
  "type": "https://tools.ietf.org/html/rfc2616#section-10",
  "title": "An error occurred",
  "status": 500,
  "detail": "Attempted to call an undefined method named \"getId\" of class \"Symfony\\Component\\Security\\Core\\User\\User\".",
  "class": "Symfony\\Component\\ErrorHandler\\Error\\UndefinedMethodError",
  "trace": [
    {
      "namespace": "",
      "short_class": "",
      "class": "",
      "type": "",
      "function": "",
      "file": "/usr/src/app/src/EventListener/JWTCreatedListener.php",
      "line": 13,
      "args": []
    },
    {
      "namespace": "App\\EventListener",
      "short_class": "JWTCreatedListener",
      "class": "App\\EventListener\\JWTCreatedListener",
      "type": "->",
      "function": "onJWTCreated",
      "file": "/usr/src/app/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
      "line": 126,
      "args": [
        [
          "object",
          "Lexik\\Bundle\\JWTAuthenticationBundle\\Event\\JWTCreatedEvent"
        ],
        [
          "string",
          "lexik_jwt_authentication.on_jwt_created"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "WrappedListener",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
      "type": "->",
      "function": "__invoke",
      "file": "/usr/src/app/vendor/symfony/event-dispatcher/EventDispatcher.php",
      "line": 251,
      "args": [
        [
          "object",
          "Lexik\\Bundle\\JWTAuthenticationBundle\\Event\\JWTCreatedEvent"
        ],
        [
          "string",
          "lexik_jwt_authentication.on_jwt_created"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "callListeners",
      "file": "/usr/src/app/vendor/symfony/event-dispatcher/EventDispatcher.php",
      "line": 73,
      "args": [
        [
          "array",
          [
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ]
          ]
        ],
        [
          "string",
          "lexik_jwt_authentication.on_jwt_created"
        ],
        [
          "object",
          "Lexik\\Bundle\\JWTAuthenticationBundle\\Event\\JWTCreatedEvent"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "/usr/src/app/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
      "line": 168,
      "args": [
        [
          "object",
          "Lexik\\Bundle\\JWTAuthenticationBundle\\Event\\JWTCreatedEvent"
        ],
        [
          "string",
          "lexik_jwt_authentication.on_jwt_created"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "TraceableEventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "/usr/src/app/vendor/lexik/jwt-authentication-bundle/Services/JWTManager.php",
      "line": 87,
      "args": [
        [
          "object",
          "Lexik\\Bundle\\JWTAuthenticationBundle\\Event\\JWTCreatedEvent"
        ],
        [
          "string",
          "lexik_jwt_authentication.on_jwt_created"
        ]
      ]
    },
    {
      "namespace": "Lexik\\Bundle\\JWTAuthenticationBundle\\Services",
      "short_class": "JWTManager",
      "class": "Lexik\\Bundle\\JWTAuthenticationBundle\\Services\\JWTManager",
      "type": "->",
      "function": "generateJwtStringAndDispatchEvents",
      "file": "/usr/src/app/vendor/lexik/jwt-authentication-bundle/Services/JWTManager.php",
      "line": 67,
      "args": [
        [
          "object",
          "Symfony\\Component\\Security\\Core\\User\\User"
        ],
        [
          "array",
          {
            "roles": [
              "array",
              [
                [
                  "string",
                  "ROLE_USER"
                ]
              ]
            ],
            "email": [
              "string",
              "elvis.jaskolski@gmail.com"
            ]
          }
        ]
      ]
    },
    {
      "namespace": "Lexik\\Bundle\\JWTAuthenticationBundle\\Services",
      "short_class": "JWTManager",
      "class": "Lexik\\Bundle\\JWTAuthenticationBundle\\Services\\JWTManager",
      "type": "->",
      "function": "create",
      "file": "/usr/src/app/vendor/lexik/jwt-authentication-bundle/Security/Http/Authentication/AuthenticationSuccessHandler.php",
      "line": 58,
      "args": [
        [
          "object",
          "Symfony\\Component\\Security\\Core\\User\\User"
        ]
      ]
    },
    {
      "namespace": "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Http\\Authentication",
      "short_class": "AuthenticationSuccessHandler",
      "class": "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Http\\Authentication\\AuthenticationSuccessHandler",
      "type": "->",
      "function": "handleAuthenticationSuccess",
      "file": "/usr/src/app/vendor/lexik/jwt-authentication-bundle/Security/Http/Authentication/AuthenticationSuccessHandler.php",
      "line": 49,
      "args": [
        [
          "object",
          "Symfony\\Component\\Security\\Core\\User\\User"
        ]
      ]
    },
    {
      "namespace": "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Http\\Authentication",
      "short_class": "AuthenticationSuccessHandler",
      "class": "Lexik\\Bundle\\JWTAuthenticationBundle\\Security\\Http\\Authentication\\AuthenticationSuccessHandler",
      "type": "->",
      "function": "onAuthenticationSuccess",
      "file": "/usr/src/app/vendor/gesdinet/jwt-refresh-token-bundle/Service/RefreshToken.php",
      "line": 120,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "object",
          "Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken"
        ]
      ]
    },
    {
      "namespace": "Gesdinet\\JWTRefreshTokenBundle\\Service",
      "short_class": "RefreshToken",
      "class": "Gesdinet\\JWTRefreshTokenBundle\\Service\\RefreshToken",
      "type": "->",
      "function": "refresh",
      "file": "/usr/src/app/vendor/symfony/http-kernel/HttpKernel.php",
      "line": 169,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handleRaw",
      "file": "/usr/src/app/vendor/symfony/http-kernel/HttpKernel.php",
      "line": 81,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handle",
      "file": "/usr/src/app/vendor/symfony/http-kernel/Kernel.php",
      "line": 201,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ],
        [
          "boolean",
          true
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "Kernel",
      "class": "Symfony\\Component\\HttpKernel\\Kernel",
      "type": "->",
      "function": "handle",
      "file": "/usr/src/app/public/index.php",
      "line": 32,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ]
      ]
    }
  ]
}
lexik/jwt-authentication-bundle v2.16.0 This bundle provides JWT authentication for your Symfony REST API
gesdinet/jwt-refresh-token-bundle v1.1.1 Implements a refresh token system over Json Web Tokens in Symfony
symfony/framework-bundle           v4.4.47 Provides a tight integration between Symfony components and the Symfony full-stack framework
symfony/http-client                v4.4.47 Provides powerful methods to fetch HTTP resources synchronously or asynchronously
symfony/http-client-contracts      v2.5.2  Generic abstractions related to HTTP clients
symfony/http-foundation            v4.4.48 Defines an object-oriented layer for the HTTP specification
symfony/http-kernel                v4.4.48 Provides a structured process for converting a Request into a Response

Ok I solved it adding a new service provider

gesdinet_jwt_refresh_token:
  user_identity_field: username
  refresh_token_class: App\Entity\RefreshToken
  user_provider: fos_user.user_provider.username_email

If you have doctrines auto_mapping set to false, you need to add the vendors mapping to your own:

<?php

declare(strict_types=1);

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->extension('doctrine', [
        'orm' => [
            'auto_mapping' => false,
            'mappings' => [
                ...
                'VendorRefreshToken' => [
                    'type' => 'xml',
                    'dir' => '%kernel.project_dir%/vendor/gesdinet/jwt-refresh-token-bundle/Resources/config/doctrine',
                    'prefix' => 'Gesdinet\JWTRefreshTokenBundle\Entity',
                ],
            ],
        ],
    ]);
};

(maybe also when auto_mapping is true, I don't know, its false in my project)

I just had a headache with this issue.

@darthf1 's solution is by far the cleanest I can think of.
Thank you, I was not sure how to configure a second mapping entry inside my main EntityManager. This works like a charm 👍

Here's the same but with yaml :

# [...]
mappings:
    main:
        # [...]
    refresh_token:
        type: xml
        dir: '%kernel.project_dir%/vendor/gesdinet/jwt-refresh-token-bundle/Resources/config/doctrine'
        prefix: 'Gesdinet\JWTRefreshTokenBundle\Entity'

Hi, i have same error with symfony 6.2 and gesdinet/jwt-refresh-token-bundle 1.1.3.
For fix this, i override abstract fields like this :

<?php

namespace App\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshTokenRepository;
use Gesdinet\JWTRefreshTokenBundle\Model\AbstractRefreshToken as BaseAbstractRefreshToken;

#[
    ORM\Entity(
        repositoryClass: RefreshTokenRepository::class,
    ),
    ORM\Table('refresh_tokens')
]
class RefreshToken extends BaseAbstractRefreshToken
{
    #[
        ORM\Id,
        ORM\Column(
            name: 'id',
            type: Types::INTEGER,
            nullable: false,
        ),
        ORM\GeneratedValue(strategy: 'AUTO'),
    ]
    protected $id;

    #[
        ORM\Column(
            name: 'refresh_token',
            type: Types::STRING,
            nullable: false,
        )
    ]
    protected $refreshToken;

    #[
        ORM\Column(
            name: 'username',
            type: Types::STRING,
            nullable: false,
        ),
    ]
    protected $username;

    #[
        ORM\Column(
            name: 'valid',
            type: Types::DATETIME_MUTABLE,
            nullable: false,
        )
    ]
    protected $valid;
}

Seems to be a bit of a false positive somewhere tbh.
When you straight up delete the deprecated class locally and run tests it'll work fine and without deprecation

Hi, i have same error with symfony 6.2 and gesdinet/jwt-refresh-token-bundle 1.1.3. For fix this, i override abstract fields like this :

<?php

namespace App\Entity;

…

class RefreshToken extends BaseAbstractRefreshToken
{

…

    #[
        ORM\Column(
            name: 'refresh_token',
            type: Types::STRING,
            nullable: false,
        )
    ]
    protected $refreshToken;
 …
}

You have to be careful, the original ORM mapping of the package has the $refreshToken property (refresh_token column) field mapped as unique and 128 characters long. My mapping looks like this and is not triggering a database change when creating a new migration:

#[
    ORM\Column(
        name: 'refresh_token',
        type: Types::STRING,
        length: 128,
        unique: true,
        nullable: false,
    )
]
protected $refreshToken;

Same mistake occurred with Symfony version 7.0.3. I opted for the method proposed by @loicngr. If there is another way to do it I am interested.