Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"code": 401, "message": "Invalid credentials." while passing the jwt #906

Closed
Humb3l opened this issue Aug 15, 2021 · 15 comments
Closed

"code": 401, "message": "Invalid credentials." while passing the jwt #906

Humb3l opened this issue Aug 15, 2021 · 15 comments

Comments

@Humb3l
Copy link

Humb3l commented Aug 15, 2021

Hey,
I'm currently trying to implement the JWT Authentication Bundle in my API Platform Project.
Everything has worked well so far, except that I'm unable to access my resources with a generated JWT Token.

I'm able to generate a JWT Token, when passing a username and a password to the backend.
But as soon as I'm trying to use this token I receive this 401 error.

The request is passed with http to the backend on my localhost, I also tried https.
I manually validated the jwt on jwt.io and everything seems to be fine with the token.

The API Platform project runs in a docker container.

If you need some further information, just let me know

Operating System: Mac OS

My security.yml

security:
    password_hashers:
        # use your user class name here
        App\Entity\User:
            # Use native password hasher, which auto-selects the best
            # possible hashing algorithm (starting from Symfony 5.3 this is "bcrypt")
            algorithm: auto
    encoders:
        App\Entity\User:
            algorithm: auto

    # https://symfony.com/doc/current/security/experimental_authenticators.html
    enable_authenticator_manager: true
    # 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
        public:
            methods: [POST]
            pattern: ^/user/create
            security: false
        login:
            methods: [POST]
            pattern: ^/authentication_token
            json_login:
                check_path: /authentication_token
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
        main:
            stateless: true
            # anonymous: true
            provider: app_user_provider
            guard:
                authenticators:
                     - lexik_jwt_authentication.jwt_token_authenticator

            # 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: ^/users, roles: PUBLIC, methods: [POST] }
        - { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Allows accessing the Swagger UI
        - { path: ^/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

api-platform.yml

api_platform:
    title: Hello API Platform
    version: 1.0.0
    mapping:
        paths: ['%kernel.project_dir%/src/Entity']
    patch_formats:
        json: ['application/merge-patch+json']
    swagger:
        versions: [3]
        api_keys:
            apiKey:
                name: Authorization
                type: header
    # Mercure integration, remove if unwanted
    mercure: ~
    # Good cache defaults for REST APIs
    defaults:
        stateless: true
        cache_headers:
            vary: ['Content-Type', 'Authorization', 'Origin']

Lexikon_jwt_authentication.yml

lexik_jwt_authentication:
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
    pass_phrase: '%env(JWT_PASSPHRASE)%'

JwtDecorator.php

<?php
// api/src/OpenApi/JwtDecorator.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 JwtDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private OpenApiFactoryInterface $decorated
    ) {}

    public function __invoke(array $context = []): OpenApi
    {
        $openApi = ($this->decorated)($context);
        $schemas = $openApi->getComponents()->getSchemas();

        $schemas['Token'] = new \ArrayObject([
            'type' => 'object',
            'properties' => [
                'token' => [
                    'type' => 'string',
                    'readOnly' => true,
                ],
            ],
        ]);
        $schemas['Credentials'] = new \ArrayObject([
            'type' => 'object',
            'properties' => [
                'email' => [
                    'type' => 'string',
                    'example' => 'johndoe@example.com',
                ],
                'password' => [
                    'type' => 'string',
                    'example' => 'apassword',
                ],
            ],
        ]);

        $pathItem = new Model\PathItem(
            ref: 'JWT Token',
            post: new Model\Operation(
                operationId: 'postCredentialsItem',
                tags: ['Token'],
                responses: [
                    '200' => [
                        'description' => 'Get JWT token',
                        'content' => [
                            'application/json' => [
                                'schema' => [
                                    '$ref' => '#/components/schemas/Token',
                                ],
                            ],
                        ],
                    ],
                ],
                summary: 'Get JWT token to login.',
                requestBody: new Model\RequestBody(
                    description: 'Generate new JWT Token',
                    content: new \ArrayObject([
                        'application/json' => [
                            'schema' => [
                                '$ref' => '#/components/schemas/Credentials',
                            ],
                        ],
                    ]),
                ),
            ),
        );
        $openApi->getPaths()->addPath('/authentication_token', $pathItem);

        return $openApi;
    }
}

image

@fd6130
Copy link

fd6130 commented Aug 16, 2021

Maybe something wrong with your security.yaml? I see some old config that shouldn't appear in there since you have enable_authenticator_manager: true.

Is it ok to have password_hashers and encoders in the same config file (not sure about this tho)? I think you should remove the encoders and also the guard stuff.

How about change to the following config and try again?

https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.md#configuration

@Humb3l
Copy link
Author

Humb3l commented Aug 16, 2021

Hey, thank you for trying to help :)
But sadly trying out the new config hasn't changed anything :/

I also noticed that when I make a request via the UI of the API platform I even get a 401 JWT not found, which is somehow also quite strange.
image

Despite the fact that I have inserted a token
image

This is the new security.yml:

security:
    enable_authenticator_manager: true
    # ...

    password_hashers:
        # use your user class name here
        App\Entity\User:
            # Use native password hasher, which auto-selects the best
            # possible hashing algorithm (starting from Symfony 5.3 this is "bcrypt")
            algorithm: auto
    # encoders:
    #     App\Entity\User:
    #         algorithm: auto
    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
        public:
            methods: [POST]
            pattern: ^/user/create
            security: false
        login:
            pattern: ^/authentication_token
            stateless: true
            json_login:
                check_path: /authentication_token
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern:   ^/
            stateless: true
            jwt: ~

    access_control:
        - { path: ^/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Allows accessing the Swagger UI
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

@chalasr chalasr closed this as completed Aug 16, 2021
@chalasr chalasr reopened this Aug 16, 2021
@chalasr
Copy link
Collaborator

chalasr commented Aug 16, 2021

Hey, sorry for the close/reopen, that's a misclick.
Are you using the apache webserver? If yes, check https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.md#important-note-for-apache-users. Otherwise, please create and share an application with just enough code to reproduce the issue.

@fd6130
Copy link

fd6130 commented Aug 17, 2021

Hey, thank you for trying to help :)
But sadly trying out the new config hasn't changed anything :/

I also noticed that when I make a request via the UI of the API platform I even get a 401 JWT not found, which is somehow also quite strange.
image

Despite the fact that I have inserted a token
image

This is the new security.yml:

security:
    enable_authenticator_manager: true
    # ...

    password_hashers:
        # use your user class name here
        App\Entity\User:
            # Use native password hasher, which auto-selects the best
            # possible hashing algorithm (starting from Symfony 5.3 this is "bcrypt")
            algorithm: auto
    # encoders:
    #     App\Entity\User:
    #         algorithm: auto
    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
        public:
            methods: [POST]
            pattern: ^/user/create
            security: false
        login:
            pattern: ^/authentication_token
            stateless: true
            json_login:
                check_path: /authentication_token
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure

        api:
            pattern:   ^/
            stateless: true
            jwt: ~

    access_control:
        - { path: ^/authentication_token, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Allows accessing the Swagger UI
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

Are you using Bearer Token header? Cause i see you only have Authorization: <jwt> in your request but not Authorization: Bearer <jwt>.

If you using apache, you might need to check this out https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.md#important-note-for-apache-users

@Humb3l
Copy link
Author

Humb3l commented Aug 17, 2021

I also noticed that the header in the Ui is not correct, so I always used Insomnia to set the header to authorzation bearer.

I'm using the symfony server (PHP Server), so this should actually work

@fd6130
Copy link

fd6130 commented Aug 17, 2021

I also noticed that the header in the Ui is not correct, so I always used Insomnia to set the header to authorzation bearer.

I'm using the symfony server (PHP Server), so this should actually work

So it works now? Did you manage to find the source of the issue?

@Humb3l
Copy link
Author

Humb3l commented Aug 17, 2021

No unfortunately it still does not work and I still do not know why.

No idea, maybe it's because of the php server

@fd6130
Copy link

fd6130 commented Aug 17, 2021

No unfortunately it still does not work and I still do not know why.

No idea, maybe it's because of the php server

Have you try this if your server are using apache? You may composer require symfony/apache-pack, it will download .htaccess to your /public directory. Perhaps you can modify something there?

https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.md#important-note-for-apache-users

I'm not sure if we can set it at .htaccess instead though.

@Humb3l
Copy link
Author

Humb3l commented Aug 19, 2021

To use the Symfony Apache-Pack doesn't seam to work either. I'll try to set up an Apache now and then try this again

@furang
Copy link

furang commented Sep 9, 2021

Having absolutely the same issue. Used docker-compose config from api platform. Followed all their instructions from here. After passing correct creds i get token, but when I try make request with it i get 401 error. Checked token on jwt.io. It says everything is correct and signature is valid.

UPD. I think I faced the reason.
Symfony\Bridge\Doctrine\Security\User\EntityUserProvider::loadUserByIdentifier
For some reason I receive here my username in $identifier var. But in my security settings i have

providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email

So user not found.
To confirm this. If i set property: username everything works fine.
I think this happens because you always put username in token. But not always this is unique property. Correct me if I'm wrong. Also mb there's a workaround how we can store another value in token (email e.g.).
Thanks beforehand!

UPD2. I guess I've solved the problem

# api/config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
    ....
    user_identity_field: email

solved the issue

@fd6130
Copy link

fd6130 commented Sep 10, 2021

To use the Symfony Apache-Pack doesn't seam to work either. I'll try to set up an Apache now and then try this again

What's the status of your progress? Did you manage successfully to authenticate?

@Humb3l
Copy link
Author

Humb3l commented Sep 10, 2021

To use the Symfony Apache-Pack doesn't seam to work either. I'll try to set up an Apache now and then try this again

What's the status of your progress? Did you manage successfully to authenticate?

Unfortunately I could not try anything else yet due to time constraints. But I will try out the suggested solutuion soon

@fd6130
Copy link

fd6130 commented Sep 10, 2021

To use the Symfony Apache-Pack doesn't seam to work either. I'll try to set up an Apache now and then try this again

What's the status of your progress? Did you manage successfully to authenticate?

Unfortunately I could not try anything else yet due to time constraints. But I will try out the suggested solutuion soon

And also like @furang suggested, do remember to check your user identify that are use for authenticate.

@chalasr
Copy link
Collaborator

chalasr commented Sep 16, 2021

Seems like the issue is solved as per #906 (comment).

@chalasr chalasr closed this as completed Sep 16, 2021
@theo-dubois
Copy link

theo-dubois commented Oct 8, 2023

user_identity_field: email

Thank you man ! It solved my issue, it should be automatic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants