lexik / LexikJWTAuthenticationBundle

JWT authentication for your Symfony API
MIT License
2.52k stars 610 forks source link

JWTToken change fields for generate #457

Open shubaivan opened 6 years ago

shubaivan commented 6 years ago

I use Symfony 3.3.10 with "lexik/jwt-authentication-bundle": "~2.0", and for user_identity_field I use email

lexik_jwt_authentication:
private_key_path: '%jwt_private_key_path%'
public_key_path:  '%jwt_public_key_path%'
pass_phrase:      '%jwt_key_pass_phrase%'
token_ttl:        '%jwt_token_ttl%'
user_identity_field: email

But after my user, call put request with email, I mean change email to something another. after have been change, when I call another api, example get user profile, I have error:

{
  "code": 401,
  "message": "Unable to load an user with property \"email\" = \"q@q.com\". If the user identity has changed, you must renew the token. Otherwise, verify that the \"lexik_jwt_authentication.user_identity_field\" config option is correctly set."
}

I understood, because I change email, which part for generating token, but I thought token saved in session and don't need regenerate token after change email or password.

My question how to change generating token fields, example I want use some constant field, example id?

saft1g commented 6 years ago

i have the same issue with two differences: user_identity_field: id

and my id of the user entity is not auto increment: `/**

example: 9186b8bd-170b-11e8-82f8-480fcf324d89

and i use Symfony 4

bonfante commented 6 years ago

The user entity method getusername is returning the email?

HecFranco commented 6 years ago

How can I change the user_identity_field username (default value) an email? I use symfony 4 and I get this error "message": "The key \"_username\" must be a string, \"NULL\" given.",. Someone found a solution?

alex74m commented 6 years ago

Hi @HecFranco, did you find a solution ?

chalasr commented 6 years ago

@HecFranco that's for the form_login part which is a symfony thing, not related to this bundle. What you need is:

form_login:
    # ...
    username_parameter: email
alex74m commented 6 years ago

Well, the error is

The key "email" must be a string, "NULL" given.

chalasr commented 6 years ago

Then your HTTP request is incorrect, it just does not contain the email parameter.

chalasr commented 6 years ago

Again, that's a symfony issue, form_login is part of symfony. You're looking for https://symfony.com/support

RajeshwariN commented 5 years ago

I use Symfony 3.3.11 with "lexik/jwt-authentication-bundle": "2.4.4", and user_identity_field - mobileNo

I get this error on consuming login API

eerror": { "code": 400, "message": "Bad Request", "exception": [ { "message": "The key \"username\" must be provided.", ...... } ] }

EresDev commented 5 years ago

I use Symfony 3.3.11 with "lexik/jwt-authentication-bundle": "2.4.4", and user_identity_field - mobileNo

I get this error on consuming login API

eerror": { "code": 400, "message": "Bad Request", "exception": [ { "message": "The key "username" must be provided.", ...... } ] }

The answer to your problem is right there. If you are not using the key 'username' for authentication, you must specify it. For example, if you are using 'mobileNo' for authentication, you must specify it in your security.yaml.

form_login:
    # ...
    username_parameter: mobileNo
RajeshwariN commented 5 years ago

@EresDev thanks for help! I updated user_identity_field in config.yml and it worked

saifgo commented 4 years ago

login: # json_login: # username_path: email

HashtagAssist commented 3 years ago

Hello :)) Have I misunderstood something? I want to use the field "email" to get the token at the endpoint "check_login". I have exactly the same problem as @RajeshwariN and have tried the solution of @EresDev. Or the one from @saifgo because I use json_login. But it doesn't work. "message": "The key "username" must be provided.".. I have change the following parts in the security.yaml

 providers:
        entity_provider:
            entity:
                class: App\Entity\User
                property: email
  firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            json_login:
                username_path: email
                check_path: /login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

and into lexik_jwt_authentication.yaml

lexik_jwt_authentication:
    secret_key:       '%kernel.project_dir%/config/jwt/private.pem' # required for token creation
    public_key:       '%kernel.project_dir%/config/jwt/public.pem'  # required for token verification
    pass_phrase:      '' # required for token creation, usage of an environment variable is recommended
    token_ttl:        3600
    user_identity_field: email
RajeshwariN commented 3 years ago

@HashtagAssist Try by adding provider: entity_provider under login

HashtagAssist commented 3 years ago

Thanks for the quick response! Anyway that dont help :( i still get the the token by this json {"username":"test","password":"test"}. {"username":"test@test.de","password":"test"} return "Invalid credentials" . And {"email":"test@test.de","password":"test"} result in a 400 (The key "username" must be provided.)

gettmure commented 3 years ago

I have the same issue, while I am trying to redirect to login api route, but it gives me The key "password" must be provided. My controller returns a redirect response:

return $this->redirectToRoute('api_login', [
    'email'    => $user->getEmail(),
    'password' => $user->getPassword(),
], Response::HTTP_TEMPORARY_REDIRECT);

My security.yaml is configured with the following code:

security:
    role_hierarchy:
        ROLE_ADMIN:         [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN:   [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
        json_provider:
            entity:
                class: 'App\Entity\Security\User'
                property: 'id'

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        refresh:
            pattern: ^/api/auth/token/refresh
            stateless: true
            anonymous: true

        api_registration:
            pattern: ^/api/auth/register
            stateless: true
            anonymous: true

        api_send_reset_email:
            pattern: ^/api/auth/reset/send-email
            stateless: true
            anonymous: true

        api_reset_password:
            pattern: ^/api/auth/reset/reset-password
            stateless: true
            anonymous: true

        api_login:
            pattern: ^/api/auth/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            json_login:
                check_path: /api/auth/login
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern: ^/api
            stateless: true
            provider: json_provider
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
                provider: json_provider

        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     sonata_user_admin_security_login
                use_forward:    true
                check_path:     sonata_user_admin_security_check
                failure_path:   null

            logout:
                path:           sonata_user_admin_security_logout

            remember_me:
                secret:      '%env(APP_SECRET)%'
                path:       /
                lifetime:   2592000 # 1 month in seconds

            anonymous:          true

        main:
            pattern: .*
            context: user
            anonymous: true
            logout: true

        default:
            anonymous: ~

    access_control:
#        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Admin login page needs to be access without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # 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: ^/eloquent-media/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

    access_decision_manager:
        strategy: unanimous

And 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:           86400
    user_identity_field: id

Any suggestions?

priyankatotala commented 3 years ago

I have the same issue, while I am trying to redirect to login api route, but it gives me The key "password" must be provided. My controller returns a redirect response:

return $this->redirectToRoute('api_login', [
    'email'    => $user->getEmail(),
    'password' => $user->getPassword(),
], Response::HTTP_TEMPORARY_REDIRECT);

My security.yaml is configured with the following code:

security:
    role_hierarchy:
        ROLE_ADMIN:         [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN:   [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
        json_provider:
            entity:
                class: 'App\Entity\Security\User'
                property: 'id'

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        refresh:
            pattern: ^/api/auth/token/refresh
            stateless: true
            anonymous: true

        api_registration:
            pattern: ^/api/auth/register
            stateless: true
            anonymous: true

        api_send_reset_email:
            pattern: ^/api/auth/reset/send-email
            stateless: true
            anonymous: true

        api_reset_password:
            pattern: ^/api/auth/reset/reset-password
            stateless: true
            anonymous: true

        api_login:
            pattern: ^/api/auth/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            json_login:
                check_path: /api/auth/login
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern: ^/api
            stateless: true
            provider: json_provider
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
                provider: json_provider

        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     sonata_user_admin_security_login
                use_forward:    true
                check_path:     sonata_user_admin_security_check
                failure_path:   null

            logout:
                path:           sonata_user_admin_security_logout

            remember_me:
                secret:      '%env(APP_SECRET)%'
                path:       /
                lifetime:   2592000 # 1 month in seconds

            anonymous:          true

        main:
            pattern: .*
            context: user
            anonymous: true
            logout: true

        default:
            anonymous: ~

    access_control:
#        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Admin login page needs to be access without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # 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: ^/eloquent-media/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

    access_decision_manager:
        strategy: unanimous

And 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:           86400
    user_identity_field: id

Any suggestions?

I assume you would have used this class use Symfony\Component\HttpFoundation\Request; to handle the requests. In symfony 5.2, somehow this class is not retrieving the post payloads. Instead use this class use SymfonyBundles\JsonRequestBundle\JsonRequestBundle; after installing it using composer require symfony-bundles/json-request-bundle command.

gettmure commented 3 years ago

I have the same issue, while I am trying to redirect to login api route, but it gives me The key "password" must be provided. My controller returns a redirect response:

return $this->redirectToRoute('api_login', [
    'email'    => $user->getEmail(),
    'password' => $user->getPassword(),
], Response::HTTP_TEMPORARY_REDIRECT);

My security.yaml is configured with the following code:

security:
    role_hierarchy:
        ROLE_ADMIN:         [ROLE_USER, ROLE_SONATA_ADMIN]
        ROLE_SUPER_ADMIN:   [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    encoders:
        FOS\UserBundle\Model\UserInterface: sha512

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email
        json_provider:
            entity:
                class: 'App\Entity\Security\User'
                property: 'id'

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        refresh:
            pattern: ^/api/auth/token/refresh
            stateless: true
            anonymous: true

        api_registration:
            pattern: ^/api/auth/register
            stateless: true
            anonymous: true

        api_send_reset_email:
            pattern: ^/api/auth/reset/send-email
            stateless: true
            anonymous: true

        api_reset_password:
            pattern: ^/api/auth/reset/reset-password
            stateless: true
            anonymous: true

        api_login:
            pattern: ^/api/auth/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            json_login:
                check_path: /api/auth/login
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern: ^/api
            stateless: true
            provider: json_provider
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator
                provider: json_provider

        admin:
            pattern:            /admin(.*)
            context:            user
            form_login:
                provider:       fos_userbundle
                login_path:     sonata_user_admin_security_login
                use_forward:    true
                check_path:     sonata_user_admin_security_check
                failure_path:   null

            logout:
                path:           sonata_user_admin_security_logout

            remember_me:
                secret:      '%env(APP_SECRET)%'
                path:       /
                lifetime:   2592000 # 1 month in seconds

            anonymous:          true

        main:
            pattern: .*
            context: user
            anonymous: true
            logout: true

        default:
            anonymous: ~

    access_control:
#        # URL of FOSUserBundle which need to be available to anonymous users
        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # Admin login page needs to be access without credential
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        # 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: ^/eloquent-media/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
        - { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

    access_decision_manager:
        strategy: unanimous

And 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:           86400
    user_identity_field: id

Any suggestions?

I assume you would have used this class use Symfony\Component\HttpFoundation\Request; to handle the requests. In symfony 5.2, somehow this class is not retrieving the post payloads. Instead use this class use SymfonyBundles\JsonRequestBundle\JsonRequestBundle; after installing it using composer require symfony-bundles/json-request-bundle command.

I've already figured out, that trouble is in 307 response. It just does not support this behaviour. It can pass only those parameters, which are in the request. In this situation it would work, if I will pass email and password fields in the request body.

But thank you for the reply! :3

eciosilva commented 2 years ago

The user entity method getusername is returning the email?

Thank U very much! This solved for me... I had changed the login field to phone number, but (my mistake, I know) I forgot to change that info on User::getUsername() method at my entity class!!

ujikstark commented 2 years ago

Hello :)) Have I misunderstood something? I want to use the field "email" to get the token at the endpoint "check_login". I have exactly the same problem as @RajeshwariN and have tried the solution of @EresDev. Or the one from @saifgo because I use json_login. But it doesn't work. "message": "The key "username" must be provided.".. I have change the following parts in the security.yaml

 providers:
        entity_provider:
            entity:
                class: App\Entity\User
                property: email
  firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            json_login:
                username_path: email
                check_path: /login_check
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

and into lexik_jwt_authentication.yaml

lexik_jwt_authentication:
    secret_key:       '%kernel.project_dir%/config/jwt/private.pem' # required for token creation
    public_key:       '%kernel.project_dir%/config/jwt/public.pem'  # required for token verification
    pass_phrase:      '' # required for token creation, usage of an environment variable is recommended
    token_ttl:        3600
    user_identity_field: email

it works properly