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:
- 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.
- RequestRocket validates the JWT via a
jwtVerifyproxy credential and uses filtertokenchecks to enforce that the required claim is present before forwarding. - 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.