🔐 Authentication & Authorization

Interactive guide to OAuth 2.0, OpenID Connect, and Keycloak

Authentication vs Authorization

🔑 Authentication

"Who are you?"

Proves your identity — typically via username/password, MFA, biometrics, or SSO tokens.

  • Login with credentials
  • Multi-factor (TOTP, WebAuthn)
  • Social login (Google, GitHub)

🎫 Authorization

"What can you do?"

Determines which resources you can access — enforced via roles, permissions, or scopes.

  • RBAC (Role-Based Access Control)
  • ABAC (Attribute-Based)
  • OAuth 2.0 scopes

Where Keycloak Fits

🌐 Browser / Mobile App
🛡️ Keycloak
Identity & Access Management
⚙️ Your Backend
Resource Server

Keycloak sits between your users and your backend — it handles all authentication and issues tokens your backend validates.

🏰 Realms

Isolated tenant spaces. Each realm has its own users, clients, roles, and identity providers — completely separate from other realms.

📱 Clients

Applications that use Keycloak for auth. A client can be a SPA, mobile app, backend service, or even another Keycloak instance.

👤 Users & Roles

Users are identities. Roles are collections of permissions. Assign roles to users or groups to control access.

🔗 Identity Providers

Delegate authentication to external providers like Google, GitHub, LDAP, or SAML. Keycloak acts as a broker.

Which OAuth Flow Should I Use?

🤔 Answer these questions:

👆
Answer the questions above to find your flow

📊 Flow Comparison

Flow Use Case Refresh Token? Status
Authorization Code + PKCE SPAs, mobile apps, web apps ✅ Yes ✅ Use This
Client Credentials Machine-to-machine, daemons ⚠️ Optional ✅ Use This
Device Authorization TVs, IoT, input-limited devices ✅ Yes ✅ Use This
Resource Owner Password Legacy / migration only ✅ Yes ❌ Avoid
Implicit None (deprecated in OAuth 2.1) ❌ No ❌ Deprecated

Authorization Code Flow + PKCE

🎯 Why This Flow?

The only recommended flow for SPAs and mobile apps in OAuth 2.1. PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks.

🔐 What PKCE Solves

Without PKCE, a malicious app could intercept the authorization code from the redirect URI. PKCE binds the code exchange to the client that initiated it using a cryptographic challenge.

Ready
🌐
Browser / App
🛡️
Keycloak
⚙️
Backend API
Resource Server
📜 Flow Log

📝 Keycloak Configuration (Authorization Code + PKCE)

Keycloak Client Settings

Client Protocol: openid-connect
Access Type: public
Standard Flow: ✅ Enabled
Implicit Flow: ❌ Disabled
Direct Access: ❌ Disabled

Valid Redirect URIs:
  http://localhost:3000/*
  myapp://callback

Web Origins:
  http://localhost:3000

Frontend (SPA - Authorization URL)

const codeVerifier = generateRandomString(64);
const codeChallenge = sha256(codeVerifier)
  .replace(/\+/g, '-')
  .replace(/\//g, '_');

const params = {
  client_id: 'my-spa',
  redirect_uri: 'http://localhost:3000/callback',
  response_type: 'code',
  code_challenge_method: 'S256',
  code_challenge: codeChallenge,
  scope: 'openid profile email'
};

window.location = authUrl + '?' +
  new URLSearchParams(params);

Backend (Token Exchange)

POST /realms/myrealm/protocol/
     openid-connect/token

Body:
  grant_type=authorization_code
  code=
  redirect_uri=http://localhost:3000/callback
  client_id=my-spa
  code_verifier=

Response: { access_token, refresh_token,
            id_token, expires_in }

Client Credentials Flow

🤖 Machine-to-Machine Only

For backend services talking to each other — no user involved. The client authenticates directly with its ID + secret and receives an access token.

⚠️ Critical Rule

Never use this flow in a browser or mobile app. The client secret would be exposed. Only use from secure backend environments.

🤖
Service A
Your Backend
client_id + secret 🔒 stored in vault
🛡️
Keycloak
Token Endpoint
📡
External API
Resource Server
📜 Flow Log

⚠️ What NOT to Do

❌ Never Use Implicit Flow

Returns tokens directly in the URL fragment — tokens leak into browser history, referrer headers, and analytics scripts.

// ❌ BAD: Implicit flow — token in URL
https://app.com/callback#
  access_token=eyJ...&
  token_type=Bearer&
  expires_in=3600

✅ Do this instead: Authorization Code + PKCE

❌ Never Store Tokens in localStorage

Any JavaScript on the page can read localStorage — including third-party scripts, browser extensions, and XSS payloads.

// ❌ BAD: Token exposed to XSS
localStorage.setItem('token', accessToken);

// ❌ BAD: Also exposed to XSS
sessionStorage.setItem('token', accessToken);

✅ Do this instead: In-memory variable + refresh token in httpOnly secure cookie

❌ Never Use Password Grant

Your app collects the user's password directly — breaks the entire point of OAuth. The user can't revoke access to just your app. Deprecated in OAuth 2.1.

// ❌ BAD: App handles raw password
POST /token
  grant_type=password
  username=alice
  password=hunter2

❌ Never Hardcode Secrets

Client secrets, signing keys, and database passwords should never appear in source code or client-side code. Use environment variables or a secrets manager.

// ❌ BAD: Secret in source
const CLIENT_SECRET = "abc123-secret";

// ✅ GOOD: From env
const CLIENT_SECRET = process.env
  .KEYCLOAK_CLIENT_SECRET;

⚠️ Don't Skip Token Validation

Every backend endpoint must validate: signature (RS256/HS256), expiry (exp), issuer (iss), audience (aud), and not-before (nbf).

// ❌ BAD: No validation
const token = req.headers.authorization;

// ✅ GOOD: Full validation
const decoded = jwt.verify(
  token,
  publicKey,
  { algorithms: ['RS256'],
    issuer: 'https://keycloak/realms/myrealm',
    audience: 'my-backend' }
);

⚠️ Don't Use Long-Lived Access Tokens

Access tokens should live 5–15 minutes. Use refresh token rotation to get new ones — each refresh invalidates the previous refresh token.

// ❌ BAD: 24h access token
accessTokenLifespan: 86400

// ✅ GOOD: 5min access + rotating refresh
accessTokenLifespan: 300
refreshTokenMaxReuse: 0
clientOfflineSessionMaxLifespan: 0

Keycloak Architecture

🏗️ High-Level Architecture

👥 Users & Clients
SPAMobile AppBackend ServiceIoT Device
⬇ HTTPS ⬇
🛡️ Keycloak Server
Authentication Login forms, MFA, WebAuthn, social login
Token Service JWT issuance, refresh, introspection
User Federation LDAP, Kerberos, custom providers
Identity Brokering Google, GitHub, SAML IdPs
⬇ Validated JWT ⬇
⚙️ Your Backend Services
API Gateway validates JWTs Microservice A Microservice B Database

🔑 Token Types in Keycloak

🎫 Access Token (JWT)

Short-lived (5–15 min). Contains claims about the user/client and permissions. Sent to your API as Bearer token.

{
  "iss": "https://keycloak/realms/myrealm",
  "sub": "user-uuid",
  "aud": "my-backend",
  "exp": 1722900000,
  "realm_access": {
    "roles": ["user", "admin"]
  }
}

🪪 ID Token (JWT)

Contains user profile info. For the client app only — never send this to your API. Use the access token instead.

{
  "iss": "https://keycloak/realms/myrealm",
  "sub": "user-uuid",
  "aud": "my-spa",
  "name": "Alice",
  "email": "alice@example.com",
  "email_verified": true
}

🔄 Refresh Token (opaque)

Longer-lived. Used to get new access tokens without re-login. Must use rotation — each refresh invalidates the previous token.

🔓 Offline Token

Special refresh token for offline access (mobile apps, background jobs). Survives user logout and password changes unless explicitly revoked.

✅ Best Practices Checklist

🔐 Token Handling

  • Access token: 5–15 min lifetime
  • Refresh token rotation enabled
  • Validate iss, aud, exp, nbf on every request
  • Never store tokens in localStorage
  • Use httpOnly secure cookies for refresh tokens

🛡️ Security

  • Always use HTTPS in production
  • Use PKCE for all public clients
  • Rotate signing keys regularly
  • Enable brute-force detection in Keycloak
  • Set strict CORS policies

📱 Client Configuration

  • SPAs: public client + PKCE
  • Backend: confidential client + secret in vault
  • Restrict redirect URIs to exact matches
  • Disable unused flows (implicit, direct grant)

🚀 Production

  • Keycloak behind a reverse proxy (TLS termination)
  • Use external database (PostgreSQL)
  • Enable distributed caching (Infinispan)
  • Set up monitoring and alerting
  • Regular backup of realm configs