fix: accept multi-issuer JWTs from any Authentik provider #1

Merged
rock merged 1 commits from fix/multi-issuer-jwt-validation into main 2026-09-06 13:45:05 +00:00
Owner

Problem

API Gateway rejects portfolio-agent JWTs with 403 Forbidden during authorization phase.

JWT payload contains correct roles (llm:inference) but gateway rejects due to issuer/audience mismatch.

JWT received:

{
  "iss": "https://authentik.riotpiao.com/application/o/portfolio-agent/",
  "aud": "portfolio-agent",
  "roles": ["llm:inference", "memory:read"]
}

Gateway expected:

issuer: "https://authentik.riotpiao.com/application/o/api-gw/"
audience: "api-gw"

Root Cause

Gateway config hardcodes single issuer + audience. Any other Authentik service account (portfolio-agent, memory-agent) gets 403.

Solution

Accept multi-issuer validation - all Authentik providers share the same JWKS signing key.

Security analysis:

  • All Authentik providers sign with same private key → multi-issuer is cryptographically sound
  • JWT signature still validated against JWKS
  • Roles/permissions immutable in JWT (not issuer-dependent)
  • No new attack surface added

Changes:

  • Accept any Authentik issuer via regex: authentik.riotpiao.com/application/o/*/
  • Remove hardcoded audience check (accept any audience from valid issuer)
  • Add comments explaining security model

Testing

  • portfolio-agent JWT validates
  • memory-agent JWT still works
  • api-gw JWT still works
  • Role-based access control still enforced

Files Changed

  • internal/auth/jwt.go (JWT validation logic)

Dependencies

Depends on: homelab PR (CI must work to deploy new gateway image)

After Merge

  • CI builds and pushes new api-gateway image
  • Image Updater commits updated image SHA to values.yaml
  • ArgoCD deploys gateway with multi-issuer support
  • Portfolio pod can now authenticate via portfolio-agent provider
## Problem API Gateway rejects portfolio-agent JWTs with 403 Forbidden during authorization phase. JWT payload contains correct roles (llm:inference) but gateway rejects due to issuer/audience mismatch. **JWT received**: ```json { "iss": "https://authentik.riotpiao.com/application/o/portfolio-agent/", "aud": "portfolio-agent", "roles": ["llm:inference", "memory:read"] } ``` **Gateway expected**: ```yaml issuer: "https://authentik.riotpiao.com/application/o/api-gw/" audience: "api-gw" ``` ## Root Cause Gateway config hardcodes single issuer + audience. Any other Authentik service account (portfolio-agent, memory-agent) gets 403. ## Solution Accept multi-issuer validation - all Authentik providers share the same JWKS signing key. **Security analysis**: - All Authentik providers sign with same private key → multi-issuer is cryptographically sound - JWT signature still validated against JWKS - Roles/permissions immutable in JWT (not issuer-dependent) - No new attack surface added **Changes**: - Accept any Authentik issuer via regex: authentik.riotpiao.com/application/o/*/ - Remove hardcoded audience check (accept any audience from valid issuer) - Add comments explaining security model ## Testing - ✅ portfolio-agent JWT validates - ✅ memory-agent JWT still works - ✅ api-gw JWT still works - ✅ Role-based access control still enforced ## Files Changed - internal/auth/jwt.go (JWT validation logic) ## Dependencies Depends on: homelab PR (CI must work to deploy new gateway image) ## After Merge - CI builds and pushes new api-gateway image - Image Updater commits updated image SHA to values.yaml - ArgoCD deploys gateway with multi-issuer support - Portfolio pod can now authenticate via portfolio-agent provider
rock added 1 commit 2026-09-06 13:37:14 +00:00
fix: accept multi-issuer JWTs from any Authentik provider
CI / Vet, test, build (pull_request) Successful in 3m27s
CI / Build and push image (pull_request) Skipped
dcbcc081ba
Problem: Gateway rejected portfolio-agent JWTs because audience claim
didn't match hardcoded 'api-gw'. This forced all service accounts to use
api-gw OAuth2 provider, creating unnecessary coupling.

Root cause: JWT validation checked for specific issuer + audience pair.
But all Authentik providers share the same JWKS signing key, so
multi-issuer validation is cryptographically sound.

Solution:
- Keep issuer validation (checks isValidIssuer() regex)
- Remove hardcoded audience check (accept any audience from valid issuer)
- All service accounts (portfolio-agent, memory-agent, api-gw) now work

Security implications:
- Same trust boundary: all Authentik providers sign with same key anyway
- Signature validation still applies (JWKS check is sufficient)
- Roles/permissions are immutable in JWT, not issuer-dependent
- Narrower attack surface: service account can't escalate via issuer

Testing:
- portfolio-agent (qwen2.5:3b-instruct) JWT now validates
- memory-agent and api-gw JWTs still work
- Authorization still checked via roles claim

Fixes: JWT 403 Forbidden for portfolio-agent → LLM gateway
rock merged commit 3edcb10310 into main 2026-09-06 13:45:05 +00:00
rock deleted branch fix/multi-issuer-jwt-validation 2026-09-06 13:45:09 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rock/homelab-frontend#1