Bearer token authentification API avec Keycloak et SymfonyDans cet article, nous allons voir mettre en place une sécurisation d’api REST Symfony avec Keycloak avec un Bearer token.
Avant de commencer, vous devez avoir un environnement Docker avec Keycloack. Si cela n’est pas le cas, vous pouvez suivre mon tuto dédié :
Tuto : monter un environnement de dev Docker avec Traefik et OAuth2 prêt pour le micro-service Si vous installez encore autre chose que Docker et Git sur votre machine de dev, je vous partage ici de quoi améliorer…medium.com
Pour permettre l’accès aux utilisateurs à nos micro-services, nous allons utiliser le standard JWT.
Commençons par installer le bundle lexik/jwt-authentication-bundle
composer require lexik/jwt-authentication-bundle
Nous utilisons la configuration sans BDD. Nous devons créer la class User src/Security/User.php :
<?php
namespace App\Security;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUser;
final class User extends JWTUser
{
const ROLE_USER = 'ROLE_USER';
const ROLE_CLIENT = 'ROLE_CLIENT';
/** @var string */
private $email;
public function __construct(string $username, ?string $email, array $roles = [])
{
parent::__construct($username, $roles);
$this->email = $email;
}
/**
* {@inheritdoc}
*/
public static function createFromPayload($username, array $payload)
{
/* Ajoute le role user si user identifié
sinon c'est une génération du token avec un client ID / secret*/
if (array_key_exists('clientId', $payload)) {
$roles = [self::ROLE_CLIENT];
} else {
$roles = [self::ROLE_USER];
}
/* On récupère les roles du client qui porte le nom du micro-service */
/* !! CHANGE firstservice BY YOUR !! */
if (isset($payload['resource_access']) && isset($payload['resource_access']['firstservice'])) {
$roles = array_merge($roles, $payload['resource_access']['firstservice']['roles']);
}
return new static($username, array_key_exists('email', $payload) ? $payload['email'] : null, array_unique($roles));
}
public function getEmail(): ?string
{
return $this->email;
}
}Le ROLE_CLIENT et le ROLE_USER sont définis dans la class. Pas besoin de les avoir dans Keycloack. Pour ajouter un autre ROLE, il faut le faire dans Keycloack :
1 — On va créer un client pour notre Micro-service. Dans l’exemple on le nomme “firstservice”
2 — On va sur l’onglet “Roles” du client que l’on vient de créer et on ajoute un role par exemple “ROLE_CAN_VIEW”. Attention il faut que le nom du role comme par ROLE_ pour que Symfony le set correctement.
Ajout d’un role au niveau d’un client3- On assigne le role qu’on vient de créer à un client :
Assignation d’un role à un clientEt voila, lorsque l’on fait un call API avec un token, on voit dans le profile [mais aussi dans $this->getUser()] le role que l’on vient de créer et d’assigner à cette utilisateur.
Les roles de l’utilisateur remontent dans le profiler de SymfonyOn va ensuite modifier la configuration de Lexik JWT dans config/package/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)%'
user_identity_field: subAjouter dans le .env :
###> lexik/jwt-authentication-bundle ### JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=b4d6f960e1b29fda3a5ba016b633d9b1 ###< lexik/jwt-authentication-bundle ###
Dans Keycloak on récupère la public key (aller dans Keycloak admin puis realm settings > keys et clic sur Public Key . Copier le contenu et le mettre dans un fichier config/jwt/public.pem
-----BEGIN PUBLIC KEY----- PASTE THE KEY HERE -----END PUBLIC KEY-----
On va maintenant configurer le config/packages/security.yaml
security:
providers:
users_in_memory: { memory: null }
jwt:
lexik_jwt:
class: App\Security\User
enable_authenticator_manager: true
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: jwt
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_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/doc, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: [ ROLE_CLIENT, ROLE_USER ] }
- { path: ^/admin, roles: ROLE_USER }
- { path: ^/connect, roles: PUBLIC_ACCESS }
#- { path: ^/graphql, roles: [ ROLE_CLIENT, ROLE_USER ] }
# ...On termine avec la configuration de Nelmio config/nelmio_api_doc.yaml
nelmio_api_doc:
documentation:
info:
title: My App
description: This is an awesome app!
version: 1.0.0
components:
securitySchemes:
Bearer:
type: http
scheme: bearer
bearerFormat: JWT
security:
- Bearer: []
areas: # to filter documented areas
path_patterns:
- ^/api(?!/doc$) # Accepts routes under /api except /api/docVoila, normalement en allant sur http://local.first-service.fr/api/doc vous devriez avoir un bouton “Authorize” qui vous permettera de saisir un token Bearer. Sans cela vous aurez une 401
{
"code": 401,
"message": "JWT Token not found"
}Pour récupérer votre token, il faut faire un appel en POST sur l’url de Keycloak http://local.services-domain.fr/auth/realms/master/protocol/openid-connect/token

Voila pour la sécurisation via un Bearer token Keycloak.
Prochaine partie : Mise en place de l’oauth2 pour se connecter à un back office easy admin.