O que é o JWT e como ele funciona?
JWT (JSON Web Token) é um método de autenticação que consiste em um token criptografado que contém informações sobre o usuário. Ele é utilizado em web APIs para autenticar e autorizar usuários sem a necessidade de armazenar informações de sessão no servidor.
O JWT é composto por três partes: o header, o payload e a assinatura. O header contém informações sobre o algoritmo usado para gerar a assinatura e o tipo do token. O payload contém as informações do usuário, como por exemplo o ID e as permissões. A assinatura é criptografada usando a chave secreta do servidor.
Para validar um JWT, o servidor decodifica a assinatura usando a chave secreta. Se a assinatura for válida, o servidor pode confiar nas informações do payload do JWT para autenticar o usuário.
Qual é o papel do JWT no Symfony?
O Symfony é um framework PHP poderoso para desenvolvimento web e usado por muitas empresas e desenvolvedores ao redor do mundo. Uma das principais funcionalidades do Symfony é autenticação dos usuários. O Symfony também tem suporte para autenticação baseada em tokens, que é onde entra o JWT.
O JWT é usado no Symfony para autenticação de usuários em APIs, devido à sua capacidade de armazenar informações de sessão no próprio token criptografado. O JWT permite que o Symfony autentique o usuário sem precisar armazenar informações de sessão no servidor, o que é uma opção de escalabilidade para grandes projetos.
Além disso, o uso de JWT em APIs permite a autenticação e autorização de usuários sem a necessidade de enviar credenciais de autenticação em cada requisição. Algo que, além de mais seguro, também aumenta o desempenho da aplicação.
Como gerar um JWT no Symfony?
Para usar o JWT em um projeto Symfony, você precisará de uma biblioteca como o LexikJWTAuthenticationBundle. Para começar, é preciso configurar o bundle em seu projeto Symfony e gerar as chaves pública e privada para assinar sua API.
O processo de criação do JWT é relativamente simples. Primeiro, você precisará de uma instância do gerador de token JWT. Em seguida, você pode usar o método encode() para gerar um novo JWT e definir as informações que você deseja inserir no payload do token.
use LexikBundleJWTAuthenticationBundleServicesJWTTokenManagerInterface;
use SymfonyComponentSecurityCoreUserUserInterface;
class TokenAuthenticator
{
private $jwtManager;
public function __construct(JWTTokenManagerInterface $jwtManager)
{
$this->jwtManager = $jwtManager;
}
public function createToken(UserInterface $user)
{
$payload = [
'sub' => $user->getUsername(),
'exp' => time() + 3600 //expires in an hour
];
return $this->jwtManager->create($payload);
}
}
Como validar um JWT no Symfony?
Validar um JWT no Symfony é tão fácil quanto gerá-lo. Você precisará de uma instância do validador de token JWT, que normalmente deve ser obtido por meio de injeção de dependência.
O processo começa com você precisando decodificar o token usando a chave secreta do servidor. Em seguida, você pode verificar se o token ainda é válido, comparando-o com a data atual. Se tudo estiver correto, você terá um objeto UserInterface para seu usuário.
use LexikBundleJWTAuthenticationBundleServicesJWTTokenManagerInterface;
use SymfonyComponentSecurityCoreUserUserInterface;
class TokenAuthenticator
{
private $jwtManager;
public function __construct(JWTTokenManagerInterface $jwtManager)
{
$this->jwtManager = $jwtManager;
}
public function validateToken(string $token): ?UserInterface
{
$payload = $this->jwtManager->decode($token);
if (!$payload) {
return null;
}
$exp = $payload['exp'];
if (time() > $exp) {
return null;
}
$username = $payload['sub'];
// load your user object based on username
$user = $this->userRepository->loadUserByUsername($username);
return $user;
}
}
Como proteger rotas usando JWT no Symfony?
Para proteger rotas usando JWT no Symfony, você precisa usar o guard authenticator. O guard authenticator permite que você autentique usuários com diferentes esquemas de autenticação, incluindo o JWT.
Para começar, crie um novo autenticador de guarda e configure-o para usar o JWT como esquema de autenticação. Em seguida, use a anotação @Guard no controlador para proteger as rotas que exigem autenticação.
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreUserUserProviderInterface;
use SymfonyComponentSecurityGuardAbstractGuardAuthenticator;
use SymfonyComponentSecurityCoreExceptionAuthenticationException;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationJsonResponse;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentSecurityCoreSecurity;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
private $jwtManager;
private $userProvider;
public function __construct(JWTTokenManagerInterface $jwtManager, UserProviderInterface $userProvider)
{
$this->jwtManager = $jwtManager;
$this->userProvider = $userProvider;
}
public function supports(Request $request)
{
return $request->headers->has('Authorization');
}
public function getCredentials(Request $request)
{
$token = str_replace('Bearer ', '', $request->headers->get('Authorization'));
return $token;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $this->validateToken($credentials);
}
public function checkCredentials($credentials, UserInterface $user)
{
return is_object($user);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = [
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
];
return new JsonResponse($data, 401);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return null;
}
public function start(Request $request, AuthenticationException $authException = null)
{
$data = [
'message' => 'Authentication Required'
];
return new JsonResponse($data, 401);
}
public function supportsRememberMe()
{
return false;
}
public function validateToken(string $token): ?UserInterface
{
$payload = $this->jwtManager->decode($token);
if (!$payload) {
return null;
}
$exp = $payload['exp'];
if (time() > $exp) {
return null;
}
$username = $payload['sub'];
// load your user object based on username
$user = $this->userProvider->loadUserByUsername($username);
return $user;
}
}
Como configurar o JWT no Symfony?
Para configurar o JWT no Symfony, você precisará de uma biblioteca que ofereça suporte ao JWT e ao guard authenticator, como a biblioteca LexikJWTAuthenticationBundle.
Depois de instalar a biblioteca, adicione as configurações necessárias ao arquivo app/config/security.yml. Você precisará definir a chave secreta do servidor, o token TTL (expirar após um determinado período) e o caminho de autenticação.
# app/config/security.yml
security:
providers:
app_user_provider:
entity:
class: AppBundle:User
property: username
firewalls:
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
encoders:
AppBundleEntityUser:
algorithm: bcrypt
lexik_jwt_authentication:
secret_key: '%env(APP_SECRET)%'
public_key: '%kernel.project_dir%/config/jwt/public.pem'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
Como injetar credenciais JWT em solicitações?
Uma das maneiras mais comuns de injetar credenciais JWT em solicitações do cliente é usando o cabeçalho Authorization. O cabeçalho Authorization é um cabeçalho HTTP padrão que é usado para incluir informações de autenticação em uma solicitação HTTP.
headers: {
Authorization: 'Bearer ' + JWT_TOKEN
}
Como personalizar as respostas de erro JWT?
As respostas de erro JWT podem ser personalizadas em sua aplicação Symfony. Para fazer isso, você precisa definir configurações para cada resposta de erro específica na configuração de autenticação.
Ao fazer isso, você pode alterar as mensagens de erro padrão, as respostas HTTP e muito mais. Por exemplo, você pode adicionar conteúdo personalizado à resposta 401 ou colocar a mensagem de erro em um formato diferente.
# app/config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
# ...
unauthorized_entry_point:
#...
message: 'Custom Unauthorized Message'
#...
authentication_failure_handler:
#...
failure_message: 'Custom Authentication Failure Message'
#...
Como usar o JWT com OAuth2 no Symfony?
Usar o JWT com OAuth2 é uma escolha popular, já que o JWT é uma forma segura e escalável para autenticar usuários, e o OAuth2 é uma maneira confiável de autorizar solicitações em nome desses usuários.
Para usar o JWT com OAuth2 no Symfony, você precisará de uma biblioteca que ofereça suporte a esses protocolos, como a biblioteca OAuth2 Server do Symfony. Depois de instalar a biblioteca, você pode configurá-la para usar o JWT como método de autenticação.
# app/config/packages/hwi_oauth_server.yaml
hwi_oauth:
firewall_names: [api]
resource_owners:
github:
type: github
client_id: %github_client_id%
client_secret: %github_client_secret%
scope: "email"
paths:
email: emails
options:
csrf: false
# oauth server
server_url: "/api/oauth"
access_token_ttl: 3600
refresh_token_ttl: 604800
# jwt
use_accesstoken_as_code: false
token_formatter: 'hwi_oauth.token_formatter.jwt'
jwt_private_key_path: '%kernel.root_dir%/config/jwt/private.pem'
jwt_key_pass_phrase: '%env(JWT_PASS_PHRASE)%'
token_parameter_name: 'access_token'
Como trabalhar com múltiplos emissores (iss) em um JWT?
Trabalhar com múltiplos emissores em um JWT é uma opção útil em muitos casos, como quando você precisa de diferentes emissores para diferentes tipos de usuários ou aplicativos. Para fazer isso no Symfony, você pode usar a biblioteca LexikJWTAuthenticationBundle.
Para começar, você precisará configurar a biblioteca para trabalhar com múltiplos emissores. Em seguida, você pode gerar tokens contendo diferentes emissores, dependendo de suas necessidades.
# app/config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
# ...
token_extractors:
#...
custom_iss:
#...
pattern: '^/api/custom-iss'
emitters:
- iss-one
- iss-two
#...
Como implementar o refreshToken em um JWT no Symfony
O refreshToken é uma maneira de prolongar a vida útil do JWT de um usuário sem exigir que o usuário faça login novamente. Isso é muito conveniente para usuários que passam muito tempo em sua aplicação Symfony.
Para implementar o refreshToken em um JWT no Symfony, você precisará de uma biblioteca que ofereça suporte ao refreshToken, como a biblioteca Symfony/lexik-jwt-authentication-bundle. A biblioteca oferece suporte ao refreshToken out-of-the-box.
# app/config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
token_ttl: 3600 # 1 hour
refresh_token:
enabled: true
ttl: 604800 # 1 week