Skip to content
Advertisement

Check JWT (Firebase) Token with Symfony 5.3

I’m working on an API, and I had implemented a JWT to make it stateless. I created an AuthController, which returns a JWT when login information is correct. Here you can see the return code that generates the token:

/* RETURN MESSAGE */
$body = [
    'auth_token' => $jwt,
];
$json = new JsonResponse($body);
$json->setStatusCode(201, "Created");   // Headers
return $json;

This is the result when I run the authenticate method, un the URL localhost:8000/authenticate.
Now, what I would need to do is that, when a user tries to get another / URL, the program doesn’t allow him to reach it if he’s not passing the Bearer token in the request’s header. But it’s not working. The platform always allows me to enter any URL without setting an Authorization in the header.
Here’s my security file, where I tried to set this:

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:
        SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface: 'auto'

    encoders:
        AppEntityATblUsers:
            algorithm: bcrypt

    providers:
        users_in_memory: { memory: null }

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            # Anonymous property is no longer supported by Symfony. It is commented by now, but it will be deleted in
            # future revision:
            # anonymous: true
            guard:
                authenticators:
                    - AppSecurityJwtAuthenticator
            lazy: true
            provider: users_in_memory

            # 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
            stateless: 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: ^/admin, roles: ROLE_ADMIN }
        # - { path: ^/profile, roles: ROLE_USER }

And, finally, here’s my AppSecurityJwtAuthenticator:

namespace AppSecurity;

use DoctrineORMEntityManagerInterface;
use SymfonyComponentDependencyInjectionParameterBagContainerBagInterface;
use SymfonyComponentHttpFoundationJsonResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentSecurityCoreExceptionAuthenticationException;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreUserUserProviderInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use FirebaseJWTJWT;
use SymfonyComponentSecurityGuardAbstractGuardAuthenticator;

class JwtAuthenticator extends AbstractGuardAuthenticator
{
    private $em;
    private $params;

    public function __construct(EntityManagerInterface $em, ContainerBagInterface $params)
    {
        $this->em = $em;
        $this->params = $params;
    }

    public function start(Request $request, AuthenticationException $authException = null): JsonResponse
    {
        $body = [
            'message' => 'Authentication Required',
        ];
        return new JsonResponse($body, Response::HTTP_UNAUTHORIZED);
    }

    public function supports(Request $request): bool
    {
        return $request->headers->has('Authorization');
    }

    public function getCredentials(Request $request)
    {
        return $request->headers->get('Authorization');
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        try{
            $credentials = str_replace('Bearer ', '', $credentials);
            $jwt = (array) JWT::decode($credentials, $this->params->get('jwt_secret'), ['HS256']);
            return $this->em->getRepository('App:ATblUsers')->find($jwt['sub']);
        }catch (Exception $exception){
            throw new AuthenticationException($exception->getMessage());
        }

    }

    public function checkCredentials($credentials, UserInterface $user)
    {

    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
    {
        return new JsonResponse([
            'message' => $exception->getMessage()
        ], Response::HTTP_UNAUTHORIZED);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
    {
        return;
    }

    public function supportsRememberMe(): bool
    {
        return false;
    }
}

I’ve been looking at a lot of websites and tutorials, but not anyone is doing exactly what I need or are implementing very basic functionalities that don’t match with what I need. Almost all of that websites explain this using Symfony 4, but I’m using Symfony 5, so a lot of functions that use in tutorials are deprecated. Does someone know what I am missing?

Advertisement

Answer

You are probably missing access_control configuration in security.yaml:

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:
        SymfonyComponentSecurityCoreUserPasswordAuthenticatedUserInterface: 'auto'

    encoders:
        AppEntityATblUsers:
            algorithm: bcrypt

    providers:
        users_in_memory: { memory: null }

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            # Anonymous property is no longer supported by Symfony. It is commented by now, but it will be deleted in
            # future revision:
            # anonymous: true
            guard:
                authenticators:
                    - AppSecurityJwtAuthenticator
            lazy: true
            provider: users_in_memory

            # 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
            stateless: 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: ^/authenticate, roles: PUBLIC_ACCESS }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement