Skip to content

Commit e723573

Browse files
authored
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>
1 parent e2904d1 commit e723573

File tree

3 files changed

+32
-32
lines changed

3 files changed

+32
-32
lines changed

internal/outpost/proxyv2/application/auth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims {
6464

6565
// Claims are always stored as types.Claims but may be deserialized differently:
6666
// - Filesystem store (gob): preserves struct type as types.Claims
67-
// - PostgreSQL store (JSON): deserializes as map[string]interface{}
67+
// - PostgreSQL store (JSON): deserializes as map[string]any
6868

6969
// Handle struct type (filesystem store)
7070
if c, ok := claims.(types.Claims); ok {
7171
return &c
7272
}
7373

7474
// Handle map type (PostgreSQL store)
75-
if claimsMap, ok := claims.(map[string]interface{}); ok {
75+
if claimsMap, ok := claims.(map[string]any); ok {
7676
var c types.Claims
7777
if err := mapstructure.Decode(claimsMap, &c); err != nil {
7878
return nil

internal/outpost/proxyv2/application/auth_test.go

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/gorilla/sessions"
10+
"github.com/mitchellh/mapstructure"
1011
"github.com/stretchr/testify/assert"
1112
"github.com/stretchr/testify/require"
1213

@@ -27,7 +28,7 @@ func TestClaimsJSONSerialization(t *testing.T) {
2728
Entitlements: []string{"read", "write"},
2829
Sid: "session-id-456",
2930
Proxy: &types.ProxyClaims{
30-
UserAttributes: map[string]interface{}{
31+
UserAttributes: map[string]any{
3132
"custom_field": "custom_value",
3233
"department": "engineering",
3334
},
@@ -70,35 +71,33 @@ func TestClaimsJSONSerialization(t *testing.T) {
7071
assert.Equal(t, "engineering", parsedClaims.Proxy.UserAttributes["department"])
7172
}
7273

73-
// TestClaimsMapSerialization tests that Claims stored as map[string]interface{} can be converted back
74+
// TestClaimsMapSerialization tests that Claims stored as map[string]any can be converted back
7475
func TestClaimsMapSerialization(t *testing.T) {
7576
// Simulate how claims are stored in session as map (like from PostgreSQL JSONB)
76-
claimsMap := map[string]interface{}{
77+
claimsMap := map[string]any{
7778
"sub": "user-id-123",
7879
"exp": float64(1234567890), // json numbers become float64
7980
"email": "test@example.com",
8081
"email_verified": true,
8182
"name": "Test User",
8283
"preferred_username": "testuser",
83-
"groups": []interface{}{"admin", "user"},
84-
"entitlements": []interface{}{"read", "write"},
84+
"groups": []any{"admin", "user"},
85+
"entitlements": []any{"read", "write"},
8586
"sid": "session-id-456",
86-
"ak_proxy": map[string]interface{}{
87-
"user_attributes": map[string]interface{}{
87+
"ak_proxy": map[string]any{
88+
"user_attributes": map[string]any{
8889
"custom_field": "custom_value",
8990
},
9091
"backend_override": "custom-backend",
9192
"host_header": "example.com",
9293
"is_superuser": true,
9394
},
95+
"raw_token": "not-a-real-token",
9496
}
9597

96-
// Convert map to Claims using JSON marshaling (like getClaimsFromSession does)
97-
jsonData, err := json.Marshal(claimsMap)
98-
require.NoError(t, err)
99-
98+
// Convert map to Claims using mapstructure marshaling (like getClaimsFromSession does)
10099
var claims types.Claims
101-
err = json.Unmarshal(jsonData, &claims)
100+
err := mapstructure.Decode(claimsMap, &claims)
102101
require.NoError(t, err)
103102

104103
// Verify fields
@@ -111,6 +110,7 @@ func TestClaimsMapSerialization(t *testing.T) {
111110
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
112111
assert.Equal(t, []string{"read", "write"}, claims.Entitlements)
113112
assert.Equal(t, "session-id-456", claims.Sid)
113+
assert.Equal(t, "not-a-real-token", claims.RawToken)
114114

115115
// Verify proxy claims
116116
require.NotNil(t, claims.Proxy)
@@ -122,7 +122,7 @@ func TestClaimsMapSerialization(t *testing.T) {
122122

123123
// TestClaimsMinimalFields tests that Claims work with minimal required fields
124124
func TestClaimsMinimalFields(t *testing.T) {
125-
claimsMap := map[string]interface{}{
125+
claimsMap := map[string]any{
126126
"sub": "user-id-123",
127127
"exp": float64(1234567890),
128128
}
@@ -144,11 +144,11 @@ func TestClaimsMinimalFields(t *testing.T) {
144144

145145
// TestClaimsWithEmptyArrays tests that empty arrays are handled correctly
146146
func TestClaimsWithEmptyArrays(t *testing.T) {
147-
claimsMap := map[string]interface{}{
147+
claimsMap := map[string]any{
148148
"sub": "user-id-123",
149149
"exp": float64(1234567890),
150-
"groups": []interface{}{},
151-
"entitlements": []interface{}{},
150+
"groups": []any{},
151+
"entitlements": []any{},
152152
}
153153

154154
jsonData, err := json.Marshal(claimsMap)
@@ -167,7 +167,7 @@ func TestClaimsWithEmptyArrays(t *testing.T) {
167167

168168
// TestClaimsWithNullProxyClaims tests that null proxy claims don't cause issues
169169
func TestClaimsWithNullProxyClaims(t *testing.T) {
170-
claimsMap := map[string]interface{}{
170+
claimsMap := map[string]any{
171171
"sub": "user-id-123",
172172
"exp": float64(1234567890),
173173
"ak_proxy": nil,
@@ -185,18 +185,18 @@ func TestClaimsWithNullProxyClaims(t *testing.T) {
185185
}
186186

187187
// TestGetClaimsFromSession_Success tests successful retrieval of claims from session
188-
// uses a mock session that returns claims as map[string]interface{} to simulate
188+
// uses a mock session that returns claims as map[string]any to simulate
189189
// how PostgreSQL storage deserializes JSONB data
190190
func TestGetClaimsFromSession_Success(t *testing.T) {
191191
// Create a custom mock store that returns claims as map
192192
store := &mockMapSessionStore{
193-
claimsMap: map[string]interface{}{
193+
claimsMap: map[string]any{
194194
"sub": "user-id-123",
195195
"exp": float64(1234567890),
196196
"email": "test@example.com",
197197
"email_verified": true,
198198
"preferred_username": "testuser",
199-
"groups": []interface{}{"admin", "user"},
199+
"groups": []any{"admin", "user"},
200200
},
201201
}
202202

@@ -217,9 +217,9 @@ func TestGetClaimsFromSession_Success(t *testing.T) {
217217
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
218218
}
219219

220-
// mockMapSessionStore is a mock session store that returns claims as map[string]interface{}
220+
// mockMapSessionStore is a mock session store that returns claims as map[string]any
221221
type mockMapSessionStore struct {
222-
claimsMap map[string]interface{}
222+
claimsMap map[string]any
223223
}
224224

225225
func (m *mockMapSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) {
@@ -314,7 +314,7 @@ func TestClaimsRoundTrip(t *testing.T) {
314314
Entitlements: []string{"ent1", "ent2"},
315315
Sid: "session-789",
316316
Proxy: &types.ProxyClaims{
317-
UserAttributes: map[string]interface{}{
317+
UserAttributes: map[string]any{
318318
"attr1": "value1",
319319
"attr2": float64(42),
320320
"attr3": true,
@@ -329,8 +329,8 @@ func TestClaimsRoundTrip(t *testing.T) {
329329
jsonData, err := json.Marshal(originalClaims)
330330
require.NoError(t, err)
331331

332-
// Step 2: Deserialize to map[string]interface{} (simulating PostgreSQL load)
333-
var claimsMap map[string]interface{}
332+
// Step 2: Deserialize to map[string]any (simulating PostgreSQL load)
333+
var claimsMap map[string]any
334334
err = json.Unmarshal(jsonData, &claimsMap)
335335
require.NoError(t, err)
336336

internal/outpost/proxyv2/types/claims.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package types
22

33
type ProxyClaims struct {
4-
UserAttributes map[string]interface{} `json:"user_attributes"`
5-
BackendOverride string `json:"backend_override"`
6-
HostHeader string `json:"host_header"`
7-
IsSuperuser bool `json:"is_superuser"`
4+
UserAttributes map[string]any `json:"user_attributes" mapstructure:"user_attributes"`
5+
BackendOverride string `json:"backend_override" mapstructure:"backend_override"`
6+
HostHeader string `json:"host_header" mapstructure:"host_header"`
7+
IsSuperuser bool `json:"is_superuser" mapstructure:"is_superuser"`
88
}
99

1010
type Claims struct {
@@ -19,5 +19,5 @@ type Claims struct {
1919
Sid string `json:"sid" mapstructure:"sid"`
2020
Proxy *ProxyClaims `json:"ak_proxy" mapstructure:"ak_proxy"`
2121

22-
RawToken string `mapstructure:"-"`
22+
RawToken string `json:"raw_token" mapstructure:"raw_token"`
2323
}

0 commit comments

Comments
 (0)