Blog

Fine-grained authorization with RequestRocket and OktaFGA

·4 min read

Why role-based checks stop being enough

Role-based access control (RBAC) answers “can this user role call this API?” It breaks down the moment your authorization question becomes more specific: “can this user access this specific resource given their relationship to it?”

Examples that RBAC can’t answer cleanly:

  • A user can view a document only if they own it or belong to its sharing group.
  • An agent can call /v1/orders/{orderId} only if that order belongs to the tenant in the agent’s JWT claim.
  • A team member can approve requests only for projects they’re assigned to.

Relationship-based access control (ReBAC), as implemented by OktaFGA (formerly Auth0 FGA), models these relationships explicitly as a graph. RequestRocket can enforce the results of FGA checks at the gateway layer using JWT claim matching in authorization rules — no backend changes required.

The integration model

The pattern has three parts:

  1. OktaFGA evaluates whether the calling identity has the required relationship to the requested resource, and includes the result (or relevant claims) in a downstream JWT.
  2. RequestRocket validates the JWT via a jwtVerify proxy credential and uses filter token checks to enforce that the required claim is present before forwarding.
  3. Your backend receives only requests that have already been authorized at the gateway.

Step 1: Configure JWT verification

Create a jwtVerify proxy credential pointing at your identity provider (the one issuing the FGA-enriched tokens):

POST /clients/{clientId}/credentials
{
  "credentialType": "proxy",
  "credentialAuthType": "jwtVerify",
  "credentialName": "okta-fga-enriched-token",
  "credentialRegion": "us-east-1",
  "credentialSecret": {
    "jwksUri": "https://your-tenant.okta.com/oauth2/v1/keys",
    "audience": "https://api.myapp.com",
    "issuer": "https://your-tenant.okta.com/"
  }
}

Step 2: Create a proxy with deny-by-default

POST /clients/{clientId}/proxies
{
  "proxyName": "orders-api-fga",
  "proxyRegion": "us-east-1",
  "proxyProxyCredentialId": "<okta-fga-enriched-token-id>",
  "proxyTargetId": "<orders-api-target-id>",
  "proxyTargetCredentialId": "<orders-api-internal-key-id>",
  "proxyDefaultRuleEffect": "deny"
}

Step 3: Enforce FGA claim requirements with authorization rules

OktaFGA can include authorization decisions as custom claims in the access token. For example, a claim permissions might contain orders:read when the calling identity has read access to orders.

Because the proxy credential is jwtVerify, authorization rules can inspect decoded claim values. Use a rule with effect: "deny" to block requests to the order endpoints when the required permission is absent:

POST /clients/{clientId}/proxies/{proxyId}/rules
{
  "effect": "deny",
  "methods": ["GET"],
  "path": {
    "path": { "pattern": "^/v1/orders(/.*)?$" },
    "presence": "must_exist"
  },
  "token": [
    {
      "claim": { "pattern": "^permissions$" },
      "value": { "pattern": "orders:read" },
      "presence": "must_absent"
    }
  ],
  "notes": "FGA enforcement: deny GET /v1/orders when orders:read permission is missing"
}

For write operations, add a matching rule for the orders:write permission:

POST /clients/{clientId}/proxies/{proxyId}/rules
{
  "effect": "deny",
  "methods": ["POST", "PUT", "PATCH"],
  "path": {
    "path": { "pattern": "^/v1/orders(/.*)?$" },
    "presence": "must_exist"
  },
  "token": [
    {
      "claim": { "pattern": "^permissions$" },
      "value": { "pattern": "orders:write" },
      "presence": "must_absent"
    }
  ],
  "notes": "FGA enforcement: deny mutating order operations when orders:write is missing"
}

Both rules are evaluated in the pre-request phase, before the request is forwarded. A missing permission returns a 403 immediately.

Step 4: Enforce tenant isolation using JWT claim variables

A more powerful pattern uses the variables system to extract the tenant ID from the JWT and require it to match a path parameter. For example, only allow /v1/tenants/{tenantId}/orders when the token’s tenant_id claim matches the {tenantId} in the path:

POST /clients/{clientId}/proxies/{proxyId}/rules
{
  "effect": "allow",
  "methods": ["GET", "POST"],
  "variables": [
    {
      "name": "tenantId",
      "source": "jwtClaim",
      "key": "tenant_id"
    }
  ],
  "path": {
    "path": {
      "pattern": "^/v1/tenants/{{tenantId}}/"
    },
    "presence": "must_exist"
  },
  "notes": "Tenant isolation: JWT tenant_id must match path tenant segment"
}

With proxyDefaultRuleEffect: "deny", only requests where the path’s tenant segment matches the JWT’s tenant_id claim are forwarded. Requests targeting another tenant’s data are denied at the gateway without ever reaching the backend.

What this gives you

  • Zero backend changes — authorization policy is enforced at the gateway, before your backend processes the request.
  • Consistent enforcement — every path through to the backend passes the same gateway rules, regardless of which client or agent is calling.
  • Auditable — denied requests appear in the request log with the path and credential that triggered the denial.
  • Composable — combine JWT claim rules with path checks, method restrictions, and rate limits on the same proxy.

Limitations to be aware of

This pattern enforces what’s in the JWT claim at request time. It does not call OktaFGA live on each request — that happens upstream when the token is issued. If OktaFGA relationship data changes between token issuance and expiry, the gateway won’t see the change until the token is refreshed. Use short JWT expiry times (5–15 minutes) for sensitive operations.

Next steps

JWT claim-based authorization rules in RequestRocket cover a large portion of practical authorization scenarios without requiring backend changes. Read the rule and credential documentation to see the full schema, or start for free.

Enhance ISO 27001
Enhance SOC 2
Enhance GDPR
Enhance HIPAA

Add outbound API security
without changing code

Start on your own or talk to our team about improving the security of every API call you make.