---
title: "Fine-Grained API Authorization: From RBAC to AuthZEN at the Gateway"
description: "Learn how fine-grained authorization models like ABAC, ReBAC, and the OpenID AuthZEN standard let API gateways enforce resource-level access control beyond basic RBAC."
canonicalUrl: "https://zuplo.com/learning-center/fine-grained-api-authorization-rbac-authzen-gateway"
pageType: "learning-center"
authors: "nate"
tags: "API Security"
image: "https://zuplo.com/og?text=Fine-Grained%20API%20Authorization%3A%20From%20RBAC%20to%20AuthZEN%20at%20the%20Gateway"
---
Broken access control has held the #1 spot on the
[OWASP Top 10](https://owasp.org/Top10/) since 2021, and for good reason —
studies show that the vast majority of tested applications contain some form of
access control vulnerability. The typical cause isn't a lack of authentication.
It's that authorization is too coarse. A user might have a valid JWT and the
right role, yet the API still fails to check whether _that specific user_ should
access _that specific resource_.

Role-Based Access Control (RBAC) is a solid starting point, but modern APIs need
more. When your system needs to answer questions like "Can this user edit this
particular document?" or "Can this service read records belonging to this
tenant?", you need fine-grained authorization. And increasingly, teams are
enforcing those decisions at the API gateway rather than scattering
authorization logic across every backend service.

This article covers the evolution from coarse-grained RBAC to fine-grained
authorization, introduces the OpenID AuthZEN standard that's unifying how
authorization decisions are made, and shows how you can implement these patterns
at the gateway layer with Zuplo.

**In this article:**

- [Why RBAC Isn't Enough](#why-rbac-isnt-enough)
- [Understanding Fine-Grained Authorization](#understanding-fine-grained-authorization)
- [What Is OpenID AuthZEN?](#what-is-openid-authzen)
- [Gateway-Enforced vs. Application-Level Authorization](#gateway-enforced-vs-application-level-authorization)
- [Implementing Fine-Grained Authorization with Zuplo](#implementing-fine-grained-authorization-with-zuplo)
- [The Future of API Authorization](#the-future-of-api-authorization)
- [Getting Started](#getting-started)

## Why RBAC Isn't Enough

[RBAC](/learning-center/how-rbac-improves-api-permission-management) works well
for broad access decisions: admins can manage users, editors can update content,
viewers can read it. You assign roles, check them at the API layer, and move on.

But RBAC starts to break down when your authorization requirements become more
granular:

- **Resource-level access**: "Can user A edit document 42?" RBAC only tells you
  that editors can edit documents in general — not which ones.
- **Contextual decisions**: "Can this request proceed given the user's
  department, the resource's classification, and the time of day?" Roles don't
  capture this context.
- **Relationship-based access**: "Can this user view this file because they
  belong to a team that owns the parent folder?" Role hierarchies can't model
  arbitrary resource relationships.

These gaps are exactly what OWASP categorizes as
[Broken Object Level Authorization (BOLA)](/learning-center/troubleshooting-broken-object-level-authorization)
and
[Broken Function Level Authorization (BFLA)](/learning-center/troubleshooting-broken-function-level-authorization)
— vulnerabilities that exist even when authentication and basic role checks are
in place.

## Understanding Fine-Grained Authorization

Fine-grained authorization moves beyond "can this role access this endpoint" to
"can this subject perform this action on this specific resource." There are
three main approaches.

### Attribute-Based Access Control (ABAC)

ABAC evaluates policies based on attributes of the subject, resource, action,
and environment. A policy engine might check that the requesting user's
department matches the resource's owner department, or that the request
originates from an approved network during business hours.

ABAC is sometimes called "policy-as-code" because the authorization logic lives
in a policy language (like Rego for Open Policy Agent, or Cedar for AWS Verified
Permissions) rather than in application code.

### Relationship-Based Access Control (ReBAC)

ReBAC models authorization as a graph of relationships between subjects and
resources. Instead of asking "does this user have the editor role?", ReBAC asks
"does this user have an editor relationship with this specific document?" This
approach, popularized by Google's Zanzibar paper, powers systems like OpenFGA
and Okta FGA.

ReBAC excels at modeling hierarchical and inherited permissions — a user who
owns a folder automatically gets access to documents within it.

### Policy Decision Points (PDPs)

Both ABAC and ReBAC typically rely on a **Policy Decision Point (PDP)** — an
external service that evaluates authorization requests and returns allow/deny
decisions. The application or gateway acts as the **Policy Enforcement Point
(PEP)**, asking the PDP for a decision and enforcing the result.

This separation is powerful: your authorization logic lives in one place, and
every service in your architecture can query the same PDP for consistent
decisions.

## What Is OpenID AuthZEN?

The challenge with fine-grained authorization has been interoperability. Every
PDP vendor — whether it's OPA, Cedar, Topaz, or a custom solution — has its own
API for requesting authorization decisions. If you want to swap out your policy
engine, you have to rewrite every integration point.

[AuthZEN](https://openid.net/wg/authzen/) is a specification from the OpenID
Foundation that solves this by defining a standard API for communicating with
PDPs. Think of it as what OpenID Connect did for authentication, but for
authorization. The [Authorization API 1.0](https://openid.github.io/authzen/)
specification defines a JSON-based request/response format that any PDP can
implement:

- **Subject**: Who is making the request (a user ID, a service identity)
- **Resource**: What is being accessed (a document, an API route, a database
  record)
- **Action**: What operation is being performed (read, write, delete)

The PDP receives this standardized payload, evaluates it against whatever policy
model it uses internally (RBAC, ABAC, ReBAC, or anything else), and returns a
decision. Because the interface is standard, you can switch between policy
engines without changing your enforcement points.

AuthZEN reached a significant milestone in early 2026 when the Authorization API
1.0 specification was approved as a Final Specification by the OpenID
Foundation. Gartner has recommended that organizations "adopt standards like
AuthZEN to reduce vendor lock-in and enhance interoperability" in their
authorization infrastructure.

## Gateway-Enforced vs. Application-Level Authorization

You can enforce authorization at two levels: in your application code, or at the
API gateway. Each approach has trade-offs.

### Application-level authorization

Authorization logic lives inside each service. The service extracts identity
information from the request, calls a PDP (or evaluates policies locally), and
decides whether to proceed.

**Advantages**: The service has full context — it knows the business domain, the
data model, and can make highly specific decisions.

**Disadvantages**: Every service must implement authorization correctly.
Authorization logic gets scattered across your codebase. A single missed check
creates a vulnerability.

### Gateway-level authorization

The API gateway intercepts requests before they reach your backend and makes
authorization decisions at the edge. The gateway extracts identity context (from
JWTs, API keys, or headers), constructs an authorization query, sends it to a
PDP, and either allows or blocks the request.

**Advantages**: Centralized enforcement means no backend service can
accidentally skip authorization. You get a single audit point for all access
decisions. Policies can be updated without redeploying application code.

**Disadvantages**: The gateway may not have deep business context. Some
decisions (like "can this user see this specific field in the response?") might
still need application-level checks.

### The best approach: both

In practice, gateway-enforced authorization works best as the first line of
defense. The gateway handles coarse-to-medium-grained checks — "is this user
allowed to call this endpoint with this method on this resource type?" — while
backend services handle any remaining fine-grained or business-specific logic.
This layered approach ensures that even if a backend service has a bug,
unauthorized requests never reach it.

## Implementing Fine-Grained Authorization with Zuplo

Zuplo's programmable API gateway supports fine-grained authorization natively,
with built-in policies for the AuthZEN standard and other popular authorization
systems.

### Using the AuthZEN policy

Zuplo provides a built-in
[AuthZEN Authorization Policy](https://zuplo.com/docs/policies/authzen-inbound)
that integrates directly with any AuthZEN-compatible PDP. You configure it in
your `policies.json` file with the PDP hostname and the subject, resource, and
action mappings:

```json
{
  "name": "my-authzen-inbound-policy",
  "policyType": "authzen-inbound",
  "handler": {
    "export": "AuthZenInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "authorizerHostname": "authzen.example.com",
      "authorizerAuthorizationHeader": "Bearer $env(AUTHZEN_PDP_TOKEN)",
      "subject": {
        "type": "identity",
        "id": "$authzen-prop(request.user.sub)"
      },
      "resource": {
        "type": "route",
        "id": "$authzen-prop(context.route.path)"
      },
      "action": {
        "name": "$authzen-prop(request.method)"
      },
      "throwOnError": true
    }
  }
}
```

The special `$authzen-prop()` syntax dynamically pulls values from the incoming
request or context — for example, `$authzen-prop(request.user.sub)` extracts the
authenticated user's subject identifier from the JWT, and
`$authzen-prop(context.route.path)` uses the matched route path as the resource
ID.

When a request comes in, the policy constructs a standard AuthZEN authorization
request, sends it to your PDP, and blocks the request with a 403 response if the
PDP returns a deny decision.

> **Note:** The AuthZEN policy is currently in beta and is available as part of
> Zuplo's [enterprise plans](https://zuplo.com/pricing). It's free to try on any
> plan for development purposes.

### Programmatic control with custom policies

For more complex scenarios, you can set the authorization payload
programmatically in a
[custom TypeScript policy](https://zuplo.com/docs/articles/custom-code-patterns)
that runs before the AuthZEN policy:

```typescript
import {
  ZuploRequest,
  ZuploContext,
  AuthZenInboundPolicy,
} from "@zuplo/runtime";

export default async function setAuthzContext(
  request: ZuploRequest,
  context: ZuploContext,
) {
  // Extract resource information from the request
  const documentId = request.params?.documentId;

  // Set a custom authorization payload for the AuthZEN PDP
  AuthZenInboundPolicy.setAuthorizationPayload(context, {
    subject: {
      type: "user",
      id: request.user.sub,
    },
    resource: {
      type: "document",
      id: documentId,
    },
    action: {
      name: request.method,
    },
  });

  return request;
}
```

This custom policy runs first in the pipeline, enriching the authorization
context with domain-specific information. The AuthZEN policy then sends this
payload to the PDP. This pattern lets you combine Zuplo's declarative
configuration with programmatic logic — start with the built-in
`$authzen-prop()` syntax for simple cases, and add custom policies when you need
richer context.

### Other fine-grained authorization policies

Zuplo also offers built-in policies for other popular authorization systems:

- [**OpenFGA Authorization Policy**](https://zuplo.com/docs/policies/openfga-authz-inbound)
  — integrates with OpenFGA for relationship-based access control using Google's
  Zanzibar model
- [**Okta FGA Authorization Policy**](https://zuplo.com/docs/policies/okta-fga-authz-inbound)
  — connects to Okta's hosted Fine-Grained Authorization service for managed
  ReBAC

Like the AuthZEN policy, the OpenFGA and Okta FGA policies are currently in beta
and available on enterprise plans. Each follows the same pattern: configure the
connection to your authorization service, set the context checks (either
declaratively or programmatically), and let the gateway enforce the decision.

### Chaining policies for layered security

A common pattern is to chain authentication and authorization policies on a
route. For example, you might verify a JWT first, then run an AuthZEN
authorization check:

```json
{
  "paths": {
    "/documents/{documentId}": {
      "get": {
        "x-zuplo-route": {
          "policies": {
            "inbound": [
              "jwt-auth-inbound",
              "set-authz-context",
              "my-authzen-inbound-policy"
            ]
          }
        }
      }
    }
  }
}
```

This pipeline first authenticates the user (ensuring `request.user` is
populated), then runs your custom policy to set the authorization context, and
finally calls the AuthZEN PDP to make the access decision — all before the
request ever reaches your backend service.

## The Future of API Authorization

AuthZEN is part of a broader shift toward standardized, externalized
authorization. Several trends are converging:

- **AI agents and automated access**: As
  [agentic applications](/learning-center/owasp-top-10-agentic-applications-api-gateway)
  make autonomous API calls, the need for fine-grained, real-time authorization
  decisions becomes critical. An AI agent shouldn't inherit blanket permissions
  from the user who deployed it — each action should be individually authorized.
- **Zero-trust architectures**: The principle of "never trust, always verify"
  extends naturally to authorization. Every API call should be evaluated against
  the current policy state, not against a static role assignment cached in a
  token.
- **Authorization as infrastructure**: Just as authentication moved from
  homegrown solutions to standardized identity providers, authorization is
  moving to dedicated PDPs with standard interfaces. AuthZEN accelerates this by
  ensuring your enforcement points aren't locked into a single vendor.

The AuthZEN Working Group has outlined plans for deeper integration with
frameworks like the Shared Signals Framework, signaling that standardized
authorization is becoming a foundational layer of API infrastructure.

## Getting Started

If your APIs currently rely on RBAC alone, here's a practical path forward:

1. **Audit your authorization gaps**: Identify endpoints where role checks
   aren't sufficient — anywhere a user could access another user's resources by
   manipulating IDs or parameters.
2. **Choose a PDP**: Evaluate options like Topaz, OpenFGA, Cerbos, or
   Axiomatics. If you want vendor flexibility, prioritize PDPs that support the
   AuthZEN standard.
3. **Enforce at the gateway**: Configure Zuplo's
   [AuthZEN policy](https://zuplo.com/docs/policies/authzen-inbound) to call
   your PDP on every request. Start with a few critical routes and expand from
   there.
4. **Layer your defenses**: Use gateway-level authorization for broad
   enforcement and add application-level checks where you need deep business
   context.

Fine-grained authorization isn't just a security improvement — it's the
foundation for building APIs that can safely serve multi-tenant applications, AI
agents, and partner integrations. With standards like AuthZEN maturing and API
gateways like Zuplo providing native enforcement, the barrier to adopting these
patterns has never been lower.

Ready to enforce fine-grained authorization at your API gateway?
[Get started with Zuplo's AuthZEN policy](https://zuplo.com/docs/policies/authzen-inbound)
or explore the full suite of
[Zuplo authorization policies](https://zuplo.com/docs/articles/policies).