TECHNICAL PRESENTATION

Authorization
Models

RBAC · ABAC · ReBAC · Policy-as-Code
RBAC ABAC ReBAC / Zanzibar OPA / Cedar
subject + action + resource allow / deny

Once you've authenticated the user and got a token — what may they actually do? The decision-making layer the rest of the Identity & Access series feeds into.

Model  ·  Decide  ·  Enforce  ·  Audit
01

Topics

Foundations

  • The PEP / PDP / PAP / PIP vocabulary
  • DAC, MAC, RBAC, ABAC, ReBAC — the historical arc
  • Subject + action + resource + context

The classical models

  • RBAC — roles, role hierarchies, separation of duties
  • ABAC — attributes, predicates, XACML history
  • ReBAC — Google Zanzibar, relationship tuples

Policy-as-Code

  • OPA / Rego — the generalist
  • Cedar — Amazon's purpose-built language
  • OpenFGA / SpiceDB — Zanzibar at scale
  • In-app vs sidecar vs external service

Operational

  • Performance — caching, decision latency
  • Auditing & explainability
  • Choosing a model
  • Migration patterns
02

The PEP / PDP / PAP / PIP Vocabulary

Every authorisation system separates "asking the question" from "answering it" from "where the rules live" from "where the data lives". The four-letter abbreviations come from XACML, but the architecture survives every modern engine.

PEP Policy Enforcement Point app code, gateway, sidecar PDP Policy Decision Point OPA, Cedar, OpenFGA, … PAP Policy Admin Point — git / UI PIP Policy Information Point — DB / IdP "may S do A on R, given context C?" allow / deny + obligations policy bundle pull attribute lookup PEP asks the question. PDP decides. PAP curates the policy. PIP supplies missing facts. The model lets you change the engine, the policy language, or the data source independently.

A request, in protocol-agnostic form

{
  "subject":  { "id":"alice", "role":"editor", "team":"emea" },
  "action":   "document.publish",
  "resource": { "type":"document", "id":"d_42",
                "owner":"alice", "classification":"public" },
  "context":  { "ip":"203.0.113.5", "time":"2026-05-06T09:00Z",
                "auth_strength":"mfa" }
}

A response

{
  "decision": "allow",
  "obligations": [ "log:audit", "redact:ssn" ],
  "explanation": "rule allow-editor-on-own-public-doc"
}

Obligations are side effects the PEP must apply (e.g. mask a field, log to audit). Explainability is what auditors actually want.

03

The Historical Arc — DAC → MAC → RBAC → ABAC → ReBAC

ModelEraThe ideaWhere it survives
DAC Discretionary Access Control 1970s The owner of a resource grants access to whoever they like. Unix file permissions; shared-folder ACLs; Google Drive sharing.
MAC Mandatory Access Control 1970s Central authority assigns labels to subjects and resources; access is determined by lattice rules (Bell-LaPadula). Government & defence systems; SELinux; AppArmor.
RBAC Role-Based Access Control 1992 (Ferraiolo & Kuhn) → ANSI standard 2004 Subjects are assigned roles; permissions are attached to roles. Almost every enterprise app from 1995–2015.
ABAC Attribute-Based Access Control 2000s — XACML 1.0 in 2003 Decisions evaluated as predicates over attributes of subject, resource, action, environment. Government, finance, healthcare; modern Policy-as-Code (OPA, Cedar) is ABAC at heart.
ReBAC Relationship-Based Access Control 2019 (Google Zanzibar paper) Authorisation derived from a graph of relationships between subjects and resources. Modern collaboration apps (Google Drive, GitHub, Notion); OpenFGA, SpiceDB, Warrant.

A useful framing

Each model is a different way to compress the boolean function "may subject S do action A on resource R?". RBAC compresses by clustering subjects (roles); ABAC by parameterising the condition (predicates); ReBAC by exploiting the structure of the resource graph (relationships).

In practice — a hybrid

Real systems mix models. Google Drive: ReBAC on documents, RBAC on workspace administration, ABAC for "external sharing only allowed for users with the EXTERNAL_OK attribute". The model is a vocabulary; production policy is the union.

04

RBAC — Roles, Hierarchies, Separation of Duties

The four NIST RBAC levels (ANSI INCITS 359-2004)

  1. Flat RBAC — users → roles → permissions.
  2. Hierarchical — roles inherit from each other (admineditorviewer).
  3. Constrained — adds separation of duties: a user can hold either approver or requester, never both.
  4. Symmetric — adds permission review (queries like "who can do X?").

Where RBAC shines

  • Org charts that map naturally to roles ("all customer-success reps see the customer-care console").
  • Compliance regimes that audit who has which role.
  • Coarse-grained tooling — Linux sudoers, Kubernetes Role/ClusterRole, AWS IAM groups.

Where RBAC breaks down — "role explosion"

  • You start with admin, editor, viewer. A customer asks "viewer that can also export". You add viewer-with-export.
  • Another asks "admin without billing access". admin-no-billing.
  • Six months later you have 200 roles and 14 are misnamed near-duplicates.
  • The cure: parameterise. That's ABAC.

RBAC in code (Kubernetes)

kind: Role
metadata: { name: pod-reader, namespace: dev }
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","list","watch"]

---
kind: RoleBinding
metadata: { name: alice-pod-reader, namespace: dev }
subjects: [{ kind: User, name: alice }]
roleRef: { kind: Role, name: pod-reader }
05

ABAC — Attributes & Predicates

Instead of pre-clustering users into roles, ABAC writes a predicate over the request and the world. A role becomes a special case ("subject.role == admin"), but you can also express things RBAC can't: time-of-day, IP range, classification level, ownership.

XACML — the granddaddy

  • OASIS standard, 2003 (XACML 1.0) → 3.0 (2013).
  • XML policy language with PEP/PDP/PIP/PAP architecture.
  • Policy combinators (deny-overrides, permit-overrides, first-applicable).
  • Verbose, hard to read; widely-deployed in government and finance through the 2000s.
  • Largely superseded in greenfield by OPA/Rego and Cedar — but the concepts survived.

A modern ABAC policy (Cedar)

permit (
  principal,
  action == Action::"document.publish",
  resource is Document
) when {
  resource.owner == principal
  && resource.classification == "public"
  && context.auth_strength == "mfa"
};

No roles in sight; the rule reads like the natural-language policy.

When ABAC wins

  • Decisions depend on resource attributes — owner, sensitivity, project, customer.
  • Decisions depend on context — time, location, auth strength, device posture.
  • Multi-tenant SaaS where each customer has different rules but the same engine.
  • Compliance contexts (HIPAA "minimum necessary", FedRAMP) that require explicit data-classification logic.

Where it bites

  • "Who can publish this document?" — ABAC requires evaluating the rule against every (subject, resource) pair, which doesn't scale to a UI listing.
  • Solution: ABAC for enforcement + a separate index for list / search.
06

ReBAC & Google Zanzibar

Google's 2019 paper "Zanzibar: Google's Consistent, Global Authorization System" described the system that powers Drive, Calendar, YouTube, Cloud, and most of Google's product surface. It's a relationship-based model implemented as a globally-consistent tuple store.

The core data structure — relation tuples

# object # relation @ user
doc:report1#owner@user:alice
doc:report1#viewer@user:bob
doc:report1#viewer@group:engineering#member
group:engineering#member@user:carol
folder:q3#parent@doc:report1
folder:q3#viewer@user:dan

Every authorisation fact is a small tuple. The graph is the policy.

A namespace schema

type doc {
  relation owner:   user
  relation viewer:  user | group#member | doc#owner
  permission view = viewer + owner
                  + parent.viewer    // inherit from folder
}
type folder {
  relation viewer:  user | group#member
}
type group {
  relation member:  user
}

Why ReBAC fits modern apps

  • Every collaboration product is fundamentally a graph (folders contain documents; teams contain people; orgs contain teams).
  • Sharing semantics are natural: "viewer of folder X = viewer of every document in X" falls out of the schema.
  • ListUserPermissions ("what can Alice see?") and ListResourceAccessors ("who can see this?") are first-class queries.
  • External Zanzibar-clones — OpenFGA (Auth0), SpiceDB (AuthZed), Warrant, Permify, Topaz — are open-source/SaaS.

The hard parts (Zanzibar's contributions)

  • Consistency — Zanzibar uses zookies (causally-consistent tokens) so a write reflects in subsequent reads.
  • Global scale — billions of writes/sec across data centres.
  • Cache coherence across the graph traversal.
07

Policy-as-Code — The Class of Tools

Policy-as-Code (PaC) means: the rules live in version-controlled files, are reviewed in PRs, are tested with unit tests, and are deployed by a pipeline. The actual policy language varies — Rego, Cedar, internal DSLs — but the engineering practice is the same.

ToolLanguageSweet spotHosted by
OPA (Open Policy Agent)Rego (datalog-ish)General-purpose; outside-of-app PDP for k8s, Terraform, microservicesCNCF (graduated); Styra
CedarCedar (purpose-built)App-level RBAC + ABAC; safer / more analysable than RegoAWS (open source); AVP service
OpenFGAFGA-DSL + tuples (Zanzibar-like)ReBAC at app scale; collaboration appsAuth0/Okta, CNCF sandbox
SpiceDBSchema + relationships (Zanzibar-like)Same niche as OpenFGA; commercial AuthZed managed cloudAuthZed
CasbinMulti-model (RBAC, ABAC, ReBAC)Embedded in many languages; small apps that prefer a libraryopen source
Permify / Topaz / WarrantVariants on the aboveEach scratches a slightly different operational itchopen / commercial
XACML / WSO2 ISXACML 3.0Legacy enterprise; government / regulated environmentsOASIS standard, WSO2

Picking on engine vs language

Two questions to ask. (1) What's your dominant model — RBAC, ABAC, ReBAC? (2) Where will the engine run — embedded, sidecar, central service? Most rejection of "the wrong tool" comes from forcing a Zanzibar-style graph onto a Rego rule, or vice versa.

08

OPA & Rego — The General-Purpose Engine

A Rego policy

package authz

default allow = false

allow if {
  input.action == "document.publish"
  input.subject.role == "editor"
  input.resource.owner == input.subject.id
  input.resource.classification == "public"
  input.context.auth_strength == "mfa"
}

allow if {
  input.subject.role == "admin"
  not deny
}

deny if {
  input.context.ip in data.blocked_cidrs
}

How OPA is deployed

  • Sidecar next to the app — Unix socket / localhost HTTP. The most common pattern.
  • In-process via the OPA WebAssembly bundle. Lower latency; harder to update.
  • Centralised service — easy to manage, single point of failure.
  • Policies pulled from a bundle server (S3 / GitHub release / Styra DAS).

Where OPA shines

  • Kubernetes admission control (Gatekeeper).
  • Terraform / IaC validation (conftest).
  • Service-mesh authz (Envoy ext_authz).
  • CI/CD policy ("no PR can merge with a rule violation").
  • Cross-language app authz when you have polyglot services.

Common pain

  • Rego's evaluation rules surprise people — "default" + "rule with the same name" can both fire and produce confusing results.
  • Performance degrades with poorly-written rules (cartesian iteration over data sets).
  • No native graph traversal → ReBAC in OPA gets ugly.
  • Test coverage requires explicit fixtures; many shops don't.
09

Cedar — The App-Level Specialist

AWS published Cedar in 2023 (Apache 2.0 + the AWS-hosted Verified Permissions service). Designed from a formal-methods background: deliberately small, deliberately analysable, deliberately app-level.

A Cedar policy

permit (
  principal in Group::"engineering",
  action in [Action::"doc.read", Action::"doc.comment"],
  resource is Document
) when {
  resource.classification != "secret"
  || principal has clearance && principal.clearance >= 3
};

forbid (
  principal,
  action == Action::"doc.delete",
  resource is Document
) unless {
  principal == resource.owner
};

RBAC (principal in Group::"engineering") and ABAC (resource.classification) and ReBAC-lite (principal == resource.owner) all live together.

What Cedar does that Rego can't

  • Schema-validated — every policy is checked against an entity schema; misspelled attributes fail at parse time.
  • Analysable — formal verification tools answer questions like "does this policy ever permit X?". Same engine that powers AWS IAM Access Analyzer.
  • No undefined behaviour — explicit semantics for every operation.
  • Permit / forbid combinators with explicit precedence (forbid wins).

Where Cedar fits

  • App authorisation — "may this user perform this action on this object?".
  • Multi-tenant SaaS with similar shape across tenants.
  • Anywhere you'd write XACML in 2010 but want a modern syntax in 2026.

Where it doesn't

Infrastructure / k8s / IaC validation. That's still Rego/OPA territory.

10

OpenFGA & SpiceDB — Zanzibar-Class Engines

OpenFGA — schema + tuples

// schema
model
  schema 1.1
type user
type group
  relations
    define member: [user]
type document
  relations
    define owner:  [user]
    define editor: [user, group#member]
    define viewer: [user, group#member]
    define can_edit:  editor or owner
    define can_view:  can_edit or viewer

// runtime tuples
write document:report1 owner    user:alice
write document:report1 viewer   group:engineering#member
write group:engineering member  user:bob

// query
check document:report1 can_view user:bob   → allow

Capabilities both share

  • Sub-millisecond check at the 99th percentile (with caches).
  • list_objects — all docs Alice can view (for UI listings).
  • list_users — everyone who can view this doc.
  • Conditional relationships — "viewer if today.weekday() < 6".

SpiceDB — opinions

  • Same Zanzibar lineage; commercial backing from AuthZed.
  • Stronger story on consistency (zookies / "fully consistent" reads).
  • Schema language is more expressive (caveats, computed sets).
  • Both gRPC + HTTP APIs.

When this is the right tool

  • You're building a collaboration app — docs, folders, comments, mentions, notifications.
  • You have hierarchical resources where sharing inherits down (or up).
  • Your UI needs "things shared with me" queries; computing this from RBAC/ABAC is painful.
  • You expect > 10⁶ relationship tuples and want sub-millisecond evaluation.

Operational note

Both are real databases. Treat them like one — backups, monitoring, schema migrations, capacity planning.

11

Deployment Topologies — In-App, Sidecar, External

In-app library

"Just import the SDK and call can()."

  • OPA WASM bundle, Casbin, embedded Cedar.
  • Lowest latency — function call.
  • Policy updates require redeploy (or a runtime bundle reload).
  • Drift between services if each updates at its own pace.

Best for: monolithic apps, latency-sensitive paths.

Sidecar (PEP next to app, PDP next to PEP)

"localhost:8181 says yes."

  • OPA sidecar; SpiceDB / OpenFGA local proxy.
  • Policy bundles pulled centrally; loaded by every sidecar.
  • Sub-ms latency over loopback.
  • Standard pattern in service-mesh architectures.

Best for: microservices, k8s, polyglot stacks.

External service (PDP as its own SaaS)

"call the AuthZ team's API."

  • AWS Verified Permissions; Auth0 FGA Cloud; AuthZed Cloud; Styra DAS.
  • Centralised audit, single source of truth for policy.
  • Higher latency — 5–30 ms per check.
  • Network failure mode = your app fails closed (or opens — be deliberate).

Best for: organisations with strong central security teams; small apps that want SaaS authZ.

A common production pattern

RBAC checks (course-grained) inside the app for low-latency hot path. ReBAC checks against a sidecar/external PDP for object-level decisions. ABAC overlay for "additional constraints" (geo, time, classification). The decisions agree because the schemas are unified, but each runs at the right cost.

12

Performance — Decisions Per Second, Caching, Indexes

Where the time goes

  • Network round-trip (PEP → PDP)
  • Policy evaluation
  • Attribute fetch (PIP → DB / IdP / UserInfo)
  • Graph traversal (ReBAC)

Most production AuthZ latency is not the policy engine — it's the data lookups.

Caching, sensibly

  • Cache the request inputs not the decision when policy is changing fast.
  • Cache the decision when the inputs are stable for the request lifetime.
  • Use short TTLs (≤ 60 s) with explicit invalidation events when group memberships change.
  • Negative caching matters — denials are repeated.

Hot indexes for ReBAC

  • Zanzibar maintains materialised views ("all users with view on this object") computed asynchronously.
  • This makes list_users & list_objects sub-ms.
  • Trade-off: writes are slower (graph re-computation); reads are O(log n).

Bulk authorisation

If your UI shows 50 documents and asks "which can I edit?", don't make 50 round-trips. Every modern engine has a batchCheck / checkBulk primitive — use it.

Anti-pattern

Calling the PDP from inside a database query / N+1. Authz must run before / after the query, not per row.

13

Audit & Explainability — What Auditors Actually Want

An auditable decision log

Every auth decision should be loggable as a structured record. Sample fields:

{ "ts": "2026-05-06T09:00:01.234Z",
  "trace_id": "abc…",
  "subject": { "sub":"alice", "iss":"https://idp/" },
  "action": "document.publish",
  "resource": { "type":"document", "id":"d_42" },
  "decision": "allow",
  "matched_rule": "allow-editor-on-own-public-doc",
  "policy_version": "git:5f3a91c",
  "engine": "cedar-3.2.1",
  "latency_ms": 1.4 }

Three questions auditors ask

  1. Show me every action user X performed in Q3.
  2. Show me everyone who could have viewed customer Y's data on date Z.
  3. Show me what policy was in effect for every decision.

If your decision log + policy versioning can answer these, you're SOC 2 / HIPAA / FedRAMP-credible.

Explainability inside the engine

  • OPA: opa eval with --explain=full dumps the trace; usable in tests.
  • Cedar: response includes "permit from policy policyId" identifiers.
  • OpenFGA: check can return the path through the relationship graph.
  • Translate technical traces into human-readable strings for support / customer admins.

Test policies like code

  • Every PR to a policy file requires unit tests.
  • Tests cover both allow and deny paths.
  • Tests assert on the matched rule — not just the boolean — so a refactor that "happens to allow" still flags.
  • OPA: opa test. Cedar: built-in test harness. OpenFGA: store-test.
14

Tying AuthZ to AuthN & Identity

An authorisation engine is only as good as the facts it gets about the subject. Those facts come from the rest of the Identity & Access stack.

From the IdP / tokenBecomes a PDP input
OIDC subsubject.id
OIDC audresource scope check (ensure this token is for me)
OIDC scopeaction allow-list pre-filter
OIDC acr / SAML AuthnContextClassRefcontext.auth_strength ("mfa" / "password")
OIDC amrcontext.auth_methods (["pwd","webauthn"])
OIDC auth_timecontext.auth_age_seconds
SAML AttributeStatement.groups / SCIM-synced groupssubject.roles / membership tuples
Custom claims (department, region, clearance)subject.<attribute>

A common mistake

Re-fetching the user's roles from the database on every PDP call when they're already on the access token. Trust the IdP that issued the token; if you don't, that is your problem to fix.

When to step up

If a policy demands context.auth_strength == "mfa" and the token only attests pwd, the PEP should issue a step-up — not flatly deny. With OIDC: redirect to /authorize?prompt=login&acr_values=mfa and try again.

15

Choosing a Model — Decision Matrix

If your authorisation question is...Best-fit modelLikely engine
"Does this user have a role with this permission?"RBACIn-app, or any engine in RBAC mode (Cedar, OPA)
"Does this user have this attribute combo at this time?"ABACCedar (analysable) or OPA/Rego (general)
"Has someone shared this object with this user, possibly transitively?"ReBACOpenFGA / SpiceDB / Permify / AuthZed
"All of the above"HybridEngine that supports both — Cedar + Zanzibar-class side car, or AWS Verified Permissions + Cognito
"Is this Kubernetes manifest allowed?"Policy-as-Code on infraOPA / Gatekeeper
"Is this Terraform plan allowed?"Policy-as-Code on IaCOPA / conftest, HashiCorp Sentinel
"Can this microservice talk to that one?"Network + workload identity (mTLS / SPIFFE)Service mesh (Istio, Linkerd, Cilium)
"Can this database column be read by this user?"Row/column-level security in DB + central PDPPostgres RLS, Snowflake row access policies, plus PDP for the rule body

Don't pick the model first

Pick the questions first. Write down the ten authorisation questions the product needs to answer. The model that makes those questions short and natural is the right one. The model that requires more JOINs / more roles / more rules than questions is wrong.

16

Migration Patterns

Hand-rolled if/else → Policy engine

  1. Inventory every if (user.role == ...) in the codebase. Tag each with the question it answers.
  2. Pick an engine based on the question shape (slide 16).
  3. Implement the new policy in shadow mode — every check runs both old and new code paths; log the diff. Don't change behaviour.
  4. Burn down the diff. When zero for ≥ 1 week, flip the engine to authoritative.
  5. Delete the if/else.

RBAC → ReBAC

  • Map roles to relationships: user X has admin role on workspace W = workspace:W#admin@user:X.
  • Backfill existing role assignments as relationship tuples.
  • Add object-level relationships gradually (folders, documents) as features want them.
  • Keep the role tuple too; it lets you keep RBAC checks for hot paths.

XACML → Cedar / OPA

  • XACML's policy combinators map cleanly onto Cedar's permit/forbid semantics.
  • Most XACML rules are ABAC; keep the structure, port the syntax.
  • Test in shadow against the old XACML PDP before cutover.

Lessons from real migrations

  • Plan for twice as many edge cases as the old code admits to. Hand-rolled authz accretes them.
  • Don't re-design the model and the engine at once. Lift-and-shift first; redesign once stable.
  • Audit log diff is your safety net — don't skip the shadow phase.
17

Reference Architectures by Domain

DomainStack
Collaboration SaaS (docs, kanban, chat) OIDC at the edge · OpenFGA / SpiceDB for object-level ReBAC · embedded RBAC for admin actions · ABAC overlay for data-residency rules.
B2B SaaS, multi-tenant OIDC + SCIM-synced groups · Cedar or OPA per request · per-tenant policy bundles via Styra/AVP · row-level security in DB for defence-in-depth.
Kubernetes platform Workload OIDC for service identity · OPA/Gatekeeper for admission · Kubernetes RBAC for role-binding · SPIFFE/SPIRE for service-to-service mTLS.
FinTech / Open Banking FAPI 2.0 OIDC at the edge · Cedar-style ABAC for transaction policies · separate engine for fraud rules · audit log streamed to regulator.
Healthcare / FHIR SMART-on-FHIR scopes (OAuth) · ABAC on patient-clinician relationships · ReBAC for care-team graphs · break-glass emergency access pattern.
Internal corporate IT Workforce IdP (Entra/Okta) issues OIDC + SAML · SCIM provisions to apps · per-app RBAC · CAEP for session revocation.
Public cloud account AWS IAM (resource-scoped) · AWS Verified Permissions / Cedar for app-level · OPA/Conftest for IaC.
Government / regulated SAML / OIDC SSO · ABAC with classification labels · audit + dual control / SoD enforced at PDP · MAC-style label propagation in DB.
18

Production Gotchas

"Default allow"

Easy to ship, expensive to fix. Always default-deny; require an explicit permit.

Stale group sync

SCIM provisioned the user out at 17:00. PDP cached "alice ∈ admins" for 60 min. Use change-events to invalidate, not just TTL.

Authorising on the access token's scope alone

Scopes are coarse — "can call this API", not "can call this method on this object". The PDP must check the resource too.

Mixing AuthN and AuthZ in the same code path

If you look at a JWT and decide both "is the token valid?" and "is this user allowed to call this method?" in one function, you've coupled two concerns that change at very different rates.

"Admin" as a magic role

An admin role that bypasses every rule is the most common reason customers find their data was accessed by a support engineer they didn't expect. Subject every privileged action to the same logging + step-up MFA as regular users.

Changing the model "just for one feature"

That feature now bypasses every authz check. Add the new feature to the existing engine — even if it's awkward — until the engine genuinely doesn't fit and you re-architect.

Trusting the front-end

UI hides the "Delete" button if the user can't delete. Backend doesn't check. User opens devtools and POSTs DELETE /api/x. Authorise at the API, not just in the UI.

Logging what was requested, not what was decided

"Alice tried to view doc X" is not enough. Log the policy version, matched rule, decision and obligations. Half of post-incident analysis is this log.

19

Summary & References

What we covered

  • PEP / PDP / PAP / PIP architecture
  • The DAC → MAC → RBAC → ABAC → ReBAC arc
  • RBAC in detail and "role explosion" as the symptom that pushes you onward
  • ABAC — XACML lineage, Cedar in 2026
  • ReBAC — Google Zanzibar, OpenFGA, SpiceDB
  • Policy-as-Code engines compared
  • OPA/Rego, Cedar, OpenFGA/SpiceDB in concrete code
  • Deployment topologies — in-app, sidecar, external
  • Performance, caching, bulk authorisation
  • Audit, explainability, testing policies like code
  • Tying AuthZ to AuthN/identity (the OIDC/SAML connection)
  • Choosing a model; migration patterns; reference architectures
  • Production gotchas

Three take-aways

  1. Pick the questions, then the model. If your authz questions are about relationships, force-fitting them into roles is how you get role-explosion.
  2. Authorisation is data + code. The data (groups, ownership, relationships) lives in the IdP / app / Zanzibar store; the code (policy) lives in git. Separate them.
  3. Default deny, log everything, version policy. Without these three, no engine on earth saves you.

Companion decks

OAuth Primer / Part 1 / Part 2 · Introduction / Advanced OpenID Connect · SAML 2.0 & SCIM · Authentication Methods.

References

Ferraiolo & Kuhn, Role-Based Access Control (1992) · ANSI INCITS 359-2004 RBAC · OASIS XACML 3.0 · Pang et al., Zanzibar: Google's Consistent, Global Authorization System (USENIX 2019) · OPA — openpolicyagent.org · Cedar — cedarpolicy.com · OpenFGA — openfga.dev · SpiceDB / AuthZed — authzed.com · AWS Verified Permissions · NIST SP 800-162 (ABAC) · SP 800-205 (ABAC for Cloud)

One-line takeaway

Authentication tells you who. Tokens tell apps that. Authorisation is the actual rules — and rules belong in version-controlled code, not scattered through the application.