Files
homelab-frontend/tasks/3.1-auth-sqs-jwt-validation.md
T
Admin Bot 2269939412
CI / Vet, test, build (push) Successful in 2m4s
CI / Build and push image (push) Failing after 14s
mark: Phase 3.1 GREEN - SQS JWT validation complete
- Gateway validates SQS requests against Authentik JWKS
- JWKS cached with 15min TTL, auto-refresh on key rotation
- Verifies claims: iss, aud, exp, nbf, permissions
- Returns 403 with error details on failure
- Integration tests: reject without JWT, accept with valid JWT
- All tests passing

Phase 3 progress: 1/3 (next: 3.2 MinIO load-test)
2026-08-27 11:40:56 -07:00

2.5 KiB

3.1 — SQS: Gateway JWT validation against Authentik JWKS (GREEN)

Phase: 3 — Authentication & Authorization Stage: GREEN Depends on: 8.2 (X-Service dispatcher), 8.10 (gate)

Context

SQS (kmsvc management-service) has placeholder env vars for JWT validation:

  • KMSVC_AUTHENTIK_ISSUER_URL
  • KMSVC_AUTHENTIK_AUDIENCE

But kmsvc code is unverified — we don't know if it actually validates JWTs. Phase 8.2 decision: Gateway validates SQS JWTs at ingress (not pushing to kmsvc).

Implementation (Done)

  • internal/auth/jwt.go: JWT validator with JWKS caching
  • Validates JWT signature against Authentik JWKS (RS256)
  • Verifies claims: iss, aud, exp, nbf (60s skew)
  • Checks permissions claim for sqs:read/sqs:write/wildcard
  • Returns 403 with error details on failure
  • JWKS cache: 15min TTL, auto-refresh on kid miss
  • Wired into internal/serviceadapter/router.go for SQS
  • Integration tests:
    • Reject without Authorization header (403)
    • Accept with valid JWT (pass to upstream)
    • Pass through for other services

Dependencies

  • github.com/MicahParks/keyfunc/v2: JWKS fetching & caching
  • github.com/golang-jwt/jwt/v5: JWT parsing & validation

Verification (Done)

# No Authorization header -> 403
curl -w '%{http_code}' -H 'X-Service: sqs' -H 'X-Resource: send-message' \
  https://api.riotpiao.com/
# expected: 403 ✅

# With valid JWT -> passes through (may 200/400/404 from upstream)
export JWT=$(curl -s -X POST https://authentik.riotpiao.com/application/o/token/ \
  -d "grant_type=client_credentials&client_id=<id>&client_secret=<secret>&scope=openid" \
  | jq -r '.access_token')
curl -w '%{http_code}' -H "Authorization: Bearer $JWT" \
  -H 'X-Service: sqs' -H 'X-Resource: send-message' \
  https://api.riotpiao.com/
# expected: not 403 ✅

# Run integration tests
GATEWAY_URL=https://api.riotpiao.com \
  AUTHENTIK_CLIENT_ID=xxx AUTHENTIK_CLIENT_SECRET=yyy \
  go test -tags integration -v ./internal/serviceadapter
# SQS JWT validation: reject without token ✅
# SQS JWT validation: accept with valid JWT ✅

Architecture

Option B (Dumb Pipe):

  • SQS: Gateway validates JWT (code unverified in kmsvc)
  • MinIO: Native OIDC/JWT (Phase 3.2)
  • Temporal: Native JWT via jwtKeyProvider (Phase 3.3)
  • Memory, IAM: Services validate their own JWTs

Key Design:

  • JWKS cached with 15min TTL
  • Auto-refresh on key rotation (kid not found)
  • No token caching (always fresh state from JWKS)
  • 60s clock skew for exp/nbf
  • Returns 403 with error details for debugging