Integração keycloak 26.1.4 com laravel 12

Meu objetivo principal é armazenar, meu conhecimento adquirido com a integração que eu fiz do keycloak com o laravel. Deste modo não tratarei aqui de temas anteriores como instalação do php, composer, laravel e docker usando as imagens do docker hub. Não só porque na internet já possui muitos bons conteúdos, mas isso me tiraria do foco neste momento. Também vale lembrar que estou utilizando o ubuntu.
Estrutura
Postgresql
keycloak
laravel 12
Para o keycloak e o postgresql utilizei o docker. A primeira coisa que eu fiz foi subir uma imagem do docker.compose.yaml. Depois criei um repositório onde criei o projeto laravel.
services:
postgres:
image: postgres:16
container_name: postgres_lista_compras
restart: unless-stopped
environment:
POSTGRES_DB: db_lista_compras
POSTGRES_USER: adm_lista_compras
POSTGRES_PASSWORD: "<minha-senha>" # meu password, se tiver caracteres especiais precisa estar dentro das ""
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
keycloak:
image: quay.io/keycloak/keycloak:26.1.4
container_name: keycloak_lista_compras
restart: unless-stopped
command: ["start-dev"]
environment:
KC_DB: postgres
KC_DB_URL_HOST: postgres_lista_compras
KC_DB_URL_DATABASE: db_lista_compras
KC_DB_USERNAME: adm_lista_compras
KC_DB_PASSWORD: "<minha-senha>"
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- "8080:8080"
depends_on:
- postgres
volumes:
pgdata:
name: postgres_data
Se tudo tiver instalado corretamente bastar usar o comando na pasta onde este arquivo foi colocado para subir as imagens
docker compose up
Depois subir as imagens, você conseguirá acessar o keycloak em qualquer navegador utilizando localhost:8080. Aparecerá uma tela semelhante a tela abaixo, onde você informará o username como admin e o password como admin.
O keycloak separa a camada de login dando mais segurança e dando mais escalabilidade ao projeto. Além disso, se você pretende de trabalhar com aplicações REST isso separará o login e ajudará cada aplicação a trabalhar de forma independente ao mesmo tempo com o login integrado.
O keycloak trabalha com o multitenancy através do realm. Desse modo, o realm é um espaço isolado da aplicação, cada realm possui o seus próprios usuários, clients, grupos, roles, politicas de autenticação e etc.
Depois de criar um realm, precisa criar um client. É aqui no client que apontamos para nossa aplicação. É possivel criar vários serviços distintos, cada um dele com um client. Para isso basta criar o client na tela abaixo do keycloak.
Nessa mesma tela temos abaixo um link para Users, onde você poderá criar e configurar um usuário, dando as permissões criadas nos diferentes clients
com o compose e laravel instalados, criei um projeto laravel.
Configuração do keycloak 26.1.4 no Laravel 12
Além da instalação do php, composer e do laravel instalaremos o pacote robsontenorio/laravel-keycloak-guard
. A escolha do pacote para integrar o Keycloak ao Laravel depende da arquitetura da aplicação e dos requisitos específicos de autenticação, porém alguns pontos me fez escolher o pacote citado:
- Para APIs Laravel: O pacote
robsontenorio/laravel-keycloak-guard
é altamente recomendado devido à sua robustez em termos de segurança e adequação para autenticação via tokens JWT. Saiba mais...
composer require robsontenorio/laravel-keycloak-guard
1 - Configuração do keycloak no Laravel
A primeira coisa a ser feita é abrir o arquivo .env
colocar as chaves abaixo de acordo com o que foi criado no keycloak
KEYCLOAK_CLIENT_ID=laravel-app
KEYCLOAK_CLIENT_SECRET=<my-client-secret>
KEYCLOAK_REALM=lista_compras
KEYCLOAK_SERVER_URL=http://localhost:8080
KEYCLOAK_REDIRECT_URI=http://localhost:8000/auth/callback
KEYCLOAK_REALM_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n<my-public-key>\n-----END PUBLIC KEY-----"
Além disso no arquivo config/keycloak.php
deverão ser configurado os seguintes valores no array.
return [
'client_id' => env('KEYCLOAK_CLIENT_ID', 'laravel-app'),
'client_secret' => env('KEYCLOAK_CLIENT_SECRET', null),
'realm' => env('KEYCLOAK_REALM', 'lista_compras'),
'server_url' => env('KEYCLOAK_SERVER_URL', 'http://localhost:8080'),
'redirect_uri' => env('KEYCLOAK_REDIRECT_URI', 'http://localhost:8000/auth/callback'),
'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null),
];
2 - Configuração do Guard no Laravel
No Laravel, um Guard é responsável pela autenticação dos usuários e é configurado em config/auth.php
.
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'keycloak',
'provider' => 'users',
],
],
O driver keycloak não existe por padrão no Laravel, por esse motivo precisaremos criar o app/Providers/AppServiceProvider.php
3 - Criando um Provedor de Usuários ( User Provider )
O Guard é vinculado a um provider, que define como os usuários são recuperados do banco de dados.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
Importante perceber que o Laravel buca no banco de dados por meio do seu ORM o Eloquente.
4 - Criando um Guard Personalizado
Como o Laravel não tem suporte nativo ao Keycloak, precisamos criar um guard que interprete os tokens JWT e autentique os usuários. Nesse arquivo implementamos funções importantes como a check()
, que verifica se o usuário está autenticado, e a user()
que além de pegar o token JWT do keycloak, extrai dele todas as informações do usuário.
Esse guard é o app/Services/KeycloakGuard.php
<?php
namespace App\Services;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use App\Models\User;
/**
* Esse arquivo contém a lógica para etrari as informações do usuário do token JWT
*/
class KeycloakGuard implements Guard
{
protected $user;
protected $provider;
protected $request;
public function __construct(UserProvider $provider, Request $request)
{
$this->provider = $provider;
$this->request = $request;
}
public function guest()
{
return !$this->check();
}
public function id()
{
return $this->user ? $this->user->getAuthIdentifier() : null;
}
public function hasUser()
{
return !is_null($this->user);
}
public function setUser($user)
{
$this->user = $user;
}
public function check()
{
return !is_null($this->user());
}
public function user()
{
// verifica se o usuário existe
if ($this->user) {
return $this->user;
}
// pega o token e verfica se o mesmo existe
$token = $this->request->bearerToken();
if (!$token) {
return null;
}
// decodifica a chave para pegar os dados do arquivo JWT
try {
$decoded = JWT::decode($token, new Key(config('keycloak.realm_public_key'), 'RS256'));
$this->user = User::firstOrCreate([
'email' => $decoded->email,
], [
'name' => $decoded->name,
'password' => bcrypt(str()->random(16)),
]);
return $this->user;
} catch (\Exception $e) {
return null;
}
}
public function validate(array $credentials = [])
{
return false;
}
}
5 - Registrando o Guard no Laravel
Agora utilizamos o KeycloakGuard app/Providers/AppServiceProvider.php
, e sempre que o Guard for keycloak agora ele utilizará a classe KeycloakGuard
e user provider será users. Desse modo, o driver keycloak configurado em config/auth.php
passa a ser reconhecido
<?php
namespace App\Providers;
use App\Services\KeycloakGuard;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use KeycloakGuard\KeycloakGuard as KeycloakKeycloakGuard;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Auth::extend('keycloak', function ($app, $name, array $config) {
return new KeycloakGuard(Auth::createUserProvider($config['provider']), $app->make(Request::class));
});
}
}
6 - Criando Middleware para protejer as rotas
O usdo do middleware melhora a segurança garantindo que todas as requisições passem por ele restringindo o acesso a usuários válidos. Além disso, permite controlar a validação do JWT e ao separar as classes facilita o teste e o uso, permitindo que a aplicação ganhe em escalabilidade e ainda que no momento não estamos utilizando, facilita o uso das roles e permissões .
segue o arquivo criado app/Http/Middleware/keycloakMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class keycloakMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$authorizationHeader = $request->header('Authorization');
if (!$authorizationHeader || !str_starts_with($authorizationHeader, 'Bearer ')) {
return response()->json(['error' => 'Token JWT ausente'], 401);
}
/**
* a função Auth::guard('keycloak')->check()
* busca verifica se o usuário existena classe app/Services/KeycloakGuard.php que criamos.
* Esse keycloakGuard nós associamos no provider
* */
if (!Auth::guard('keycloak')->check()) {
return response()->json(['error' => 'Usuário não autenticado'], 401);
}
return $next($request);
}
}
7 - Configuração das rotas
Depois da criação desta estrutura basta definir as rotas projegidas com a seguinte nomeclatura
Route::group(['middleware'=>'auth:api'], function () {
Route::apiResource('/private', 'App\Http\Controllers\PrivateController', ['only' => ['index']]);
});
é importante lembrar que para que esse código funcione você precisará criar o controller definido ali App\Http\Controllers\PrivateController
.
O que foi feito
Configuramos o Laravel para usar o Guard Keycloak no
config/auth.php
.Criamos um User Provider (
users
) para armazenar usuários autenticados.Criamos um Guard personalizado (
KeycloakGuard.php
) para validar tokens JWT e autenticar usuários.Registramos o novo Guard no AppServiceProvider.php.
Criamos um Middleware para garantir que apenas usuários autenticados possam acessar rotas privadas.
Configuramos o Keycloak no config/keycloak.php e
.env
.
Isso permite que o Laravel use o Keycloak para autenticação via JWT.
Implementações Futuras
1 - Criação de uma api Restfull
2 - Criação de um front em React para consumir a api
3 - Criação de um App para consumir a api
Subscribe to my newsletter
Read articles from Páblisson Araújo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
