providers/proxy: fix missing JWT/claims header (#17759)

* replace interface{} with any

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix raw token not saved to map or json

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* also fix proxy claims

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix test

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2025-10-28 15:14:07 +01:00
committed by GitHub
parent e2904d13a9
commit e7235732bb
3 changed files with 32 additions and 32 deletions
+2 -2
View File
@@ -64,7 +64,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims {
// Claims are always stored as types.Claims but may be deserialized differently:
// - Filesystem store (gob): preserves struct type as types.Claims
// - PostgreSQL store (JSON): deserializes as map[string]interface{}
// - PostgreSQL store (JSON): deserializes as map[string]any
// Handle struct type (filesystem store)
if c, ok := claims.(types.Claims); ok {
@@ -72,7 +72,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims {
}
// Handle map type (PostgreSQL store)
if claimsMap, ok := claims.(map[string]interface{}); ok {
if claimsMap, ok := claims.(map[string]any); ok {
var c types.Claims
if err := mapstructure.Decode(claimsMap, &c); err != nil {
return nil
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/gorilla/sessions"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -27,7 +28,7 @@ func TestClaimsJSONSerialization(t *testing.T) {
Entitlements: []string{"read", "write"},
Sid: "session-id-456",
Proxy: &types.ProxyClaims{
UserAttributes: map[string]interface{}{
UserAttributes: map[string]any{
"custom_field": "custom_value",
"department": "engineering",
},
@@ -70,35 +71,33 @@ func TestClaimsJSONSerialization(t *testing.T) {
assert.Equal(t, "engineering", parsedClaims.Proxy.UserAttributes["department"])
}
// TestClaimsMapSerialization tests that Claims stored as map[string]interface{} can be converted back
// TestClaimsMapSerialization tests that Claims stored as map[string]any can be converted back
func TestClaimsMapSerialization(t *testing.T) {
// Simulate how claims are stored in session as map (like from PostgreSQL JSONB)
claimsMap := map[string]interface{}{
claimsMap := map[string]any{
"sub": "user-id-123",
"exp": float64(1234567890), // json numbers become float64
"email": "test@example.com",
"email_verified": true,
"name": "Test User",
"preferred_username": "testuser",
"groups": []interface{}{"admin", "user"},
"entitlements": []interface{}{"read", "write"},
"groups": []any{"admin", "user"},
"entitlements": []any{"read", "write"},
"sid": "session-id-456",
"ak_proxy": map[string]interface{}{
"user_attributes": map[string]interface{}{
"ak_proxy": map[string]any{
"user_attributes": map[string]any{
"custom_field": "custom_value",
},
"backend_override": "custom-backend",
"host_header": "example.com",
"is_superuser": true,
},
"raw_token": "not-a-real-token",
}
// Convert map to Claims using JSON marshaling (like getClaimsFromSession does)
jsonData, err := json.Marshal(claimsMap)
require.NoError(t, err)
// Convert map to Claims using mapstructure marshaling (like getClaimsFromSession does)
var claims types.Claims
err = json.Unmarshal(jsonData, &claims)
err := mapstructure.Decode(claimsMap, &claims)
require.NoError(t, err)
// Verify fields
@@ -111,6 +110,7 @@ func TestClaimsMapSerialization(t *testing.T) {
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
assert.Equal(t, []string{"read", "write"}, claims.Entitlements)
assert.Equal(t, "session-id-456", claims.Sid)
assert.Equal(t, "not-a-real-token", claims.RawToken)
// Verify proxy claims
require.NotNil(t, claims.Proxy)
@@ -122,7 +122,7 @@ func TestClaimsMapSerialization(t *testing.T) {
// TestClaimsMinimalFields tests that Claims work with minimal required fields
func TestClaimsMinimalFields(t *testing.T) {
claimsMap := map[string]interface{}{
claimsMap := map[string]any{
"sub": "user-id-123",
"exp": float64(1234567890),
}
@@ -144,11 +144,11 @@ func TestClaimsMinimalFields(t *testing.T) {
// TestClaimsWithEmptyArrays tests that empty arrays are handled correctly
func TestClaimsWithEmptyArrays(t *testing.T) {
claimsMap := map[string]interface{}{
claimsMap := map[string]any{
"sub": "user-id-123",
"exp": float64(1234567890),
"groups": []interface{}{},
"entitlements": []interface{}{},
"groups": []any{},
"entitlements": []any{},
}
jsonData, err := json.Marshal(claimsMap)
@@ -167,7 +167,7 @@ func TestClaimsWithEmptyArrays(t *testing.T) {
// TestClaimsWithNullProxyClaims tests that null proxy claims don't cause issues
func TestClaimsWithNullProxyClaims(t *testing.T) {
claimsMap := map[string]interface{}{
claimsMap := map[string]any{
"sub": "user-id-123",
"exp": float64(1234567890),
"ak_proxy": nil,
@@ -185,18 +185,18 @@ func TestClaimsWithNullProxyClaims(t *testing.T) {
}
// TestGetClaimsFromSession_Success tests successful retrieval of claims from session
// uses a mock session that returns claims as map[string]interface{} to simulate
// uses a mock session that returns claims as map[string]any to simulate
// how PostgreSQL storage deserializes JSONB data
func TestGetClaimsFromSession_Success(t *testing.T) {
// Create a custom mock store that returns claims as map
store := &mockMapSessionStore{
claimsMap: map[string]interface{}{
claimsMap: map[string]any{
"sub": "user-id-123",
"exp": float64(1234567890),
"email": "test@example.com",
"email_verified": true,
"preferred_username": "testuser",
"groups": []interface{}{"admin", "user"},
"groups": []any{"admin", "user"},
},
}
@@ -217,9 +217,9 @@ func TestGetClaimsFromSession_Success(t *testing.T) {
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
}
// mockMapSessionStore is a mock session store that returns claims as map[string]interface{}
// mockMapSessionStore is a mock session store that returns claims as map[string]any
type mockMapSessionStore struct {
claimsMap map[string]interface{}
claimsMap map[string]any
}
func (m *mockMapSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) {
@@ -314,7 +314,7 @@ func TestClaimsRoundTrip(t *testing.T) {
Entitlements: []string{"ent1", "ent2"},
Sid: "session-789",
Proxy: &types.ProxyClaims{
UserAttributes: map[string]interface{}{
UserAttributes: map[string]any{
"attr1": "value1",
"attr2": float64(42),
"attr3": true,
@@ -329,8 +329,8 @@ func TestClaimsRoundTrip(t *testing.T) {
jsonData, err := json.Marshal(originalClaims)
require.NoError(t, err)
// Step 2: Deserialize to map[string]interface{} (simulating PostgreSQL load)
var claimsMap map[string]interface{}
// Step 2: Deserialize to map[string]any (simulating PostgreSQL load)
var claimsMap map[string]any
err = json.Unmarshal(jsonData, &claimsMap)
require.NoError(t, err)
+5 -5
View File
@@ -1,10 +1,10 @@
package types
type ProxyClaims struct {
UserAttributes map[string]interface{} `json:"user_attributes"`
BackendOverride string `json:"backend_override"`
HostHeader string `json:"host_header"`
IsSuperuser bool `json:"is_superuser"`
UserAttributes map[string]any `json:"user_attributes" mapstructure:"user_attributes"`
BackendOverride string `json:"backend_override" mapstructure:"backend_override"`
HostHeader string `json:"host_header" mapstructure:"host_header"`
IsSuperuser bool `json:"is_superuser" mapstructure:"is_superuser"`
}
type Claims struct {
@@ -19,5 +19,5 @@ type Claims struct {
Sid string `json:"sid" mapstructure:"sid"`
Proxy *ProxyClaims `json:"ak_proxy" mapstructure:"ak_proxy"`
RawToken string `mapstructure:"-"`
RawToken string `json:"raw_token" mapstructure:"raw_token"`
}