Symfony 5.4 error AbstractGuardAuthenticator
alexandre-lxi opened this issue Β· 28 comments
Since Symfony 5.4, I have an error with the bundle.
Attempted to load class "AbstractGuardAuthenticator" from namespace "Symfony\Component\Security\Guard".
Did you forget a "use" statement for another namespace?
class RefreshTokenAuthenticator extends AbstractGuardAuthenticator
Could you help me to update the bundle ?
Ensure you have symfony/security-guard
installed. Note, the Security Guard component is deprecated in Symfony 5.4 and isn't supported in Symfony 6.
symfony/security-guard
is removed in Symfony 6. What is the workaround for this?
Update your app to use the newer refresh_jwt
authenticator.
Already using the the newer refresh_jwt
authenticator.
refresh:
pattern: ^/api/token/refresh
stateless: true
refresh_jwt: ~
If you're getting an error about the Symfony\Component\Security\Guard\AbstractGuardAuthenticator
class missing, then something's still trying to use it (maybe not this bundle specifically but somewhere in your app). Try to get a full stack trace for the error to figure out what's calling it, that will help greatly in finding the source of the problem.
For this bundle's CI, symfony/security-guard
is explicitly removed before running the tests in the Symfony 6 environment. So there is a bit of a sanity check here with the tests to make sure that nothing's trying to use the Security-Guard component in an unsupported environment.
Yes, I traced it is used in controller: gesdinet.jwtrefreshtoken::refresh
gesdinet_jwt_refresh_token:
path: /api/token/refresh
controller: gesdinet.jwtrefreshtoken::refresh
Remove the controller:
line from the route definition, itβs not needed with the newer authenticator.
Without controller:
getting this error:
Unable to find the controller for path "/api/token/refresh". The route is wrongly configured.
You shouldn't need it, https://github.com/markitosgv/JWTRefreshTokenBundle#define-the-refresh-token-route is pretty much the exact same thing I'm saying here with removing the controller key. #255 (comment) is the only other time I've seen that one referenced and there wasn't really a "fix" shared beyond just rebuilding the route configuration.
I'm in the same boat. Had the same initial config (route with controller), removed the controller part and then I get
Unable to find the controller for path "/v1/login/refresh". The route is wrongly configured.
routes:
gesdinet_jwt_refresh_token:
path: /v1/login/refresh
config:
gesdinet_jwt_refresh_token:
single_use: false
ttl: 2592000
cookie:
enabled: true
same_site: strict
path: /
domain: '%env(string:resolve:FRONTEND_DOMAIN)%'
http_only: true
secure: true
remove_token_from_body: true
security:
security:
login_refresh:
pattern: ^/v1/login/refresh
stateless: true
user_checker: Infrastructure\Shared\Security\UserChecker
refresh_jwt: ~
It looks like everything is OK in my tests, but this error is returned when navigating via the browser.
I wanted to see how my API was reacting when I deleted my cookies in the frontend manually. I manually deleted my token
and refresh_token
cookie, and I guess then the authenticator no longer supports the request.
If I remove only the token
cookie and keep the refresh_token
cookie, the token is correctly refreshed.
Unable to find the controller for path "/api/token/refresh".
path: /v1/login/refresh
Double-check everything in your configuration and whatever's supposed to be calling the refresh endpoint. Your configuration says the path is /v1/login/refresh
but something is trying to use the /api/token/refresh
path.
Yeah my bad that's a copy paste error. (I edited my message a few times). But I found the error pops up when no refresh token is set in the request.
You shouldn't need it, https://github.com/markitosgv/JWTRefreshTokenBundle#define-the-refresh-token-route is pretty much the exact same thing I'm saying here with removing the controller key. #255 (comment) is the only other time I've seen that one referenced and there wasn't really a "fix" shared beyond just rebuilding the route configuration.
No, It doesn't work without controller parameter
Same problem. I have to dive into sources and here is what I found out. Just leave the explanation and solution here.
As we can see in RefreshTokenAuthenticator supports() method returns false
when there is no refresh token in a request:
public function supports(Request $request): bool
{
return null !== $this->extractor->getRefreshToken($request, $this->options['token_parameter_name']);
}
so when no token present the authenticator will be skipped and router will fallback to gesdinet.jwtrefreshtoken::refresh
action.
Bundle's container configuration says that gesdinet.jwtrefreshtoken
alias refer to RefreshToken service which is deprecated and depends on AbstractGuardAuthenticator (this class was removed in Symfony 6).
To solve the problem you need simply define your own fallback endpoint. For example:
namespace App\Controller;
class LoginController extends AbstractController
{
//
public function refresh(): JsonResponse
{
return new JsonResponse(['msg' => 'JWT refresh token not found'], Response::HTTP_UNAUTHORIZED);
}
//
}
gesdinet.jwt_refresh_token:
path: /token/refresh
controller: App\Controller\LoginController::refresh
IΒ΄m using symfony 6 and a I have the same problem Attempted to load class "AbstractGuardAuthenticator" from namespace "Symfony\Component\Security\Guard". Did you forget a "use" statement for another namespace?
Someone know how to fix this error?
The 1.1 release should fix this.
I use the 1.1 release with Symfony 6 and I have this error:
Attempted to load class "AbstractGuardAuthenticator" from namespace "Symfony\Component\Security\Guard".
Did you forget a "use" statement for another namespace?
security.yaml:
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
providers:
admin:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
pattern: ^/admin
provider: admin
entry_point: form_login
custom_authenticators:
- App\Security\AdminAuthenticator
form_login:
provider: admin
login_path: admin_login
check_path: admin_login_check
failure_path: admin_login
default_target_path: admin
use_forward: false
use_referer: true
enable_csrf: true
logout:
path: admin_logout
target: admin_login
login:
pattern: ^/api/login$
stateless: true
json_login:
check_path: /api/login
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh:
pattern: ^/api/token/refresh
stateless: true
api:
pattern: ^/api
stateless: true
entry_point: jwt
jwt: ~
refresh_jwt:
check_path: /api/token/refresh
logout:
path: api_token_invalidate
access_control:
- { path: ^/api/token/refresh, roles: PUBLIC_ACCESS }
- { path: ^/api/docs, roles: ROLE_ADMIN }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
routes.yaml:
api_login_check:
path: /api/login
methods: ['POST']
controllers:
resource: ../src/Controller/
type: annotation
kernel:
resource: ../src/Kernel.php
type: annotation
api_refresh_token:
path: /api/token/refresh
controller: gesdinet.jwtrefreshtoken::refresh
api_token_invalidate:
path: /api/token/invalidate
If I remove controller: gesdinet.jwtrefreshtoken::refresh
I get this error:
Unable to find the controller for path "/api/token/refresh". The route is wrongly configured.
The controller must be.
Maybe you can try to remove gesdinet and install again (Clear cache too).
I have this in my composer.json:
"gesdinet/jwt-refresh-token-bundle": "dev-master",
@blosky01 same problem after cache clear and same after installed "gesdinet/jwt-refresh-token-bundle": "dev-master",
Security.yaml:
refresh_jwt: check_path: /api/auth/refresh provider: app_user_provider switch_user: false
routes.yaml:
api_refresh_token: path: /api/auth/refresh controller: gesdinet.jwtrefreshtoken::refresh
Services.yaml:
App\OpenApi\RefreshTokenDecorator: decorates: 'api_platform.openapi.factory' arguments: [ '@.inner' ]
in src/Entity/OpenApi/RefreshTokenDecorator.php
`<?php
declare(strict_types=1);
namespace App\OpenApi;
use ApiPlatform\Core\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\Core\OpenApi\OpenApi;
use ApiPlatform\Core\OpenApi\Model;
final class RefreshTokenDecorator implements OpenApiFactoryInterface
{
public function __construct(
private OpenApiFactoryInterface $decorated
) {}
public function __invoke(array $context = []): OpenApi
{
$openApi = ($this->decorated)($context);
$schemas = $openApi->getComponents()->getSchemas();
$schemas['RefreshToken'] = new \ArrayObject([
'type' => 'object',
'properties' => [],
]);
$schemas['RefreshCredentials'] = new \ArrayObject([
'type' => 'object',
'properties' => [],
]);
$pathItem = new Model\PathItem(
ref: 'Refresh JWT Token',
post: new Model\Operation(
operationId: 'postCredentialsItem',
tags: ['Refresh Token'],
responses: [
'200' => [
'description' => 'Get JWT token',
'content' => [
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/RefreshToken',
],
],
],
],
],
summary: 'Refresh JWT By Cookies',
requestBody: new Model\RequestBody(
description: 'Generate new JWT Token',
content: new \ArrayObject([
'application/json' => [
'schema' => [
'$ref' => '#/components/schemas/RefreshCredentials',
],
],
]),
),
),
);
$openApi->getPaths()->addPath('/api/auth/refresh', $pathItem);
return $openApi;
}
}`
in src/Entity/RefreshToken
`<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as BaseRefreshToken;
#[ORM\Table(name: 'refresh_tokens
')]
#[ORM\Entity()]
class RefreshToken extends BaseRefreshToken
{
}
`
Maybe this can help you.
if it doesn't work contact me.
@blosky01 thank you
Can you paste your security.yaml please ?
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
cost: 15
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
# users_in_memory: { memory: null }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api:
pattern: ^/api
stateless: true
json_login:
check_path: /api/auth/login
username_path: userIdentifier
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh_jwt:
check_path: /api/auth/refresh
provider: app_user_provider
switch_user: false
access_control:
- { path: ^/api/auth/(login|refresh), roles: PUBLIC_ACCESS }
- { path: ^/api, roles: PUBLIC_ACCESS }
- { path: ^/, roles: PUBLIC_ACCESS }
when@test:
security:
password_hashers:
# By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes
# are not important, waste resources and increase test times. The following
# reduces the work factor to the lowest possible values.
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4 # Lowest possible value for bcrypt
time_cost: 3 # Lowest possible value for argon
memory_cost: 10 # Lowest possible value for argon
@blosky01 Thank you (it's difficult to read your security.yaml ^^). I think it works, when I make a POST
request to /api/token/refresh
, the results contains a token
and refresh_token
?
security.yaml:
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
providers:
user:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
pattern: ^/admin
provider: user
entry_point: form_login
custom_authenticators:
- App\Security\AdminAuthenticator
form_login:
provider: user
login_path: admin_login
check_path: admin_login_check
failure_path: admin_login
default_target_path: admin
use_forward: false
use_referer: true
enable_csrf: true
logout:
path: admin_logout
target: admin_login
api:
pattern: ^/api
stateless: true
json_login:
check_path: /api/login
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
entry_point: jwt
jwt: ~
refresh_jwt:
check_path: /api/token/refresh
provider: user
switch_user: false
logout:
path: api_token_invalidate
access_control:
- { path: ^/api/token/refresh, roles: PUBLIC_ACCESS }
- { path: ^/api/docs, roles: ROLE_ADMIN }
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/admin/login, roles: PUBLIC_ACCESS }
- { path: ^/admin, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
routes.yaml:
api_login_check:
path: /api/login
methods: ['POST']
controllers:
resource: ../src/Controller/
type: annotation
kernel:
resource: ../src/Kernel.php
type: annotation
api_token_refresh:
path: /api/token/refresh
controller: gesdinet.jwtrefreshtoken::refresh
api_token_invalidate:
path: /api/token/invalidate
Sorry for my security.yamlπ
I hope I was able to help you solve you problem!π€πΌπ€πΌ
No problem, thank you! ππ
Remove the controller: gesdinet.jwtrefreshtoken::refresh
config from the route. That line is only required for folks using Symfony 4.4 applications and will break a Symfony 6 application because the Security-Guard component is not supported on Symfony 6.