Open faso-dev opened 2 years ago
Hi, can you post your security.yaml
here?
Hi, can you post your
security.yaml
here?
# config/packages/security.yaml
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/auth
stateless: true
json_login:
username_path: email
check_path: api_login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh_jwt:
check_path: api_refresh_token
provider: app_user_provider
api:
pattern: ^/api
stateless: true
jwt:
authenticator: app.jwt_custom_api_authenticator
main:
lazy: true
provider: app_user_provider
hi, someone can help me ?
hi, someone can help me ?
I can't reproduce your issue because my custom authenticator is working fine.
I wonder is it because of the refresh token feature? How about try remove the refresh token feature from your project?
Hi,
I have the same problem. I don't know how to solve it
The Symfony versión is: 5.4.* And the LexikJWTAuthentication is: ^2.15
https://github.com/pakolkf/sfUsuarios/blob/main/config/services.yaml https://github.com/pakolkf/sfUsuarios/blob/main/config/packages/security.yaml https://github.com/pakolkf/sfUsuarios/blob/main/src/Security/CustomAuthenticator.php https://github.com/pakolkf/sfUsuarios/blob/main/composer.json
Not sure if it is relevant or not but can you try place the code at the end of the services
in services.yaml
?
For example:
services:
#....
app.custom_authenticator:
class: App\Security\CustomAuthenticator
parent: lexik_jwt_authentication.security.jwt_authenticator
Hi,
I have the same problem. I don't know how to solve it
The Symfony versión is: 5.4.* And the LexikJWTAuthentication is: ^2.15
https://github.com/pakolkf/sfUsuarios/blob/main/config/services.yaml https://github.com/pakolkf/sfUsuarios/blob/main/config/packages/security.yaml https://github.com/pakolkf/sfUsuarios/blob/main/src/Security/CustomAuthenticator.php https://github.com/pakolkf/sfUsuarios/blob/main/composer.json
Hi, I've tried what you say too and I get the same result, my custom class doesn't take it. debugging and using the dump function I don't see that it stops by the original loadUser method either
Hi, I've tried what you say too and I get the same result, my custom class doesn't take it. debugging and using the dump function I don't see that it stops by the original loadUser method either
Hi, can you write down your use case here? The repository you provided have no controller, thus I'm not sure what is your question about custom authenticator is not working here.
@faso-dev @pakolkf From my understanding, the authenticator is a function that will decode the JWT and load the user from database whenever user access the API with JWT. So if you want to try something in authenticator without accessing API with JWT nothing will work since the authenticator haven't trigger.
Perhaps both of you want this instead: https://symfony.com/doc/current/security/user_checkers.html
hi, someone can help me ?
I can't reproduce your issue because my custom authenticator is working fine.
I wonder is it because of the refresh token feature? How about try remove the refresh token feature from your project?
To tell the truth, I don't know if it's related to the RefreshToken, but I think that it intervenes only if the token is expired.
In my case, what I want to achieve is a JWT login through email or username. It seems that the LexikJWTAuthentication bundle does not do it by itself and I want to implement this login in the loadUser since this is where it is supposed to be accessed to retrieve the user from the database when launching the api/login_check request through postman
Hi, I've tried what you say too and I get the same result, my custom class doesn't take it. debugging and using the dump function I don't see that it stops by the original loadUser method either
Hi, can you write down your use case here? The repository you provided have no controller, thus I'm not sure what is your question about custom authenticator is not working here.
@faso-dev @pakolkf From my understanding, the authenticator is a function that will decode the JWT and load the user from database whenever user access the API with JWT. So if you want to try something in authenticator without accessing API with JWT nothing will work since the authenticator haven't trigger.
Perhaps both of you want this instead: https://symfony.com/doc/current/security/user_checkers.html
The authenticator actually retrieves the JWT token if it exists in the incoming request, and then retrieves the user from the database from the $payload.
Now if we are in the case of a login process where a token does not exist yet, the userIdentifier is used to retrieve the user from the database as well.
So all in all, the loader method should be called even once.
And indeed it is called, but only in the parent class (JWTTokenAuthenticator). The local one(CustomJWTTokenAuthenticator) to override the loadUser method is never called.
In my case, what I want to achieve is a JWT login through email or username. It seems that the LexikJWTAuthentication bundle does not do it by itself and I want to implement this login in the loadUser since this is where it is supposed to be accessed to retrieve the user from the database when launching the api/login_check request through postman
May refer to #1025
In my case, what I want to achieve is a JWT login through email or username. It seems that the LexikJWTAuthentication bundle does not do it by itself and I want to implement this login in the loadUser since this is where it is supposed to be accessed to retrieve the user from the database when launching the api/login_check request through postman
I also searched in vain how it proceeds to authenticate the default user just in api/login_check in the routes.yaml file
We are almost in the same need because I too don't want to generate the token if the user's account is locked, and the only way to do it and during the user retrieval in the loadUser method, unlike you who are looking to generate the token either by the email address or by the username.
The authenticator actually retrieves the JWT token if it exists in the incoming request, and then retrieves the user from the database from the $payload.
Now if we are in the case of a login process where a token does not exist yet, the userIdentifier is used to retrieve the user from the database as well.
So all in all, the loader method should be called even once.
And indeed it is called, but only in the parent class (JWTTokenAuthenticator). The local one(CustomJWTTokenAuthenticator) to override the loadUser method is never called.
Isn't that JWTTokenAuthenticator
is Guard component which already deprecated since Symfony 5.4?
You can try add dd();
in JWTAuthenticator::loadUser()
either directly in vendor or fork a version and change it there, mine does nothing there and I'm not really sure if it is really called or not.
I also searched in vain how it proceeds to authenticate the default user just in api/login_check in the routes.yaml file
We are almost in the same need because I too don't want to generate the token if the user's account is locked, and the only way to do it and during the user retrieval in the loadUser method, unlike you who are looking to generate the token either by the email address or by the username.
Have you thought about user checker? How about give it a try? https://symfony.com/doc/current/security/user_checkers.html
You can try add
dd();
inJWTAuthenticator::loadUser()
either directly in vendor or fork a version and change it there, mine does nothing there and I'm not really sure if it is really called or not.
I tested, it only works when you access API with JWT but not in the middle of login process.. or maybe I'm wrong here?
The authenticator actually retrieves the JWT token if it exists in the incoming request, and then retrieves the user from the database from the $payload. Now if we are in the case of a login process where a token does not exist yet, the userIdentifier is used to retrieve the user from the database as well. So all in all, the loader method should be called even once. And indeed it is called, but only in the parent class (JWTTokenAuthenticator). The local one(CustomJWTTokenAuthenticator) to override the loadUser method is never called.
Isn't that
JWTTokenAuthenticator
is Guard component which already deprecated since Symfony 5.4?You can try add
dd();
inJWTAuthenticator::loadUser()
either directly in vendor or fork a version and change it there, mine does nothing there and I'm not really sure if it is really called or not.
Yes when, i put an dd
in the loadUser
method on vendor JWTAuthenticator
class, it's called.
You can try add
dd();
inJWTAuthenticator::loadUser()
either directly in vendor or fork a version and change it there, mine does nothing there and I'm not really sure if it is really called or not.I tested, it only works when you access API with JWT but not in the middle of login process.. or maybe I'm wrong here?
That's exactly it
I also searched in vain how it proceeds to authenticate the default user just in api/login_check in the routes.yaml file We are almost in the same need because I too don't want to generate the token if the user's account is locked, and the only way to do it and during the user retrieval in the loadUser method, unlike you who are looking to generate the token either by the email address or by the username.
Have you thought about user checker? How about give it a try? https://symfony.com/doc/current/security/user_checkers.html
I implement and get back to you
And indeed it is called, but only in the parent class (JWTTokenAuthenticator). The local one(CustomJWTTokenAuthenticator) to override the loadUser method is never called.
Mine custom authenticator is working, the loadUser()
(the override one) is called every time i access my API, so i think it is definitely use case issue.
Thus, my conclusion are:
If you want to do something in the middle of login progress (like check user is banned or not), use user checker https://symfony.com/doc/current/security/user_checkers.html
If you want to do something when user accessing the API with provided JWT, use a custom authenticator.
If you want to apply login with username or email (#1025), just create a custom login api and generate the JWT there.
I also searched in vain how it proceeds to authenticate the default user just in api/login_check in the routes.yaml file We are almost in the same need because I too don't want to generate the token if the user's account is locked, and the only way to do it and during the user retrieval in the loadUser method, unlike you who are looking to generate the token either by the email address or by the username.
Have you thought about user checker? How about give it a try? https://symfony.com/doc/current/security/user_checkers.html
It's work for me fine and the jwt is not generated. I think this is the best solution to do this instead of to override the JWTAuthenticator
laodUser
method.
In security.yaml
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
...
user_checker: App\Http\Api\Security\Checker\JWTUserChecker
json_login:
username_path: email
check_path: api_login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
And indeed it is called, but only in the parent class (JWTTokenAuthenticator). The local one(CustomJWTTokenAuthenticator) to override the loadUser method is never called.
Mine custom authenticator is working, the
loadUser()
(the override one) is called every time i access my API, so i think it is definitely use case issue.Thus, my conclusion are:
If you want to do something in the middle of login progress (like check user is banned or not), use user checker https://symfony.com/doc/current/security/user_checkers.html
If you want to do something when user accessing the API with provided JWT, use a custom authenticator.
If you want to apply login with username or email (#1025), just create a custom login api and generate the JWT there.
can u share your example work, maybe it's configuration issues too.
can u share your example work, maybe it's configuration issues too.
I test it with memory user config.
security.yaml
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'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
user_in_memory:
memory:
users:
test: { password: '$2y$13$.kwOziT0ZefkvNKnrCdUKOvzPSQ/Zp4TDSo5pwXnGP0FCXanqw7ym', roles: ['ROLE_ADMIN'] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt:
authenticator: app.custom_authenticator
main:
lazy: true
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
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
CustomAuthenticator.php
<?php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator;
class CustomAuthenticator extends JWTAuthenticator
{
protected function loadUser(array $payload, string $identity): UserInterface
{
/** @var UserInterface|User $user */
$user = parent::loadUser($payload, $identity);
if (true){
$ex = new UserNotFoundException('Your account has been deactivated by the administrators');
$ex->setUserIdentifier($identity);
throw $ex;
}
return $user;
}
}
services.yaml
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
app.custom_authenticator:
class: App\Security\CustomAuthenticator
parent: lexik_jwt_authentication.security.jwt_authenticator
And use curl to access the login and api:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/api/login_check -d '{"username":"test","password":"123456"}'
replace the JWT to your JWT
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2NTQ1MjgyODAsImV4cCI6MTY1NDUzMTg4MCwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJ1c2VybmFtZSI6InRlc3QifQ.fADcr2RQxmUXj54w8-6lR6LPcA85Zq3F7RJ4PNz9xij8vZjclFjTJolqFZaPinUmWtpnoA11QMUOCU8QVIkCVacocMgHqvXuv4vAapZ9MBTMugDF2J1ZM80zUySfS4oD9nsFJEdeIaHrPNKdD4ZLJPv4oTiCtjEWzIg2PGX1bNwYVHq_yy0TkNpOrcafLYGS7QS2LmQRx_bJmtyvkYEScQskRKyMlF1vblDJFT8JZSdo5xNUS_1u5_V8fVf6WPg9hSagbl_Rq28RKvsVLDyv-RHSXsfqS5WyYqVK1F9n_nSg-84k3yj4PB1dkpcOISTfw2yfawt05EAGShQ8YaNOlA" http://127.0.0.1:8000/api/test
I also searched in vain how it proceeds to authenticate the default user just in api/login_check in the routes.yaml file We are almost in the same need because I too don't want to generate the token if the user's account is locked, and the only way to do it and during the user retrieval in the loadUser method, unlike you who are looking to generate the token either by the email address or by the username.
Have you thought about user checker? How about give it a try? https://symfony.com/doc/current/security/user_checkers.html
It's work for me fine and the jwt is not generated. I think this is the best solution to do this instead of to override the
JWTAuthenticator
laodUser
method.In security.yaml
firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false login: ... user_checker: App\Http\Api\Security\Checker\JWTUserChecker json_login: username_path: email check_path: api_login_check success_handler: lexik_jwt_authentication.handler.authentication_success failure_handler: lexik_jwt_authentication.handler.authentication_failure
Glad you solve the issue.
can u share your example work, maybe it's configuration issues too.
I test it with memory user config.
security.yaml
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' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: user_in_memory: memory: users: test: { password: '$2y$13$.kwOziT0ZefkvNKnrCdUKOvzPSQ/Zp4TDSo5pwXnGP0FCXanqw7ym', roles: ['ROLE_ADMIN'] } firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false login: pattern: ^/api/login stateless: true json_login: check_path: /api/login_check success_handler: lexik_jwt_authentication.handler.authentication_success failure_handler: lexik_jwt_authentication.handler.authentication_failure api: pattern: ^/api stateless: true jwt: authenticator: app.custom_authenticator main: lazy: true access_control: - { path: ^/api/login, roles: PUBLIC_ACCESS } - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } 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
CustomAuthenticator.php
<?php namespace App\Security; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Lexik\Bundle\JWTAuthenticationBundle\Security\Authenticator\JWTAuthenticator; class CustomAuthenticator extends JWTAuthenticator { protected function loadUser(array $payload, string $identity): UserInterface { /** @var UserInterface|User $user */ $user = parent::loadUser($payload, $identity); if (true){ $ex = new UserNotFoundException('Your account has been deactivated by the administrators'); $ex->setUserIdentifier($identity); throw $ex; } return $user; } }
services.yaml
# This file is the entry point to configure your own services. # Files in the packages/ subdirectory configure your dependencies. # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: services: # default configuration for services in *this* file _defaults: autowire: true # Automatically injects dependencies in your services. autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/' exclude: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php' # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones app.custom_authenticator: class: App\Security\CustomAuthenticator parent: lexik_jwt_authentication.security.jwt_authenticator
And use curl to access the login and api:
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/api/login_check -d '{"username":"test","password":"123456"}'
replace the JWT to your JWT
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2NTQ1MjgyODAsImV4cCI6MTY1NDUzMTg4MCwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJ1c2VybmFtZSI6InRlc3QifQ.fADcr2RQxmUXj54w8-6lR6LPcA85Zq3F7RJ4PNz9xij8vZjclFjTJolqFZaPinUmWtpnoA11QMUOCU8QVIkCVacocMgHqvXuv4vAapZ9MBTMugDF2J1ZM80zUySfS4oD9nsFJEdeIaHrPNKdD4ZLJPv4oTiCtjEWzIg2PGX1bNwYVHq_yy0TkNpOrcafLYGS7QS2LmQRx_bJmtyvkYEScQskRKyMlF1vblDJFT8JZSdo5xNUS_1u5_V8fVf6WPg9hSagbl_Rq28RKvsVLDyv-RHSXsfqS5WyYqVK1F9n_nSg-84k3yj4PB1dkpcOISTfw2yfawt05EAGShQ8YaNOlA" http://127.0.0.1:8000/api/test
i'll retry this if needed. Thank u for your support. So i think this issue can be closed now
Hi there!
I use this bundle in my symfony 6 project to authenticate my users by jwt token. Everything is going fine until I want to create a custom authenticator to add some logic in how I authenticate my users. After following the documentation https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/6-extending-jwt-authenticator.rst, my authenticator is never called and I don't have any error either.
In my services.yaml file
In security.yaml file
In my App\Http\Api\Security\Authenticator\JWTCustomApiAuthenticator file
SYMFONY 6 + PHP 8.1
I would like to know how to implement my authenticator logic.