# 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) - [x] `internal/auth/jwt.go`: JWT validator with JWKS caching - [x] Validates JWT signature against Authentik JWKS (RS256) - [x] Verifies claims: iss, aud, exp, nbf (60s skew) - [x] Checks `permissions` claim for sqs:read/sqs:write/wildcard - [x] Returns 403 with error details on failure - [x] JWKS cache: 15min TTL, auto-refresh on kid miss - [x] Wired into `internal/serviceadapter/router.go` for SQS - [x] 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 (✅ COMPLETE - 10/10 Tests Pass) ### Full Integration Test Results **Test Environment:** - Gateway: http://127.0.0.1:8080 - SQS mock: http://127.0.0.1:9090 - Memory mock: http://127.0.0.1:8081 - S3 mock: http://127.0.0.1:9000 - IAM mock: http://127.0.0.1:8082 ### SQS JWT Validation ✅ 1. **Request without Authorization** → **403 Forbidden** "SQS requires Authorization header" 2. **Request with invalid JWT** → **403 Forbidden** "JWT validation failed..." ### Memory Service Routing ✅ 3. **POST query** → **200 OK** with response body proxied 4. **GET projects** → **200 OK** with response body proxied 5. **POST create project** → **200 OK** with response body proxied ### S3 Service Routing ✅ 6. **GET list-objects** → **200 OK** with response body proxied 7. **PUT put-object** → **201 Created** with response body proxied ### IAM Service Routing ✅ 8. **GET list-roles** → **200 OK** with response body proxied 9. **POST create-user** → **201 Created** with response body proxied ### Error Handling ✅ 10. **GET unknown service** → **404 Not Found** "service 'unknown-svc' not found" **Result: 10/10 tests PASS ✅** ## Verification (Done) ```bash # See /tmp/FULL_INTEGRATION_TEST_REPORT.md for complete test output # Live test examples: # 1. SQS without JWT -> 403 curl http://127.0.0.1:8080/ \ -H 'X-Service: sqs' -H 'X-Resource: send-message' \ -d '{"queue":"test"}' # Response: {"type":"about:blank#forbidden","status":403,"detail":"SQS requires Authorization header"} # 2. SQS with invalid JWT -> 403 curl http://127.0.0.1:8080/ \ -H 'X-Service: sqs' -H 'X-Resource: send-message' \ -H 'Authorization: Bearer invalid' \ -d '{"queue":"test"}' # Response: {"type":"about:blank#forbidden","status":403,"detail":"JWT validation failed..."} # 3. Memory without JWT -> 200 (no auth required) curl http://127.0.0.1:8080/ \ -H 'X-Service: memory' -H 'X-Resource: query' \ -d '{"text":"find users"}' # Response: {"result":"queried","data":{...}} # 4. S3 PUT -> 201 (no auth required) curl -X PUT http://127.0.0.1:8080/ \ -H 'X-Service: s3' -H 'X-Resource: put-object' \ -d '{"key":"file.txt"}' # Response: {"status":"created","etag":"abc123"} # 5. IAM POST -> 201 (no auth required) curl -X POST http://127.0.0.1:8080/ \ -H 'X-Service: iam' -H 'X-Resource: create-user' \ -d '{"username":"alice"}' # Response: {"status":"created","user":{...}} ``` ## 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