How to Install and Configure Keycloak on Fedora Server: Building Single Sign-On (SSO) and OAuth2/OIDC Systems

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

Quick Start (Up and Running in 5 Minutes)

Need to spin up a Keycloak cluster on Fedora Server quickly for app testing or sandbox experiments? You can get it running in about 5 minutes with just a few basic commands.

First, install OpenJDK 21 along with the necessary extraction tools:

sudo dnf install -y java-21-openjdk-headless tar gzip curl

Next, download the Keycloak distribution (Quarkus runtime) and extract it to the /opt directory:

cd /opt
KEYCLOAK_VERSION="24.0.2"
sudo curl -LO https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz
sudo tar -xzf keycloak-${KEYCLOAK_VERSION}.tar.gz
sudo mv keycloak-${KEYCLOAK_VERSION} keycloak
sudo rm -f keycloak-${KEYCLOAK_VERSION}.tar.gz

Set temporary administrator credentials and start Keycloak in development mode:

export KEYCLOAK_ADMIN=admin
export KEYCLOAK_ADMIN_PASSWORD=AdminStrongPassword123!
/opt/keycloak/bin/kc.sh start-dev --http-port=8080

Fedora Server blocks ports other than SSH by default. Open port 8080 using firewalld:

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

Now, open your browser and navigate to http://<Fedora-Server-IP>:8080. Log in with the admin user you just configured to access the Admin Console.

Detailed Explanation: Architecture and How Keycloak Handles SSO

1. Core Concepts in Keycloak

To master Keycloak for SSO and IAM use cases, you only need a solid grasp of these four foundational concepts:

  • Realm: An isolated user management space (multi-tenant). By default, Keycloak provides a master realm reserved for administrative tasks. For production projects, you should always create dedicated realms (e.g., internal-corp or ecommerce-app).
  • Client: Any application requesting identity authentication from Keycloak, ranging from Frontend SPAs (React, Vue) and Mobile Apps (Flutter) to Backend REST APIs (FastAPI, Spring Boot).
  • Roles & Groups: Manage user permissions and authorization. You can assign global permissions (Realm Roles) or fine-grained permissions scoped to specific applications (Client Roles).
  • Identity Providers (IdP): External authentication bridges. They allow users to log in via Google, GitHub, or Microsoft 365, or synchronize identities with existing LDAP / Active Directory servers.

2. Authorization Code Flow with PKCE (OIDC)

This is currently the industry-standard, most secure authentication flow for web and mobile applications. The login process consists of 6 steps:

  1. The user clicks the "Log In" button on the web application (Client).
  2. The browser redirects to Keycloak via the /realms/{realm-name}/protocol/openid-connect/auth endpoint along with the code_challenge (PKCE).
  3. The user enters their credentials and completes 2FA/OTP verification (if enabled).
  4. Keycloak authenticates the user successfully and issues an Authorization Code to the redirect URI.
  5. The Client backend sends the Authorization Code and code_verifier back to Keycloak to exchange them for an ID Token and an Access Token (JWT format).
  6. The Client stores the token and maintains the user session without ever needing to handle or store the user’s raw password.

3. Production Configuration with PostgreSQL and Systemd on Fedora

The default embedded H2 database used in development mode stores data temporarily in RAM or local files. When moving to production, using PostgreSQL is mandatory to guarantee high availability and data integrity.

Install and initialize PostgreSQL on Fedora:

sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

# Create user and database for Keycloak
sudo -u postgres psql -c "CREATE DATABASE keycloak;"
sudo -u postgres psql -c "CREATE USER keycloak WITH ENCRYPTED PASSWORD 'KeycloakDBPass456!';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;"

Next, edit the configuration file at /opt/keycloak/conf/keycloak.conf:

# Database
db=postgres
db-username=keycloak
db-password=KeycloakDBPass456!
db-url=jdbc:postgresql://localhost:5432/keycloak

# HTTP & Proxy
http-enabled=true
http-port=8080
proxy-headers=xforwarded
hostname=sso.yourcompany.com

For security best practices, never run Keycloak under the root user. Create a dedicated system user instead:

sudo useradd -r -d /opt/keycloak -s /sbin/nologin keycloak
sudo chown -R keycloak:keycloak /opt/keycloak

Create a Systemd service unit file at /etc/systemd/system/keycloak.service. Running kc.sh build precompiles static configurations, cutting service startup time down from 15–20s to under 3s:

[Unit]
Description=Keycloak Identity Provider
After=network.target postgresql.service

[Service]
Type=exec
User=keycloak
Group=keycloak
Environment="KEYCLOAK_ADMIN=admin"
Environment="KEYCLOAK_ADMIN_PASSWORD=YourRootPassword"
ExecStartPre=/opt/keycloak/bin/kc.sh build
ExecStart=/opt/keycloak/bin/kc.sh start --optimized
TimeoutStartSec=600
TimeoutStopSec=60
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Reload the systemd daemon and enable the service to start at boot:

sudo systemctl daemon-reload
sudo systemctl enable --now keycloak
sudo systemctl status keycloak

Advanced: Web Application Integration and Nginx Reverse Proxy

1. Setting Up Realm and Client in the Admin Console

  1. Click the dropdown menu in the top-left corner, select Create Realm > Name it Internal-Company.
  2. Go to Clients > Click Create Client:
    • Client type: OpenID Connect
    • Client ID: web-portal
    • Client authentication: Toggle On (for backend APIs or SSR applications) or Off (for SPAs like React/Vue or mobile apps).
    • Valid redirect URIs: https://app.yourcompany.com/callback
    • Web origins: https://app.yourcompany.com

2. Configuring Nginx Reverse Proxy with SSL

Keycloak should sit behind a reverse proxy to handle SSL termination and route HTTPS traffic properly. Create a configuration file at /etc/nginx/conf.d/keycloak.conf:

server {
    listen 80;
    server_name sso.yourcompany.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name sso.yourcompany.com;

    ssl_certificate /etc/letsencrypt/live/sso.yourcompany.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sso.yourcompany.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_buffer_size 128k;
        proxy_buffers 4 256k;
        proxy_busy_buffers_size 256k;
    }
}

3. Sample JWT Token Verification in Backend (Python FastAPI)

Below is a sample middleware demonstrating how a backend verifies digital signatures and decodes the Access Token sent by clients:

import jwt
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import requests

app = FastAPI()
security = HTTPBearer()

KEYCLOAK_URL = "https://sso.yourcompany.com/realms/Internal-Company"
# In a production environment, cache JWKS certs to avoid repeated HTTP calls
jwks_url = f"{KEYCLOAK_URL}/protocol/openid-connect/certs"
jwks = requests.get(jwks_url).json()

def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
    token = credentials.credentials
    try:
        # Retrieve the matching signing key from JWKS
        header = jwt.get_unverified_header(token)
        key = [k for k in jwks['keys'] if k['kid'] == header['kid']][0]
        public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key)
        
        payload = jwt.decode(
            token, 
            public_key, 
            algorithms=["RS256"], 
            audience="account"
        )
        return payload
    except Exception:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, 
            detail="Invalid or expired token"
        )

@app.get("/api/v1/protected-data")
def get_data(user: dict = Depends(verify_token)):
    return {"status": "success", "user": user.get("preferred_username", "Unknown")}

Production Tips and Troubleshooting Common Issues

  • Take Advantage of Fedora’s Fast Release Cadence: Fedora consistently ships with the latest OpenJDK builds and networking utilities. You can inspect LDAP/PostgreSQL connectivity directly using nc or tcpdump without worrying about missing dependencies or adding third-party repos.
  • "Invalid parameter: redirect_uri" Error: This is the most common error encountered during initial integration. It occurs when the URL in the authentication request does not match the Valid redirect URIs configured in Keycloak character-for-character (including trailing slashes / or protocol mismatches like http vs https). Never use wildcard * in production to prevent Open Redirect vulnerabilities.
  • Tuning RAM and JVM Heap: By default, Quarkus consumes around 400MB–600MB of RAM upon boot. For systems serving over 1,000 concurrent users (CCU), allocate a fixed heap size using the environment variable JAVA_OPTS_KC_HEAP="-Xms1024m -Xmx2048m" in your service configuration to prevent OutOfMemory errors.
  • Resolving 502 Bad Gateway Caused by Oversized Headers: HTTP headers containing Keycloak session cookies and Access Tokens can easily reach 8KB–16KB. If Nginx triggers a 502 Bad Gateway, increase proxy_buffer_size 128k; and proxy_buffers 4 256k; as illustrated in the Nginx configuration snippet above.
Share: