tags : Identity Management

OAuth (Open Authorization)

  • OAuth 2.1 Spec : https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07
  • Framework that provides clients a “secure delegated access” to server resources on behalf of a resource owner.
  • It’s an Authorization only thing. i.e most times assumes parties communicating are trusted already.
  • It does NOT deal with user registration, password recovery, changing a password or email address and so on.
  • OIDC (OpenID Connect)’s parent: OIDC is an authentication layer built on top of OAuth 2.0.

Some usecases

  • Allow third parties access to your data e.g. Facebook data (OAuth2).
  • Allow third parties to authenticate you via your social acc e.g. Facebook account (OpenID Connect).
  • Allow third parties to access your users data safely etc. (OAuth2)

History

  • OAuth 1.0 was published as RFC 5849 in 2010
  • OAuth 2.0 was published as RFC 6749 in 2012
  • OAuth 2.1 Authorization Framework is in draft stage, Consolidates OAuth 2.0 and adds best practices.

Flows

Implicit Flow (deprecated)

  • Issues
    • Access token is sent over in the url
    • No Authorization Code
    • Cannot have refresh token

PKCE

  • Problem: Authorization server needs to know that the correct application is requesting for an access token. Normally, client_secret solves this. BUT! we cannot use client_secret for SPAs and mobile apps. We can use the Implicit flow but PKCE is just better.
  • Solution: PKCE
    • The application generates code challenge and code verifier
    • Uses code challenge to get one time auth code
    • Uses auth code and code verifier to get access token and refresh token

Front channel & Back channel

Front channel

The indirect communication between the client and the authorization endpoint via the user agent and based on HTTP redirects

Back channel

The direct communication between the client and the token endpoint.

Authorization Types (for backend)

by Access Control

FeatureRBAC (Role-Based)ABAC (Attribute-Based)ReBAC (Relationship-Based)
Basis for DecisionUser’s assigned Role(s)Attributes (User, Resource, Action, Env.)Relationships between entities
GranularityLowVery HighMedium-High (limited by relationships)
Best Use CaseSimple, role-defined access needsComplex, dynamic, context-aware rulesHierarchies, groups, ownership structures
StrengthsSimple, Easy Management, PerformanceHighly Granular, Flexible, DynamicHandles Hierarchies well, Reverse Queries
WeaknessesInflexible, Role Explosion riskComplex (Implement/Manage), Resource-IntensiveComplex (Implement/Manage), Query Performance, Audit Difficulty
Hierarchy HandlingPoorCumbersome/VerboseExcellent
Reverse QueriesNoDifficultYes (Naturally supported)
Implementation ComplexityLowMedium-HighHigh

RBAC (Role-Based Access Control)

The simplest model. Access is granted based purely on the role(s) assigned to a user (e.g., Admin, Editor, Viewer). It’s easy to manage for straightforward scenarios but struggles with granularity and can lead to “role explosion” in complex applications.

ABAC (Attribute-Based Access Control)

Offers highly granular control. Access decisions use attributes of the user (e.g., location, department), the resource (e.g., sensitivity, type), the action, and the environment (e.g., time of day). It’s very flexible and dynamic but significantly more complex to implement and manage, and can be resource-intensive.

ReBAC (Relationship-Based Access Control)

Grants access based on the relationship between entities (e.g., user owns document, user is member of team that can access folder). It excels at handling hierarchies and nested structures (like folders and files) and naturally supports reverse queries (who can access X?). It is powerful for these cases but can be complex to implement, potentially resource-intensive for queries, and harder to audit.

ACL-Filtering

Authorization is really about “defense in depth”. In a ZTA model, your access proxy, authentication system, API gateway, application middleware, and data layer all provide additional levels of protection. So essentially you should use both the proper AuthZ system + DB level security such as Row Level Security (RLS).

  • Basic Problem

    • So in this case, we need the Authorization service to be able to modify the database query such that it only fetches the records that they’re allowed to access while maintaining security & performance.
    • WORKS: When you have a authZ service validating our requests, and you have a single resource, and you ask a second API(authZ service) on whether the request is allowed or not.
    • ERR: When you query a database for a list of items, to add access control I need to modify the database query. I can’t just filter after the fact, it’s too easy to cause performance issue(loading too many records only to filter a few!)
  • In other words

    • Gating the operation :(whether it’s retrieving a single resource, or creating / updating / deleting a resource). In this scenario, the application does need to call the authorizer before performing the operation on the resource, but the relationship between the authorizer and the application is at “arms length”.
    • Filtering a list of resources: In this scenario, the authorization system can help you by running what in OPA is known as a “partial evaluation”, which returns an abstract syntax tree. Your code would then walk that tree and insert the proper where clauses (if you’re talking to a SQL back-end). As you mentioned, by necessity, your application needs to work more closely with the authorizer.
  • Solution

    can be done in two ways: “pre-filter” and “post-filter”. Sometimes you might even have to do both.

    • Pre-Filter

      • Convert a policy into a generic AST that you can use in your data filtering logic on your data storage.
      • Eg. This is done by Cerbos, The Query Plan API is used in a relatively advanced use case. The API returns an abstract syntax tree (AST) of the relevant policy. Cerbos partially evaluates the policy because the principal is known, but the resource(s) are not, then returns the AST in the result.
      • It’s straightforward to translate this particular AST to SQL. Unless you’re using an ORM for which we have an adapter, you must write a translation layer yourself. It is common (for Cerbos users) to write a custom adapter for a particular application and database.
      • Filtering data using authorization logic | Cerbos
    • Post-Filter

      • Filtering once you’ve queried all possible results from your database can also be more performant than you’d think, because you can amortize performance by lazy loading and performing permission checks in parallel. We have some pretty large systems that are purely using this strategy.
      • The code for filtering can also be made extremely elegant because it can be hidden behind the iterator interface in whatever programming language you’re using.
    • Not have a secondary API!

      We can go with tools such as casbin which don’t operate as a secondary API for AuthZ but directly embedded in your application. casbin can push to your DB (including multi-tenant-sharing if you scale) and a legit modern policy engine - A(R)BAC, ACL, etc. Definitely warts, but pretty close to what I’d hope architecturally, meaning a clear path to prettier UIs, plugging into automatic SMT solvers/verifiers, etc, and till then, pretty easy from whatever backend lang + SQL DB you use.

    • RLS

      • Long-term, stuff like row-level security in your SQL / metadata store makes a lot of sense (people pointing that out in the thread below), but RLS is still awkward in practice for even basic enterprise RBAC/ACL policies.
      • See Row Level Security (RLS)

by Implementation

Meta Questions(FAQ)

  • When do we need to go beyond simple db check pattern(“just have the role in a db table pattern”)?

    • Growing Roles/Permissions: Managing more than a few basic roles (e.g., beyond just “admin” and “user”) becomes complex. (Eg. “editor,” “viewer,” “manager,” “auditor,” etc)
    • Fine-Grained Control Needed: Access needs to be specific to actions on particular resources (e.g., edit this document, view that folder). This points towards ABAC or ReBAC.
    • Context-Dependent Access: Decisions must factor in attributes like user location, time of day, resource sensitivity, etc. (suited for ABAC).
    • Relationship-Based Access: Permissions depend on how users and resources relate (e.g., owner, manager, group member). ReBAC excels here.
    • Auditability & Management: It becomes hard to track “who can access X?” or “what can user Y do?“. More structured systems (RBAC, ReBAC) offer better visibility.
    • Dynamic/Complex Rules: Authorization logic changes often or involves intricate conditions, benefiting from Policy-as-Code or dedicated engines (ABAC/ReBAC).
    • Code Complexity: Authorization logic starts cluttering the main application code instead of being centralized.
    • Performance Bottlenecks: Evaluating complex permissions via database queries becomes too slow.
    • Scalability Limits: The simple approach struggles to handle a large number of users, resources, and rules efficiently.
  • Difference between Graph vs Policy as Code Authz

    Feature / AspectGraph-Based Authorization (e.g., Zanzibar, SpiceDB)Policy-as-Code Authorization (e.g., OPA, Cedar)
    Primary Access ControlNatural fit for Relationship-Based Access Control (ReBAC).Excels at managing complex policies (e.g., ABAC, RBAC).
    Relationship HandlingExcellent for representing hierarchies and nested relationships.Flexible, can manage complex relationships but not inherently hierarchical.
    Data Volume ManagementManages high volumes of data consistently.Can struggle with large data volumes without proper sharding.
    Reverse IndicesSupports reverse queries (e.g., “who has access to X?”).Typically does not support reverse indices.
    PerformanceGenerally lower performance compared to policy-as-code.Generally higher performance.
    Deployment LocationOften centralized; practically infeasible to run at the edge due to size.Feasible and efficient for local or edge deployment.
    LatencyHigher latency, often due to non-local/centralized nature.Lower latency, especially when deployed locally.
    Policy ComplexityMore limited, primarily focused on ReBAC. ABAC support can be limited (e.g., SpiceDB Caveats offer a restricted form).Highly flexible, adept at handling complex rules and logic.
    Ease of Policy UpdatesCan be less flexible for updates compared to code.Policies are code, allowing flexible and easy updates.
    EcosystemEmerging ecosystem.Mature and robust ecosystem with various plugins and engines.
    Learning CurveModerate complexity, focused on data modeling.Can have a higher learning curve due to specific policy languages (e.g., Rego for OPA).
    ConsistencyStrong consistency guarantees, even distributed (like Zanzibar).Consistency depends on deployment and data sourcing strategy.
    Primary Deciding FactorBetter for simpler policies at very large scale (>1M users).Better for complex policies, often suitable for systems under 1M users unless scaled carefully.

Simple database check

  • We can maintain a table linked to each user, where the access/role of the user is defined
  • That way we can use the session token to do access control as needed

Graph based / Zanzibar based

Policy-as-Code based

3 main players as I see it: cerbos, casbin and OPA

FeatureCerbosOPA (Open Policy Agent)Casbin
Primary FormSpecialized Authorization ServiceGeneral-Purpose Policy EngineFlexible Access Control Library (primarily)
Key AdvantageOptimized DX & Perf for App AuthZ; DecoupledUltimate Flexibility; Unified Policy across stackMulti-language; Embeddable; Model variety
DeploymentStandalone API ServiceService / Sidecar / Go LibraryEmbedded Library / Optional Server
Main FocusSimplified, Decoupled Application AuthorizationGeneral-Purpose Policy (AuthZ, Infra, K8s+)Implementing Various Access Control Models
Policy LanguageYAML/JSON (Human-readable, structured)Rego (Powerful, declarative, steeper curve)PERM Metamodel (Model-driven text config)
Policy ScopeApplication RBAC/ABAC focusVery Broad (Any JSON/YAML data)Std. Models (ACL, RBAC, ABAC, custom)
Engine OriginOwn custom engine (Initially used OPA)Own Rego engineOwn core library engine
State MgmtStateless Engine (Evaluates per API request)Stateless Engine/can be stateful aswellStateful (Policy/roles via Adapters)
Data HandlingNeeds Context/Principal data provided via APINeeds Data via `Input` JSON; Can use BundlesAdapters for Policy/Role Storage & Retrieval
IAM IntegrationConsumes provided attributes (e.g., from JWT)Consumes provided attributes (e.g., from JWT)Role Mgr can optionally fetch/sync from IAM
DecouplingHigh (External Service by design)High (if run as Service/Sidecar)Lower (if Embedded as Library)
PerformanceHighly Optimized specifically for App AuthZGenerally PerformantHigh Performance (esp. as embedded Library)
Dev ExperienceFocus on Simplicity (API, YAML/JSON)Focus on Power/Flexibility (requires learning Rego)Focus on Model Definition & Embedding
Admin UINoNovia Casdoor
Use Case FitMicroservices, Centralized App AuthZ, SimplicityPlatform Policy, K8s, Complex/Diverse RulesMulti-lang Apps, Need Embeddable AuthZ

Resources