Keycloak SSO — konfiguracja Single Sign-On z OAuth2 i OIDC
Opublikowano: 10 kwietnia 2026 · Kategoria: Bezpieczeństwo / IAM
Keycloak to open source Identity Provider rozwijany przez Red Hat — de facto standard enterprise SSO w środowiskach open source. Zamiast implementować logowanie w każdej aplikacji osobno, rejestrujesz je jako klientów (clients) w Keycloak i delegujesz całą logikę uwierzytelnienia. Użytkownik loguje się raz, a token JWT przenosi jego tożsamość do wszystkich aplikacji. Ten artykuł pokazuje instalację Keycloak przez Docker lub binary, konfigurację realm i clients, zarządzanie użytkownikami i rolami oraz integrację z PHP i Node.js.
Instalacja przez Docker Compose
Najszybszy start to Docker Compose z Keycloak i PostgreSQL jako bazą danych. Keycloak domyślnie używa H2 (embedded), ale PostgreSQL jest wymagany w produkcji.
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak_secret
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
keycloak:
image: quay.io/keycloak/keycloak:25.0
command: start-dev
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak_secret
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_HOSTNAME: localhost
ports:
- "8080:8080"
depends_on:
- postgres
restart: unless-stopped
volumes:
postgres_data: docker compose up -d # Po 30-60 sekundach: http://localhost:8080 # Admin console: http://localhost:8080/admin (admin/admin)
Instalacja binary (bez Docker) z SSL
# Pobierz Keycloak wget https://github.com/keycloak/keycloak/releases/download/25.0.0/keycloak-25.0.0.tar.gz tar -xzf keycloak-25.0.0.tar.gz sudo mv keycloak-25.0.0 /opt/keycloak sudo useradd -r -d /opt/keycloak -s /bin/false keycloak sudo chown -R keycloak: /opt/keycloak # Konfiguracja /opt/keycloak/conf/keycloak.conf # db=postgres # db-url=jdbc:postgresql://localhost:5432/keycloak # db-username=keycloak # db-password=SECRET # hostname=auth.example.com # https-certificate-file=/etc/letsencrypt/live/auth.example.com/fullchain.pem # https-certificate-key-file=/etc/letsencrypt/live/auth.example.com/privkey.pem # Zbuduj konfiguracje (cache JPA) sudo -u keycloak /opt/keycloak/bin/kc.sh build # Uruchom produkcyjnie sudo -u keycloak /opt/keycloak/bin/kc.sh start \ --optimized \ --https-port=8443
# /etc/systemd/system/keycloak.service [Unit] Description=Keycloak Server After=network.target postgresql.service [Service] Type=idle User=keycloak ExecStart=/opt/keycloak/bin/kc.sh start --optimized Restart=always Environment="KEYCLOAK_ADMIN=admin" Environment="KEYCLOAK_ADMIN_PASSWORD=ZMIEN_NA_SILNE_HASLO" Environment="JAVA_OPTS_KC_HEAP=-Xms512m -Xmx1024m" [Install] WantedBy=multi-user.target
Realm, Client i Role — konfiguracja przez Admin UI
Po zalogowaniu do Admin Console stwórz nowy realm (np. myfirma). Realm master
zostaw dla administracji Keycloak. Następnie:
- Utwórz Client — Clients → Create client. Wpisz Client ID (np.
myapp), wybierz OpenID Connect. Ustaw Valid redirect URIs:https://myapp.example.com/auth/callback. Włącz Client authentication (dla server-side apps z secret). - Pobierz Client Secret — Clients → myapp → Credentials → Client secret. Skopiuj do konfiguracji aplikacji.
- Stwórz Role — Realm roles → Create role:
admin,editor,viewer. - Stwórz użytkownika — Users → Create user. Wpisz username, email. W zakładce Credentials ustaw hasło (wyłącz Temporary). W zakładce Role Mapping przypisz role.
Integracja PHP — league/oauth2-client
composer require league/oauth2-client
<?php
// config.php
define('KC_BASE', 'https://auth.example.com');
define('KC_REALM', 'myfirma');
define('KC_CLIENT_ID', 'myapp');
define('KC_CLIENT_SECRET', 'twoj-client-secret');
define('REDIRECT_URI', 'https://myapp.example.com/callback.php');
// login.php — redirect do Keycloak
require_once 'vendor/autoload.php';
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => KC_CLIENT_ID,
'clientSecret' => KC_CLIENT_SECRET,
'redirectUri' => REDIRECT_URI,
'urlAuthorize' => KC_BASE . '/realms/' . KC_REALM . '/protocol/openid-connect/auth',
'urlAccessToken' => KC_BASE . '/realms/' . KC_REALM . '/protocol/openid-connect/token',
'urlResourceOwnerDetails' => KC_BASE . '/realms/' . KC_REALM . '/protocol/openid-connect/userinfo',
]);
// Generuj URL logowania z state (CSRF protection)
$authUrl = $provider->getAuthorizationUrl(['scope' => 'openid profile email']);
$_SESSION['oauth2state'] = $provider->getState();
header('Location: ' . $authUrl);
exit; <?php
// callback.php — obsługa powrotu z Keycloak
session_start();
require_once 'vendor/autoload.php';
// ... (inicjalizacja $provider jak wyzej)
// Weryfikuj state (CSRF)
if (empty($_GET['state']) || $_GET['state'] !== $_SESSION['oauth2state']) {
die('Invalid state - CSRF attack?');
}
// Wymien code na token
$token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
// Pobierz dane uzytkownika
$user = $provider->getResourceOwner($token);
$userData = $user->toArray();
// Dane dostepne:
// $userData['sub'] - unikalny ID uzytkownika w Keycloak
// $userData['email']
// $userData['name']
// $userData['realm_access']['roles'] - lista rol
$_SESSION['user'] = $userData;
$_SESSION['access_token'] = $token->getToken();
header('Location: /dashboard.php');
exit; Integracja Node.js — openid-client
npm install openid-client express express-session
// app.js
const express = require('express');
const session = require('express-session');
const { Issuer, generators } = require('openid-client');
const app = express();
app.use(session({ secret: 'session-secret', resave: false, saveUninitialized: false }));
let client;
async function init() {
const keycloakIssuer = await Issuer.discover(
'https://auth.example.com/realms/myfirma'
);
client = new keycloakIssuer.Client({
client_id: 'myapp',
client_secret: 'twoj-client-secret',
redirect_uris: ['http://localhost:3000/callback'],
response_types: ['code'],
});
}
app.get('/login', (req, res) => {
const nonce = generators.nonce();
const state = generators.state();
req.session.nonce = nonce;
req.session.state = state;
const url = client.authorizationUrl({
scope: 'openid email profile',
state,
nonce,
});
res.redirect(url);
});
app.get('/callback', async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback(
'http://localhost:3000/callback',
params,
{ nonce: req.session.nonce, state: req.session.state }
);
const userinfo = await client.userinfo(tokenSet.access_token);
req.session.user = userinfo;
res.redirect('/dashboard');
});
app.get('/dashboard', (req, res) => {
if (!req.session.user) return res.redirect('/login');
res.json({ user: req.session.user });
});
init().then(() => app.listen(3000)); Kluczowe przepływy OAuth2/OIDC w Keycloak
| Flow | Zastosowanie | Konfiguracja w Keycloak |
|---|---|---|
| Authorization Code | Aplikacje webowe server-side (PHP, Node) | Client authentication ON, redirect URIs |
| Authorization Code + PKCE | SPA, aplikacje mobilne (bez secret) | Public client, PKCE enforced |
| Client Credentials | Komunikacja M2M (mikroserwisy, API) | Service Account Roles enabled |
| Device Authorization | Smart TV, IoT bez przeglądarki | Device Authorization Grant enabled |