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

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

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15

Most upvoted comments

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

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.