Back to Blog
Notes on Authorization — Part 1

Authorization is a System, Not a Boolean

2025-12-26 4 min read
Authorization is a System, Not a Boolean

Authorization tends to look deceptively simple at first. Most systems start with a handful of checks - a role here, a permission there - and everything feels manageable. Over time, though, requirements accumulate: different scopes, exceptions, hierarchies, temporary access, shared resources. What began as a few conditionals quietly turns into one of the most complex parts of the system.

This post is the first in a short series of notes on how authorization actually behaves once systems grow beyond the basics, and why treating it as a first-class design problem matters more than most teams expect.

Most applications start with authorization as a simple check. Someone logs in, you look at who they are, and you decide whether they're allowed to do something. Maybe that's a role comparison, maybe it's a permission string, maybe it's a flag on the user record.

At first, this feels perfectly reasonable.

But over time, as systems grow, this model starts to crack in subtle ways. You add more roles. Then exceptions. Then special cases. Then conditions based on where the action is happening, or what the resource looks like, or who else is involved. Eventually, authorization logic starts leaking into places it was never meant to live.

At that point, authorization stops being a check and quietly becomes a system. The tricky part is that most teams don't realize this transition has happened. They still think in terms of "allow" or "deny," even though the decision now depends on many moving parts: hierarchy, scope, context, inheritance, and sometimes time itself. What looks like a boolean at the call site is usually the result of a fairly complex chain of reasoning.


Where things start to break down

One of the earliest warning signs is role overload. Roles begin life as a convenience - a shorthand for grouping permissions. But as requirements grow, roles start absorbing responsibility they were never meant to carry. You end up with names that encode business rules instead of intent: ManagerWithApproval, RegionalAdminNoBilling, LimitedEditorExceptArchive.

At that point, roles stop being abstractions and start becoming policy engines.

This is usually followed by logic creeping into application code. A condition here, a special case there. Maybe a check against an amount, or a location, or a relationship. These checks feel harmless in isolation, but they accumulate. Over time, authorization becomes scattered across controllers, services, and background jobs, each implementing its own version of "the rules."

Once that happens, changing behavior safely becomes difficult. Understanding why access was granted becomes harder. Auditing becomes guesswork. Testing becomes brittle.

And none of this shows up as a single bad decision - it emerges gradually.


The missing idea: authorization as a process

A more durable way to think about authorization is not as a boolean, but as a process that produces a decision.

That process usually has a few distinct stages:
First, identify who is acting and in what scope they're operating.
Then determine what capabilities apply in that context.
Then evaluate whether a requested action is allowed, given those capabilities and any relevant attributes.

This framing sounds abstract, but it has an important consequence: it separates data, context, and policy.

Once those concerns are separated, a lot of complexity becomes manageable instead of explosive.

Why this framing scales better

When authorization is treated as a system rather than a scattered set of conditionals, a few things change:
You can reason about access without reading application code.
You can explain why a decision was made.
You can cache results safely.
You can evolve rules without rewriting business logic.
You can support more complex scenarios without rewriting everything.

And authorization stops being something you "bolt on" and starts becoming something you design.

A useful mental check

Here's a simple test I've found useful when evaluating an authorization design:

If someone asks, "Why was this action allowed?", can you answer without stepping through code?

If the only way to answer is to grep through conditionals, authorization has already leaked too far.

Wrapping up

Authorization rarely fails loudly. It usually fails quietly - through accumulated complexity, brittle rules, and assumptions that no longer hold.

Treating it as a system rather than a boolean doesn't make it simpler in the short term, but it makes it understandable, evolvable, and defensible over time.

That shift alone changes how everything else fits together.

Dan Guisinger

Dan Guisinger

AWS cloud architect and consultant specializing in system and security architecture. 20 years building enterprise applications in healthcare and finance.

Need Help With Your AWS Architecture?

Get a free 25-minute consultation to discuss your challenges.

Get in Touch