Skip to content

Instantly share code, notes, and snippets.

@NekoTashi
Created April 28, 2026 14:23
Show Gist options
  • Select an option

  • Save NekoTashi/1b4701869a725d4cec7a582fdd93aea1 to your computer and use it in GitHub Desktop.

Select an option

Save NekoTashi/1b4701869a725d4cec7a582fdd93aea1 to your computer and use it in GitHub Desktop.
Keycloak: Block Access at the Client Level

Block Access at the Client Level

This is a Keycloak-side solution — you configure Keycloak itself to reject the token request for a specific user on a specific client, before your app even sees the token.

The advantage over app-side checks is that the user never gets a token at all.


How it Works

User tries to login
       ↓
Keycloak evaluates Authorization policies
       ↓
      ┌─────────────────┐
      │  Policy: DENY   │ ← user is blocked here
      └─────────────────┘
       ↓
Returns 403 — no token issued
       ↓
Your app never receives anything

Step-by-Step Setup in Keycloak

Step 1 — Enable Authorization on your client

Clients → company-portalAuthorization tab → Enabled: ON (requires client to be confidential or bearer-only)


Step 2 — Create a "Deny" User Policy

Authorization → Policies → Create policy → User

Name:          deny-dave-policy
Description:   Explicitly deny dave from this client
Users:         dave
Logic:         NEGATIVE   ← this is what makes it a DENY

⚠️ Logic: NEGATIVE means — "apply this as a denial for the selected users"


Step 3 — Create a Permission that applies it

Authorization → Permissions → Create permission → Resource-based

Name:          deny-dave-permission
Resources:     (leave blank = applies to all)
Policies:      deny-dave-policy
Decision:      UNANIMOUS

Step 4 — Test it

Authorization → Evaluate tab

Client:   company-portal
User:     dave
→ Result: DENY ✅

User:     carol
→ Result: PERMIT ✅

What Dave Sees

When Dave tries to log in through company-portal:

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

HTTP/1.1 403 Forbidden
{
  "error": "access_denied",
  "error_description": "not_authorized"
}

He gets no token. No access. No way around it from the app side.


Real World Use Case

Scenario Solution
Contractor ended their contract Deny policy by user
Entire department loses access Deny policy by group
Block access outside business hours Deny policy by time
Block a specific IP range Deny policy by JS rule

Summary

App-side check Client-level block
Where enforced Your backend Keycloak
Token issued? ✅ Yes (then rejected) ❌ No
Audit trail In your app logs In Keycloak logs
Scalability Per endpoint One rule covers all
Best for Role-based routing Explicit user/group ban

The client-level block is the most secure option because the token is never issued — there's nothing for the user to intercept or reuse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment