I get an invalid token? - symfony

I work under a Docker project. Symfony 5.
the registration goes well, I have the user registered in the database
the connection goes well, I get the token
there is a record line in the auth table with the token during the connection
on the other hand the access to a protected route it indicates me that the token is not valid
GET http://localhost:8003/api/users/13
Authorization Bearer ey..............................................................
{
"code": 401,
"message": "Invalid JWT Token"
}
security.yml
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
doc:
pattern: ^/doc
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
username_path: email
check_path: /api/login/token
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
user_register:
pattern: ^/api/user/register
stateless: true
anonymous: true
main:
pattern: ^/api
stateless: true
anonymous: false
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/user/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/, roles: IS_AUTHENTICATED_FULLY }
lexik_jwt_authentication.yaml
lexik_jwt_authentication:
private_key_path: "%kernel.project_dir%/%env(JWT_PRIVATE_KEY_PATH)%"
public_key_path: "%kernel.project_dir%/%env(JWT_PUBLIC_KEY_PATH)%"
pass_phrase: "%env(JWT_PASSPHRASE)%"
token_ttl: 3600 # token TTL in seconds, defaults to 1 hour
user_identity_field: email
clock_skew: 0
encoder:
service: lexik_jwt_authentication.encoder.lcobucci
signature_algorithm: RS256
token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
cookie:
enabled: false
name: BEARER
query_parameter:
enabled: false
name: bearer
I have created the 2 certificates with the right passphrase :
.env
...
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=f8bfe4494b7cf3032d642a3e72dcac53
/config/jwt/private.pem
/config/jwt/public.pem
\vendor\Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator.php
public function getCredentials(Request $request)
{
$tokenExtractor = $this->getTokenExtractor();
if (!$tokenExtractor instanceof TokenExtractorInterface) {
throw new \RuntimeException(sprintf('Method "%s::getTokenExtractor()" must return an instance of "%s".', __CLASS__, TokenExtractorInterface::class));
}
if (false === ($jsonWebToken = $tokenExtractor->extract($request))) {
return;
}
$preAuthToken = new PreAuthenticationJWTUserToken($jsonWebToken);
try {
dump($preAuthToken); // --------------
dump($this->jwtManager->decode($preAuthToken)); // ERROR HERE
// --------------
if (!$payload = $this->jwtManager->decode($preAuthToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
$preAuthToken->setPayload($payload);
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
$expiredTokenException = new ExpiredTokenException();
$expiredTokenException->setToken($preAuthToken);
throw $expiredTokenException;
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
return $preAuthToken;
}
dump($preAuthToken);
JWTTokenAuthenticator.php on line 106:
Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken {#598
-rawToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjIyLCJ1c2VybmFtZSI6InRvdG8xMUB0b3RvLmZyIiwiaWF0IjoxNjIyMDI3NzQ4fQ.COr_fuXAH8iq3Ecr8mJVIVVdLI6H5zv7419gvQwLy6Q"
-payload: null
-credentials: null
-guardProviderKey: null
-user: null
-roleNames: []
-authenticated: false
-attributes: []
}
it's the right token passed in the request
an error occurs on this line: $this->jwtManager->decode($preAuthToken)
and triggers: new InvalidTokenException('Invalid JWT Token', 0, $e);

Which version are you using of LexikJWTAuthentication ? I guess the latest with symfony 5, you should use param public_key instead of public_key_path : https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/1-configuration-reference.md

Related

jwt lexik JWT Authetication not found

I have been looking for a solution for a while now.
All authentication system is in place, I get my token but when I use it to retrieve the data I get a 404 jwt not found.
I have an emergency on this project, a help will help me a lot.
this is my security.yaml:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check # or api_login_check as defined in config/routes.yaml
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
lazy: true
provider: app_user_provider
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Pour la declaration du controller:
api_login_check:
path: /api/login_check
You have to do the following;
go to /config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_extractors:
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
cookie: // if you are using cookie
enabled: true
name: cookie_name // set the cookie name
If you are using httpClient; then you can Authorization: 'Bearer ' .$token in your header as follows;
$headers = [
'Authorization' => 'Bearer '.$token,
'Content-Type' => 'application/json',
];
NOTE: make sure you have jwt directory, where you have private and public keys.

change redirection URL for not logged in user

When I'm not logged in and I navigate to a secured route (/admin/page0 in this example), it redirects me to "/login" URL.
I want to change the redirection route to /admin/login
#Secured Route:
class DefaultController extends AbstractController
{
/**
* #Route("/admin/page0", name="admin_route")
*/
public function adminIndex()
{
return $this->render('default/index.html.twig', [
'controller_name' => 'DefaultController',
]);
}
}
#security.yaml
security:
encoders:
App\Entity\Admin:
algorithm: auto
providers:
admin_provider:
entity:
class: App\Entity\Admin
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
pattern: ^/admin/
#lazy: true #it means anonymous mode
provider: admin_provider
guard:
authenticators:
- App\Security\AppAuthenticator
logout:
path: '%env(LOGOUT_REDIRECT_ROUTE)%'
# where to redirect after logout
target: default
remember_me:
secret: '%env(APP_SECRET)%'
lifetime: 604800 # 1 week in seconds
path: /
httponly: true
samesite: strict
secure: false
access_denied_handler: App\Security\AdminAccessDeniedHandler
access_denied_url: /
access_control:
- { path: ^/(|home|login|admin/login), roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: '%env(SECURE_SCHEME)%' }
- { path: ^/admin, roles: ROLE_ADMIN, requires_channel: '%env(SECURE_SCHEME)%' }
As you see I tried to do it by adding to my firewall:
access_denied_handler: App\Security\AdminAccessDeniedHandler
access_denied_url: /
But everytime I go to /admin/page0 it redirects me to /login :(
In your firewall's authenticator guard, you have this method:
protected function getLoginUrl() {
return $this->urlGenerator->generate('app_login');
}
You have just to modify the route name.

I can't get the user from JWT

I have a project with Symfony 5.1 using Lexik JWT v2.8.0, gesdinet jwt refresh token v0.9.1 and my own entity user. I can log in with JWT and get the token, save it in a HttpOnly cookie and use it with the protected APIs successfully.
My web app has some API Rest but it has also pages to browse. So my intention is do it all from API, but also login in the web browser when user obtains a token.
But as api login is stateless, after the success login the web profiler still shows logged in as anon. and I can't get te user from the token. I made a service to get the user from the token, to call it in the controller and send some logged in user data to the front end.
I've got a service from this question. The service I have implemented to get the user is:
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use UserBundle\Domain\Entity\User;
class UserService
{
private TokenStorageInterface $tokenStorage;
private JWTTokenManagerInterface $jwtManager;
public function __construct( TokenStorageInterface $storage, JWTTokenManagerInterface $jwtManager )
{
$this->tokenStorage = $storage;
$this->jwtManager = $jwtManager;
}
public function getCurrentUser() : ?User
{
$decodedJwtToken = $this->jwtManager->decode($this->tokenStorage->getToken());
if ($decodedJwtToken instanceof TokenInterface) {
$user = $decodedJwtToken->getUser();
return $user;
} else {
return null;
}
}
}
And it is declared as a service:
get.token.user.service:
class: UserBundle\Domain\Service\UserService
arguments: [ '#security.token_storage' ]
But all I get from $this->tokenStorage->getToken() is a web token with "anon." user, so it is not a JWT and jwtManager can't decode it.
UserService.php on line 25:
Symfony\Component\Security\Core\Authentication\Token\AnonymousToken {#16 ▼
-secret: "RXVaVx3"
-user: "anon."
-roleNames: []
-authenticated: true
-attributes: []
}
I also tried to get the jwt from the cookie in the controller and send it to the service as an argument, but I get an error from decode that I'm passing a string and it expects a TokenInterface object, so it did not worked neither.
How can I get the user from the token in the service? is there a best practice to login in the web through api than get the user from the jwt and send it to the render?
Edit: add the code to use lexik jwt and to save token in cookie:
# /config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
user_identity_field: email
token_extractors:
cookie:
enabled: true
name: BEARER
the security file code
# /config/packages/security.yaml
security:
encoders:
UserBundle\Domain\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: UserBundle\Domain\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
provider: app_user_provider
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
register:
pattern: ^/api/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: true
provider: app_user_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
lazy: true
provider: app_user_provider
access_control:
- { path: ^/api/user, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/linkpage, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Listener to save jwt in a cookie and avoid the token to be in the response:
use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent;
use Symfony\Component\HttpFoundation\Cookie;
class AuthenticationSuccessListener
{
private bool $secure = false;
private int $tokenTtl;
public function __construct(int $tokenTtl)
{
$this->tokenTtl = $tokenTtl;
}
public function onAuthenticationSuccess( AuthenticationSuccessEvent $event)
{
$response = $event->getResponse();
$data = $event->getData();
$token = $data['token'];
unset($data['token']); // remove token from response. It works.
unset($data['refresh_token']); // remove token from refresh token, even though I still get this token in the response
$event->setData($data);
$response->headers->setCookie(
new Cookie(
'BEARER',
$token,
(new \DateTime())->add(new \DateInterval('PT'. $this->tokenTtl . 'S')),
'/',
null,
$this->secure
)
);
}
}
#services.yaml
app.listener.authenticationsuccesslistener:
class: UserBundle\Application\Listeners\AuthenticationSuccessListener
arguments: ['%lexik_jwt_authentication.token_ttl%']
tags:
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccess }
I still coded 2 more listeners for the refresh token, but I don't think they are needed.
The rest of the authentication is the default authentication code from lexik jwt bundle.
In the BEARER cookie is stored the jwt with the correct format, something like this:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1OTgzNTE0ODYsImV4cCI6MTU5ODM1NTA4Niwicm9sZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJlbWFpbCI6ImFkbWluQGVtYWlsLmNvbSJ9.p5WYCxEE-VSxp09Eo7CxXoxi6zy1ZcnLJiBe1YGsrk3iFm7T-6JAWbvyb9ZW_-1jtpYcWQlFOjOf7uET4wRHvlvygnPOeZck7tZM8TUlSqMXIllMeVARz8mEUvXVwhDWEk5T7Ibw9c3VgfvyiLUgSb_cmSyK3DwtgPCd9vOYarkag9XKxkxNI9M1OHGL61v1NoQHdkXloC72xdUUMcj5Y8yCJWZFdOp8-Xtfq8ZChAHzwCjXUhC3VnBqZcMT0tAvtilwTGDYDykppNKK1vbNoyOex47wQH_ILEFuX5Eh1p2xfbc0lWm3Ip21z3EQ2M_eOQgZvHR65T3b2dv9g5GPiFp3CNo8AuW8m6rXjWK6NZXJO8qYodxI5cUYYyFooCfVXXU8JXzGQfCZIdOPw-iBGzQEfFuLL50_sAOVcxjklAYCFZYRHwFKWmwl1BwJF4mAw4jnNIAdMmc66Z17ul2Jep9apOO90C1dZzGuVKxWqglc9GZo7-teHt0dMsg0ADrvaOKNJUwDBGJ4lZpWx6_stcl7DkCdc5k1kePGdLa7YXRO3umPwa_FVYVgnT_Z9x7RtfnGioa2TZJCIdbJnuj0L90vkgFBjHqFdVydDaaBu3Y0mKoQ2v3Sf1so4-uwJm8z1vQVZleMQgFibMiyyk3YyDidhPSxxyp4u-4xPNOSDNo
If I am not logged in and don't have jwt or I delete the BEARER cookie I can't access the protected APIs ({"code": 401, "message": "JWT Token not found"}), when I have the jwt assigned I can request APIs correctly.
The problem was on the security.yaml configuration, specifically in the firewalls.main section, where I had to add the jwt_token_authenticator as the guard. Finally, my security.yaml is:
security:
encoders:
UserBundle\Domain\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: UserBundle\Domain\Entity\User
property: email
jwt:
lexik_jwt: ~
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
provider: app_user_provider
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
register:
pattern: ^/api/register
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: true
provider: jwt
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: true
lazy: true
provider: jwt
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
logout:
path: app_logout
target: /
access_control:
- { path: ^/api/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/token/refresh, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/logout, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Then I had some problems with the logout, as the logout didn't erase the jwt cookies and the logout kept me logged in. So I made a Listener to the LogoutEvent and made the listener delete the cookies.
use Symfony\Component\Security\Http\Event\LogoutEvent;
class LogoutListener
{
public function onSymfonyComponentSecurityHttpEventLogoutEvent(LogoutEvent $event)
{
$response = $event->getResponse();
$response->headers->clearCookie('BEARER');
$response->headers->clearCookie('REFRESH_TOKEN');
$event->setResponse($response);
}
}
and declared it as a service:
app.listener.logout:
class: UserBundle\Application\Listeners\LogoutListener
tags:
- name: 'kernel.event_listener'
event: 'Symfony\Component\Security\Http\Event\LogoutEvent'
dispatcher: security.event_dispatcher.main
And now the login is working on the web without having to decode the jwt with the service I was using and pass the user to the front. Though the service that decoded the jwt, now is working fine.
I've lost almost a week with this issue, but finally I've found a solution and it's working fine. So I'll post it here to save time if anybody has some similar issue.

lexik JWT error invalid token while decoding token

I've made an authentication system with LexikJWTBundle, below are my security.yml, my service.yml and my config.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
providers:
fos_userbundle:
id: fos_user.user_provider.username
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: fos_userbundle
logout: true
form_login:
check_path: /api/login_check
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
register:
pattern: ^/api/register
stateless: true
anonymous: true
register-admin:
pattern: ^/api/adminregister
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: true
provider: fos_userbundle
guard:
authenticators:
- jwt_token_authenticator
admin-api:
pattern: ^/api/admin
provider: fos_userbundle
stateless: true
anonymous: true
guard:
authenticators:
- jwt_token_authenticator
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
access_control:
- { path: ^/api/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/adminregister, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: 127.0.0.1, host: localhost }
- { path: ^/api/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/, roles: [IS_AUTHENTICATED_FULLY, ROLE_USER] }
- { path: ^/api/admin/, roles: [IS_AUTHENTICATED_FULLY, ROLE_ADMIN] }
- { path: ^/api/admin/users, roles: [IS_AUTHENTICATED_FULLY, ROLE_SUPER_ADMIN] }
and the service.yml
service.yml
# Learn more about services, parameters and containers at
# https://symfony.com/doc/current/service_container.html
parameters:
#parameter_name: value
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're private
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
# add more services, or override services that need manual wiring
# AppBundle\Service\ExampleService:
# arguments:
# $someArgument: 'some_value'
jwt_token_authenticator:
class: AppBundle\Security\JwtTokenAuthenticator
arguments: ['#lexik_jwt_authentication.encoder.lcobucci', '#doctrine.orm.entity_manager']
#service for cors preflight event
AppBundle\Event\Listener\:
resource: '../../src/AppBundle/Event/Listener/'
tags:
- { name: kernel.event_listener, event: kernel.response }
and the config.yml
config.yml
// I've only put the lexik config
lexik_jwt_authentication:
secret_key: "%kernel.project_dir%/config/jwt/private.pem" # required for token creation
public_key: "%kernel.project_dir%/config/jwt/private.pem" # required for token verification
pass_phrase: "%user_pass%" # required for token creation, usage of an environment variable is recommended
token_ttl: 36000
user_identity_field: username # key under which the user identity will be stored in the token payload
clock_skew: 0
# token encoding/decoding settings
encoder:
# token encoder/decoder service - default implementation based on the lcobucci/jwt library
service: lexik_jwt_authentication.encoder.lcobucci
# encryption algorithm used by the encoder service
signature_algorithm: RS256
# token extraction settings
token_extractors:
# look for a token as Authorization Header
authorization_header:
enabled: true
prefix: Bearer
name: Authorization
# check token in a cookie
cookie:
enabled: false
name: BEARER
# check token in query string parameter
query_parameter:
enabled: false
name: bearer
that's the function for guard authenticator
class JwtTokenAuthenticator extends AbstractGuardAuthenticator
{
private $jwtEncoder;
private $em;
public function __construct(JWTEncoderInterface $jwtEncoder, EntityManager $em)
{
$this->jwtEncoder = $jwtEncoder;
$this->em = $em;
}
public function supports(Request $request)
{
// look for header "Authorization: Bearer <token>"
return $request->headers->has('Authorization')
&& 0 === strpos($request->headers->get('Authorization'), 'Bearer ');
}
public function getCredentials(Request $request)
{
$extractor = new AuthorizationHeaderTokenExtractor(
'Bearer',
'Authorization'
);
$token = $extractor->extract($request);
if (!$token) {
return new JsonResponse([
"errorMessage" => "The token doesn't exist in authorization header please provide it like (Authorization: Bearer token)"
]);
}
return $token;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
var_dump($credentials);
try {
$data = $this->jwtEncoder->decode($credentials);
} catch (JWTDecodeFailureException $e) {
$reason = $e->getReason();
switch ($reason) {
case 'invalid_token':
// throw new InvalidTokenException();
throw new CustomUserMessageAuthenticationException('The token is invalid');
break;
case 'unverified_token':
throw new CustomUserMessageAuthenticationException('Unverified Token');
break;
case 'expired_token':
// throw new ExpiredTokenException();
throw new CustomUserMessageAuthenticationException('The token is expired');
break;
default:
throw new MissingTokenException();
break;
}
}
$username = $data['username'];
return $this->em
->getRepository('AppBundle:User')
->findOneBy(['username' => $username]);
}
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new JsonResponse([
'message' => $exception->getMessageKey()
], 401);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
}
public function supportsRememberMe()
{
return false;
}
public function start(Request $request, AuthenticationException $authException = NULL)
{
throw new \Exception('Not used: entry_point from other authentication is used');
}
}
the token exists in header but when I try to decode it I have an error
{"message": "The token is invalid"} even if the token is validated by jwt.io please I need help that's almost 3 weeks that i try to manage this trouble
Try to replace :
username_parameter
password_parameter
to :
username_path
password_path
and don't forget to check in your user entity getUsername() return the right username_path.

LexikJWTAuthenticationBundle always get null user over token storage

I'm working with symfony at backend (api). The authentication process is handled by FosUserBundle, LexikJWTAuthenticationBundle and LdapTools... all works fine.
The problem is when I'm about to get the Authenticated user in a controller or service.
The user is authenticated by the Authorization header, does not exist 401 Exception
$this->container->get('security.token_storage')->getToken()->getUser()//null
$preAuthToken = $this->container->get('security.token_storage')->getToken();
$tmp = $this->container->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken);//i can get the username and roles
But the real proble is with the security system
if ($this->isGranted('ROLE_USER')) {
echo 'never gets here!!';
} else {
echo 'always';
}
The security system always fails because the user returned by getUser() is always null.
My question is: the LexikJWTAuthenticationBundle should not inject or replace the current user, token after a successfull authentication?
or should I do it programatically? I dont want to fall in bad practices..
thanks in advance!
security.yml info
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
LdapTools\Bundle\LdapToolsBundle\Security\User\LdapUser: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
ldap:
id: ldap_tools.security.user.ldap_user_provider
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/login
stateless: true
provider: fos_userbundle
anonymous: true
form_login:
check_path: /login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: AppBundle\Handler\AuthenticationFailureHandler
require_previous_session: false
guard:
authenticators:
- ldap_tools.security.ldap_guard_authenticator
logout: true
api:
pattern: ^/
stateless: true
lexik_jwt: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Auth. failure handler(just in case)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$token = $exception->getToken();
if (is_string($exception->getToken()->getUser())) {
$usuario = $this->container->get('fos_user.user_manager')->findUserByUsername($token->getUsername());
if ($usuario) {
$token = new UsernamePasswordToken($usuario, 'yes', 'public', $usuario->getRoles());
} else {
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
}
return $this->handleAuthenticationFail($request, $token, $exception);
}
public function handleAuthenticationFail(Request $request, TokenInterface $token, AuthenticationException $exception)
{
$username = $token->getUsername();
$password = $request->get('password');
if ($this->ldapManager->authenticate($username, $password)) {
return $this->container->get('lexik_jwt_authentication.handler.authentication_success')->handleAuthenticationSuccess($token->getUser());
}
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}

Resources