Lexik JWT returns 401 Unauthorized - symfony

I am using LexikJWTBundle for a RESTful API.
Login works just fine and I get my token. But when I make a GET request I get a 401 with no content.
The Authorization header seems ok since I get this in the profiler:
Request Headers: authorization: Bearer {token}
Request Server Parameters: HTTP_AUTHORIZATION: Bearer {token}
The 401 I get is from: https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Security/Firewall/JWTListener.php#L80
I've tried different solutions but still it's not working.
Would you have any idea on how to resolve/debug this?
My config:
# config.yml
...
lexik_jwt_authentication:
private_key_path: %kernel.root_dir%/var/jwt/private.pem # ssh private key path
public_key_path: %kernel.root_dir%/var/jwt/public.pem # ssh public key path
pass_phrase: 'TEST' # ssh key pass phrase
token_ttl: 86400 # token ttl - defaults to 86400
And
# security.yml
security:
role_hierarchy:
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_SONATA_ADMIN, ROLE_ALLOWED_TO_SWITCH]
# http://sonata-project.org/bundles/admin/2-3/doc/reference/security.html
# set access_strategy to unanimous, else you may have unexpected behaviors
access_decision_manager:
strategy: unanimous
encoders:
FOS\UserBundle\Model\UserInterface: sha512
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login # Default: .*
provider: fos_userbundle
# form login
form_login:
login_path: fos_user_security_login
# csrf_provider: form.csrf_provider # Default: my.csrf_provider.id
# LexikJWT # 09/01/15 - Note: provient de la configuration officielle.
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
anonymous: true # Default: ~
api:
pattern: ^/api
stateless: true
lexik_jwt:
authorization_header: # check token in Authorization Header
enabled: true
prefix: Bearer
anonymous: true
access_control:
# Secured part of the site
# This config requires being logged for the whole site and having the admin role for the admin part.
# Change these rules to adapt them to your needs
- { path: "^/api/contacts$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [POST] }
- { path: "^/api/users/dt$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: "^/api/users$", roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [POST] }
- { path: "^/api", roles: [IS_AUTHENTICATED_FULLY, ROLE_API] }

I just found a solution on the same problem
Here my security.yml
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: user_db
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
api:
pattern: ^/api
stateless: true
lexik_jwt:
authorization_header: # check token in Authorization Header
enabled: true
prefix: Bearer
throw_exceptions: false # When an authentication failure occurs, return a 401 response immediately
create_entry_point: true # When no authentication details are provided, create a default entry point that returns a 401 response
authentication_provider: lexik_jwt_authentication.security.authentication.provider
My problem was the username and the password parameters. I change "username" by "_username" and "password" by "_password"

You should add the ROLE_API in the role_hierarchy of your security.yml :
role_hierarchy:
# ...
ROLE_API: [ROLE_USER]
Then, users ranked with ROLE_API can access routes restricted to IS_AUTHENTICATED_FULLY.
Also, if you are using a web server, try to use your application using the built-in server (i.e. app/console server:run).
Apache seems to modify the token in headers.

LexikJWTBundle generates token so user's credentials are valid.
Problem occurs when you try to access secured routes (behind "^/api" path).
You should definitely check roles assigned to user. Maybe ROLE_API is missing and user is not fully authenticated.

Related

Restricting HTTP Method in security.yaml doesn't work (Symfony, LexikJwk)

I'm trying to allow only to register (POST method) a new user (route: /api/users), I tried to follow the documentation (https://symfony.com/doc/current/security/firewall_restriction.html#restricting-by-http-methods), but when I test with Postman, I still manage to see all users with the GET method.
The security.yaml file :
security:
# https://symfony.com/doc/current/security/authenticator_manager.html
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#c-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
registration:
pattern: ^/api/users
stateless: true
methods: [POST]
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: ~
main:
lazy: true
provider: app_user_provider
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# 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: ^/api/login, roles: PUBLIC_ACCESS }
# - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Short version:
firewalls:
registration:
pattern: ^/api/users
stateless: true
methods: [POST]
What I should see when trying to access /api/users with the GET method is a code 401, "JWT Token not found".
But I don't, I see the users and their datas.
I had to configure it in the access control at the end:
access_control:
- { path: ^/api/users, roles: IS_AUTHENTICATED_FULLY, methods: [GET, PUT, DELETE] }

How to fix "The key _username must be a string" error with JWT

I have a website with a standard connection, and I want to add a new way to connect thanks to the API part.
I have followed this doc :
1https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/index.md#getting-started
So I add JWT bundle to my Symfony 3 project, and I changed my security.yml :
# app/config/security.yml
security:
encoders:
Symfony\Component\Security\Core\User\User: sha512
GS\UserBundle\Entity\User: sha512 #sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity:
class: GS\UserBundle\Entity\User
property: login
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: true
provider: main
stateless: true
form_login:
login_path: login
check_path: login_check
default_target_path: preLogin
logout:
path: logout
target: preLogin
remember_me:
secret: '%secret%' # se souvenir de moi
lifetime: 1000
always_remember_me: true
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: main
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
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: /admin, roles: ROLE_ADMIN }
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
I have configured my config.yml like this :
lexik_jwt_authentication:
secret_key: '%kernel.project_dir%/config/jwt/private.pem'
public_key: '%kernel.project_dir%/config/jwt/public.pem'
user_identity_field: login
pass_phrase: 'MY PASSWORD'
token_ttl: 3600
And When I tried to obtain my token with postman like this :
I have this error :
The key "_username" must be a string, "NULL" given.
So, I tried to add "user_parameter" in the security.yml in order to give the parameters name of my user entity, like this :
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check
username_parameter: login
password_parameter: mdp
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
And now Postman answer me nothing.
EDIT:
I find a commande to check my config file and I have this answer :
And if I remove the username_parameter and the password_parameter, the commande check tell me that the config is ok, but I have the same error with postman :
The key "username" must be a string, "NULL" given.
Help me please I don't find any solution...
Lexik JWT authentication config needs the provider key in the security.yaml.
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: YOUR_PRODIVER_NAME #in your case it's main
json_login:
check_path: /api/login_check
username_parameter: login
password_parameter: mdp
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
Then to be able to verify the user informations, it needs to know what field is the identity field.
So in the 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)%'
user_identity_field: YOUR_USER_FIELD # example email
token_ttl: 7200
About the request parameters, username and password have to be passed (No _)

not able to authenticate with LexikJWTAuthenticationBundle on Symfony 3.4

In my Symfony (3.4.24) project with FOSUserBundle (2.0.2) I installed LexikJWTAuthenticationBundle (2.6.3).
I installed and configured everything like said on the github page.
When running the following suggested curl command
curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/app_dev.php/api/login_check -d "{\"username\":\"johndoe\",\"password\":\"test\"}"
I allways get the following 401 response.
The response is the same for a non existing user or wrong password as well as for an existing valid user with correct password.
{"code":401,"message":"Bad credentials"}
My setup is the following:
config.yml:
(if I need to add more details let me know)
lexik_jwt_authentication:
secret_key: "%kernel.root_dir%/config/jwt/private.pem" # required for token creation
public_key: "%kernel.root_dir%/config/jwt/public.pem" # required for token verification
pass_phrase: "%jwt_key_pass_phrase%" # required for token creation, usage of an environment variable is recommended
token_ttl: "%jwt_token_tll%"
security.yml:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
...
providers:
in_memory:
memory: ~
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# Login for Lexik Json Web Token Authentication
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
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
# API for Lexik Json Web Token Authentication
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
pattern: ^/
form_login:
provider: fos_userbundle
check_path: fos_user_security_check
login_path: fos_user_security_login
csrf_token_generator: security.csrf.token_manager
default_target_path: fos_user_profile_show
always_use_default_target_path: true
logout:
path: fos_user_security_logout
target: default_loged_out_target
logout_on_user_change: true
anonymous: true
switch_user:
role: ROLE_ADMIN
provider: fos_userbundle
remember_me:
secret: '%secret%'
lifetime: 15552000
path: /
access_control:
...
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
routing.yml:
...
api_login_check:
path: /api/login_check
So - like said I allways get this response:
{"code":401,"message":"Bad credentials"}
Can anyone point into the right direction on how to fix this?
Everything seems to be OK but I suspect that since you have two providers (in_memory and fos_userbundle) from which of them does Lexik JWT looks for the credentials ? If you don't use the in_memory provider, you should try to remove it.

Symfony: Can I use both JWT + oAuth to authenticate different users?

Before explain why what I need your help, just keep in mind that I am totally new on Symfony 3.4, so please be lenient with me.
What I try to create is a RESTful API.
For the admin section, I'd like my users to log in every day in order to access the application dashboard. I already do that, by using the LexikJWTAuthenticationBundle with a token that expires every 24 hours.
Then, my API will have several client applications that should access my data using an access token, but not using JWT. Those client applications will be mobile apps ( implemented on React Native ) and desktop apps ( implemented on Electron).
The way I thought I should authorize those applications to access my API is by providing them with an Authentication token using the oAuth protocol.
So, the basic question is, if I could config both authorization methods ( JWT + oAuth ) at the same time for the same resources.
So, for example, let's say I have the following endpoint: /api/v1/repository/chocolates.
With the jms_serializer I am able to choose what data I want to display on each group, so for a user has the ROLE_ADMIN I can display all the product information, while for the user has the ROLE_SOME_GRANT will only be possible to see the title and the description.
So is it possible to access the /api/v1/repository/chocolates either by using the JWT token my admins have or by using the oAuth token my client apps will have?
For the moment my app/config/security.yml has the following setup:
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_NUTRITIONIST: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
security: false
api_user_management:
pattern: ^/api/v1/user/(login|request\/reset|password\/reset)
anonymous: true
stateless: true
api_user_management_safe:
pattern: ^/api/v1/user/(login|request\/reset|password\/reset)
anonymous: true
stateless: true
api_user_login:
pattern: ^/api/v1/login
stateless: true
anonymous: true
form_login:
check_path: /api/v1/login
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
logout: true
#############################################
## HERE IF I CHANGE THE fos_oauth to false
## I CAN ACCESS MY ENDPOINT : /api/v1/register/new/user
## WHILE IF THAT IS TRUE I GET THE ERROR MESSAGE:
##
## {
## "error": "invalid_grant",
## "error_description": "The access token provided is invalid."
## }
#############################################
api_access:
pattern: ^/api/v1
stateless: true
#lexik_jwt: ~
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
fos_oauth: true
access_control:
- { path: ^/api/v1/user/request/reset, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/v1/user/password/reset, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/v1/user/update, role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/user/((?!request)|(?!password)|(?!update)), role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/register/new/user, role: IS_AUTHENTICATED_FULLY }
- { path: ^/api/v1/, role: IS_AUTHENTICATED_FULLY }
So, based on that security setup do you think there's a way to have multiple authorization services in my application?

How to set provider to oauth token

I've set up OAuth [ using FOSOauthServerBudle ]. I'm working on setting up a password type flow.
I can get an access token with client type, but using password type I get an error.
Error: Call to a member function loadUserByUsername() on a non-object
More specifically
$user = $this->userProvider->loadUserByUsername($username); <-OAuthStorage.php at line 161
/oauth/v2/token?client_id=[CLIENTID]&client_secret=[CLIENTSECRET]&grant_type=password&username=test&password=test
So there's no user provider set for OAuthStorage. I can't find any good documentation on how to set a provider for this.
My security conf
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
user_provider:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
oauth_authorize:
pattern: ^/oauth/v2/auth
form_login:
provider: user_provider
check_path: _security_check
login_path: _demo_login
anonymous: true
api:
pattern: ^/api
fos_oauth: true
stateless: true
access_control:
# You can omit this if /api can be accessed both authenticated and anonymously
- { path: ^/api, roles: [ IS_AUTHENTICATED_FULLY ] }
What do you think guys. Any ideas?
Eureka!
So apparently the provider is configured in config.yml. I had it set to a previous custom user provider. I set it to the same provider as specified in security.yml and everything works just fine.

Resources