Skip to content

feat(forward-auth): cache authorization decisions at the edge#13664

Open
shreemaan-abhishek wants to merge 1 commit into
apache:masterfrom
shreemaan-abhishek:perf/forward-auth-cache
Open

feat(forward-auth): cache authorization decisions at the edge#13664
shreemaan-abhishek wants to merge 1 commit into
apache:masterfrom
shreemaan-abhishek:perf/forward-auth-cache

Conversation

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

Description

forward-auth makes a synchronous HTTP call to the external authorization service on every request, which dominates its per-request overhead. This adds an opt-in per-worker cache of authorization decisions so repeated calls from the same caller can be served without hitting the auth service.

A new optional cache block:

"forward-auth": {
    "uri": "http://auth.internal/verify",
    "request_headers": ["Authorization"],
    "cache": {
        "key_headers": ["Authorization"],
        "ttl": 30,
        "include_method": false,
        "include_uri": false
    }
}

When present, an allow/deny decision is stored in a per-worker resty.lrucache and replayed on subsequent matching requests.

Because this is an authorization path, correctness is prioritized over speed:

  • Never cache across identities. The cache key is namespaced per plugin conf id + version (via plugin_ctx_id, so config edits invalidate it) and built from the operator-declared key_headers values, optionally the method/URI, and — for POST — an md5 of the forwarded body. key_headers is required whenever caching is enabled, so there is no implicit "cache everything" default.
  • Respects upstream cache-control. no-store / no-cache / private skip caching; max-age caps the TTL (and max-age=0 skips).
  • Fails closed on transient errors. Auth-service 5xx responses are never cached.
  • Faithful replay. Both allow (upstream headers) and deny (status, body, client headers) outcomes are reproduced on a hit, identical to the uncached path.
  • Fully backward compatible: with no cache block the plugin behaves exactly as before.

Which issue(s) this PR fixes

N/A (enhancement).

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (if not, please discuss on the APISIX mailing list first)

Tests

New t/plugin/forward-auth4.t counts actual auth-service invocations via a shared dict and asserts:

  • schema requires key_headers and enforces ttl bounds
  • identical requests hit the cache (1 auth call for N requests)
  • a different identity is never served another's cached decision, and a cached deny stays a deny
  • caching disabled still calls the auth service every time
  • upstream Cache-Control: no-store is honored (not cached)
  • include_uri keys on the request URI
  • the POST body is part of the cache key

Existing forward-auth.t / forward-auth2.t / forward-auth3.t still pass unchanged.

forward-auth makes a synchronous HTTP call to the authorization service
on every request. Add an opt-in cache block that stores allow/deny
decisions in a per-worker lrucache, cutting calls to the auth service
for repeated callers.

Correctness guardrails, since this is an auth path:
- Cache key is namespaced per plugin conf/version and built from the
  configured identity headers, optionally method/URI, and (for POST) a
  hash of the forwarded body, so decisions never cross identities.
- Honors upstream Cache-Control (no-store/no-cache/private skip caching,
  max-age caps the TTL) and never caches transient 5xx; short opt-in TTL
  by default.
- Both allow and deny outcomes are replayed faithfully on a hit.

Adds t/plugin/forward-auth4.t (hit/miss, identity isolation, deny
replay, no-store, include_uri, POST-body keying) and documents the
cache attributes with an example.
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. enhancement New feature or request labels Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant