lexik/LexikJWTAuthenticationBundle

Does not validate token and header

PartosK opened this issue · 2 comments

Hi, help me figure it out, I don’t understand which way to figure it out.
If I add an Authorization header and an arbitrary token I get the result of the request, also if I just remove the header it will return the same result, but if I put an expired token it will throw an exception that the token is not valid.

lexik/jwt-authentication-bundle v2.6.5

security.yaml

security:
  encoders:
    App\User\Domain\Entity\User:
      algorithm: bcrypt

  providers:
    app_users:
      entity: { class: App\User\Domain\Entity\User }
    ldap:
      id: App\User\Infrastructure\Providers\LdapUserProviderInterface

    chain_provider:
      chain:
        providers: [ldap, app_users]

  role_hierarchy:
    ROLE_SUPER: [ROLE_ADMIN, ROLE_USER, ROLE_CHIEF, ROLE_ANALYST, ROLE_1C, ROLE_PUBLISHER]
    ROLE_ADMIN: [ROLE_USER, ROLE_CHIEF, ROLE_ANALYST, ROLE_1C, ROLE_PUBLISHER]
    ROLE_ANALYST: ROLE_USER
    ROLE_PUBLISHER: ROLE_USER
    ROLE_CHIEF: ROLE_USER
    ROLE_1C: ROLE_USER

  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false
    api_login:
      pattern:  ^/api/login
      stateless: true
      anonymous: true
      json_login_ldap:
        service: Symfony\Component\Ldap\Ldap
        dn_string: '%env(LDAP_AUTH_DN)%'
        query_string: '%env(LDAP_AUTH_QUERY)%'
        search_dn: '%env(LDAP_USER_DN)%'
        search_password: '%env(LDAP_PASS)%'
        provider: ldap
        check_path:      /api/login
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
      json_login:
        provider: app_users
        check_path:      /api/login
        success_handler: lexik_jwt_authentication.handler.authentication_success
        failure_handler: lexik_jwt_authentication.handler.authentication_failure
    api_graphql:
      pattern: ^/api/graphql
      security: true
      provider: chain_provider
      guard:
        entry_point: lexik_jwt_authentication.jwt_token_authenticator
        authenticators:
          - lexik_jwt_authentication.jwt_token_authenticator
    export:
      pattern: ^/(uploads/user-history|price-list|recommendation/export|plot/export|sse|uploads)
      stateless: true
      security: true
      provider: chain_provider
      guard:
        authenticators:
          - app.with_query_param_authenticator


  # 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, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/graphql, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/reset_password, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/recommendation/export, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/sse, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/price-list, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/plot/export, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/uploads/user-history, role: IS_AUTHENTICATED_FULLY }
    - { path: ^/uploads, role: IS_AUTHENTICATED_FULLY }

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: '%env(JWT_TOKEN_TTL)%'

I came up with this solution:

class JWTTokenAuthenticator2 extends LexikJWTTokenAuthenticator
{

    protected const HTTP_JWT_HEADER = 'Authorization';

    public function supports(Request $request)
    {
        if (!($request->headers->has(self::HTTP_JWT_HEADER))) {
            $this->addNullJWT($request);
        } elseif (
            $request->headers->has(self::HTTP_JWT_HEADER)
            && empty($request->headers->get(self::HTTP_JWT_HEADER))
        ) {
            $this->addNullJWT($request);
        } elseif (
            $request->headers->has(self::HTTP_JWT_HEADER)
            && !empty($request->headers->get(self::HTTP_JWT_HEADER))
            && strpos($request->headers->get(self::HTTP_JWT_HEADER), 'Bearer') === false
        ) {
            $this->addNullJWT($request);
        }

        return parent::supports($request);
    }

    private function addNullJWT(Request $request): void
    {
        $request->headers->add([self::HTTP_JWT_HEADER => 'Bearer null']);
    }
}

I'd need some reproducer to confirm the bug. Feel free to reopen if you can provide such code. Thanks