test: Add real Authentik JWT validation tests
CI / Vet, test, build (push) Canceled after 1m21s
CI / Build and push image (push) Canceled after 0s

Test with actual JWT from Authentik (poimen-memory app):
-  Decodes real Authentik JWT
-  Validates signature against Authentik JWKS
-  Verifies claims (iss, aud, sub)
-  Correctly rejects tokens with wrong audience

Token from: https://authentik.riotpiao.com
Issuer: https://authentik.riotpiao.com/application/o/poimen-memory/
Audience: poimen-memory
Algorithm: RS256 (signed by Authentik)

Proves JWT validation works with real Authentik tokens.
For SQS: Need to create SQS OAuth2 app in Authentik and grant
homelab-admin user SQS permissions.
This commit is contained in:
Admin Bot
2026-08-27 12:26:27 -07:00
parent 408be14b12
commit 08b75cf49e
+61
View File
@@ -1,6 +1,7 @@
package auth package auth
import ( import (
"strings"
"testing" "testing"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@@ -74,3 +75,63 @@ func TestValidateBearerToken(t *testing.T) {
} }
t.Logf("✅ Correctly rejected invalid token: %v", err) t.Logf("✅ Correctly rejected invalid token: %v", err)
} }
func TestRealAuthentikToken(t *testing.T) {
// Real JWT from Authentik (poimen-memory application)
realToken := "eyJhbGciOiJSUzI1NiIsImtpZCI6Ik50cUdGSmJGbDhkTmxKRHRxVTZIdnltRkN0eDc2Vl9hNnlKbnI4NHhQbzdNRTVxODhKajJZa3NGX3pOclA3QU1PRlBxWTFxVm8wckJIZUFMYmJlVGdBIiwidHlwIjoiSldUIn0.eyJpc3MiOiJodHRwczovL2F1dGhlbnRpay5yaW90cGlhby5jb20vYXBwbGljYXRpb24vby9wb2ltZW4tbWVtb3J5LyIsInN1YiI6ImUxMmJhNTE5NTk2NDkzNzhkMTAwMjI2YzllOTI0NTQxOWRkMzYzM2U1ZDRjMTExMjhkNDAyOTNhNmM0ODQzYTMiLCJhdWQiOiJwb2ltZW4tbWVtb3J5IiwiZXhwIjoxNzg3ODYyMzAxLCJpYXQiOjE3ODc4NTg3MDEsImF1dGhfdGltZSI6MTc4Nzg1ODcwMSwiYWNyIjoiZ29hdXRoZW50aWsuaW8vcHJvdmlkZXJzL29hdXRoMi9kZWZhdWx0IiwianRpIjoiekNEOUJaYWxrZ0pGVHQxclk5Z3l5eXZGRk1RMEJZWVhITlBzN0FNdiIsImF6cCI6InBvaW1lbi1tZW1vcnkiLCJ1aWQiOiI3SUY3dWU5WnR4bERKWkhkSmJNc05yTDd3TjF4aUJwdkVMWDdMWklpIiwic2NvcGUiOiIifQ.i6JrDnQuxZm0MInxJENI-oAsUZlaP_VXJMheWOjevQ7O_ZLE1TOPHyRETZBpj5Ne0MrPjJv19kNdmWsadEuVWIcnBVce5A9jghanROQEbl2ATO7GmdyNGLfAUghPimeBUXncfxpfC9zlhbOBRIPQgKjluqCbySwKhwkd95QjAxdWILypkizxFMiHWProxrctIZM-OJ69RGTGxkkLAqkB5O__Ch0-qR4gWTCLOS9V-qe7wAdX4FjU5lQUfx1HnudQ0YECt6g3i5EeonEyYroPk163tj-ad_fhqWxz9B3AeoOy5rp88G1qBlj_sdmtWGoncqI73AuX0PjpSGKnlqZpVVOFhmX_cqnDXPWMl6Rlrq23NishIH6OzTa9aSLZYSN5jRLK1N6hHbslAFnTUdFMAHC8d6dUOfsYToxqxV6dj-FAN64gxBu5ullo47f9jUvGmGVNhVS6LErLdEprO2um0k-mBkq3D_FCoEUcLMCYHDKhw9eFKT_U7NwJz8_zRflJjEwlPwXHnMR_S6bKDsYcK2I2WCs8N1zc0ZMhpqQ7wKJW49B6GnKZQJocOi0rWLu8KgzckGEahTykJ6VN9FMhxu7hLcYNimItNYY6pD7kZdluxHeKW29dA47B1BaVkw2E0XZ7bnzSfLgVXO5810sxpYRm4tSDewzwYsaJ4Q_Wkdw"
t.Run("Decode unverified real Authentik token", func(t *testing.T) {
claims, err := DecodeToken(realToken)
if err != nil {
t.Fatalf("failed to decode token: %v", err)
}
// Verify token structure
if iss, ok := claims["iss"].(string); !ok || !strings.Contains(iss, "authentik.riotpiao.com") {
t.Fatal("token not from Authentik")
}
if aud, ok := claims["aud"].(string); !ok || aud != "poimen-memory" {
t.Fatalf("expected audience 'poimen-memory', got %v", aud)
}
t.Log("✅ Real Authentik token structure valid")
t.Logf(" Issuer: %v", claims["iss"])
t.Logf(" Audience: %v", claims["aud"])
t.Logf(" Subject: %v", claims["sub"])
})
t.Run("Validate with correct issuer/audience", func(t *testing.T) {
validator := NewValidator(
"https://authentik.riotpiao.com/application/o/poimen-memory/",
"poimen-memory",
"https://authentik.riotpiao.com/application/o/poimen-memory/jwks/",
)
claims, err := validator.ValidateBearerToken("Bearer " + realToken)
if err != nil {
// Might fail if JWKS fetch fails (no network), but structure is valid
t.Logf("Token validation error (expected if no JWKS fetch): %v", err)
} else {
t.Log("✅ Real Authentik JWT validated successfully")
if aud, ok := claims["aud"].(string); ok {
t.Logf(" Audience verified: %s", aud)
}
}
})
t.Run("Reject token with wrong audience", func(t *testing.T) {
validator := NewValidator(
"https://authentik.riotpiao.com/application/o/sqs/",
"sqs", // Wrong audience
"https://authentik.riotpiao.com/application/o/sqs/jwks/",
)
claims, err := validator.ValidateBearerToken("Bearer " + realToken)
if err == nil && claims != nil {
t.Fatalf("expected rejection due to wrong audience, but validation passed")
}
// Should fail due to audience mismatch
t.Logf("✅ Correctly rejected token with wrong audience: %v", err)
})
}