Keycloak was just the beginning. Meet Keymate.
Estimated read: 7–9 minutes
RBAC is necessary but not sufficient at modern scale. Roles multiply, exceptions become the work, and cross-tenant realities strain simple role checks. The path forward is to keep identity and layer fine-grained, contextual, explainable authorization—ABAC/ReBAC/RADAC—without rip-and-replace. Run in parallel, observe decisions, and cut over with confidence.
So what: At scale, roles become the work; RBAC alone can’t encode context, relationships, risk, or time.
We’ve all seen it: the system grows; so do roles. In one environment—~650 roles across 9 apps and **120 tenants **—role changes grew 35% YoY, while audits slowed to a crawl. RBAC still helped, but the intent behind permissions got blurry.
RBAC isn’t wrong. It’s foundational. But alone, it can’t express who should access what, in which relationship, under which risk, and for how long—without exploding into ad-hoc roles and exceptions.
So what: RBAC answers who—not what/which/when/under which risk/for how long.

So what: Role explosion is exception debt; context collapses exceptions into rules.
New use-case? Add a role. Special case? Add another. Before long, “role hygiene” becomes a full-time job. Exceptions accumulate. Audits hurt.
So what: Without attributes, relationships, and risk, roles over-grant or stall behind tickets.
Roles are coarse. They rarely encode attributes (department, region, data class, time), relationships (owner, manager, collaborator), or risk (device posture, anomalous geo, after-hours). Without context, you over-grant or drown in tickets.
So what: Tenants and org trees must be first-class; scoped admin can’t be faked with roles alone.
B2B/B2B2C/G2C topologies need strict isolation and scoped administration. If your access model doesn’t “know” tenants, org trees, and delegation, you end up with fragile processes. (We’ll dig into this in Part 3.)
So what: Policy-as-code + explainability turn audits from fire-fights into routine checks.
Policies are often configs, not software artifacts. No versions, diffs, reviews, approvals, or rollbacks. When access is denied, there’s no evidence for “why denied?”—just folklore.
Policy-as-code
Policies should be treated like code—think “GitHub for authorization”: diffs, pull requests, reviews, and* > *rollbacks**.

So what: Role grants must be gated by conditions—after-hours, untrusted devices, freezes, on-leave states, and scope.
These are things a user’s role may authorize normally, but shouldn’t during higher-risk conditions:
Roles don’t carry these nuances. Context and risk do.
So what: Make intent executable and observable—small policies plus decision traces beat sprawling role sets.

Naïve check
if user.hasRole("FINANCE_ANALYST"):
allow("view_report")
Add attribute constraints
allow("view_report") if
user.role == "FINANCE_ANALYST" &&
user.region == resource.region &&
resource.classification in ["internal","confidential"]
allow("doc:read") if
relation(user, resource, "owner") ||
relation(user, resource, "collaborator")
allow("approve_payment") if
user.role == "FINANCE_MANAGER" &&
risk(user, device, geo, time) <= "medium" &&
elevation.active == true &&
now() < elevation.expires_at
Decision trace (example JSON)
{
"request_id": "ae8f...c2",
"subject": "user:1234",
"action": "approve_payment",
"resource": "invoice:987",
"inputs": {
"role": "FINANCE_MANAGER",
"region": "EU",
"device_trust": "low",
"time": "23:18Z",
"vpn": true,
"risk_score": 7
},
"evaluation": {
"strategy": "unanimous",
"policies": [
{
"name": "RBAC:Manager",
"result": "allow"
},
{
"name": "ABAC:EU-Only",
"result": "allow"
},
{
"name": "RADAC:LowRisk",
"result": "deny",
"reason": "risk_score>=6 || vpn==true"
}
]
},
"final": {
"decision": "deny",
"why": "RADAC:LowRisk"
}
}
Decision trace: RADAC failed (risk.score≥6), final=deny. The log shows exactly which rule fired and why.
So what: Don’t replace IAM—layer ABAC/ReBAC/RADAC on RBAC and compose them with PBAC strategies.
The fix isn’t to throw RBAC away. It’s to layer context and governance on top:
| Model | What it adds | Typical use | Limits |
|---|---|---|---|
| RBAC | Role grants | Stable job functions | Coarse; role sprawl |
| ABAC | Attributes (user/resource/context) | Region, data class, time | Attribute hygiene |
| ReBAC | Relationships/graph | Ownership, team, share | Graph modeling |
| RADAC | Risk signals | Device, geo, anomaly, hour | Signal quality; explainability |
| PBAC | Decision strategies (unanimous/affirmative/consensus) | Compose sub-policies | Choose strategy wisely |
Key idea: RBAC handles who someone is in the org; ABAC/ReBAC/RADAC encode what they’re doing, to which resource, in which relationship, under which risk—and for how long.
So what: **Ship confidence, not bravado—policy-as-code, explainability, and a parallel run let you cut over safely. **
Model the context you really use
Start with two attributes (e.g., region, data_class) and one relationship (owner). Keep it practical.
Policy as code
Put policy in version control. Treat changes like code: diff → PR → review → approve → deploy → rollback.
Explainability & logs
Emit decision traces: inputs, rule matched, outcome, “why denied/allowed.”
Parallel run
Run next to your legacy checks. Compare decisions. Fix mismatches. Cut over gradually. Confidence beats bravado.

In Part 3, we’ll address the elephant we touched on here: Multi-Tenancy & Delegation—how to model tenants, org trees, and scoped admin without spreadsheets. We’ll show concrete patterns and a few anti-patterns to avoid.
Series nav
← Part 1: Why Keymate? How We Got Here
→ Part 3: Multi-Tenancy & Delegation (coming next)
Get the next chapter when it drops, plus insights on modern IAM and authorization patterns.
Stay updated with our latest insights and product updates