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:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
encoders:
App\Entity\ATblUsers:
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:
- App\Security\JwtAuthenticator
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 App\Security\JwtAuthenticator
:
namespace App\Security;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Firebase\JWT\JWT;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
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?
CodePudding user response:
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:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
encoders:
App\Entity\ATblUsers:
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:
- App\Security\JwtAuthenticator
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 }
CodePudding user response:
I have not looked at your code in detail, I just want to tell you that what you are doing hard already exists in a bundle maintained and well documented and you will not need to hardcode, I really invite you to use it is very useful.