2026-08-27 11:40:56 -07:00
|
|
|
# 3.1 — SQS: Gateway JWT validation against Authentik JWKS (GREEN)
|
2026-08-27 11:36:13 -07:00
|
|
|
|
|
|
|
|
Phase: 3 — Authentication & Authorization
|
2026-08-27 11:40:56 -07:00
|
|
|
Stage: GREEN ✅
|
2026-08-27 11:36:13 -07:00
|
|
|
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).
|
|
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
## Implementation (Done)
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
- [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
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
## Dependencies
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
- `github.com/MicahParks/keyfunc/v2`: JWKS fetching & caching
|
|
|
|
|
- `github.com/golang-jwt/jwt/v5`: JWT parsing & validation
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
## Verification (Done)
|
2026-08-27 11:36:13 -07:00
|
|
|
|
|
|
|
|
```bash
|
2026-08-27 11:40:56 -07:00
|
|
|
# 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)
|
2026-08-27 11:36:13 -07:00
|
|
|
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/
|
2026-08-27 11:40:56 -07:00
|
|
|
# expected: not 403 ✅
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
# 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 ✅
|
2026-08-27 11:36:13 -07:00
|
|
|
```
|
|
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
## Architecture
|
2026-08-27 11:36:13 -07:00
|
|
|
|
2026-08-27 11:40:56 -07:00
|
|
|
**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
|