fix(reverseproxy): strip incoming id-token header for unauthenticated requests

This commit is contained in:
Trong Huu Nguyen
2025-04-28 10:50:12 +02:00
parent 6b7e3e8349
commit 762b64eff5
3 changed files with 36 additions and 14 deletions
+9 -7
View File
@@ -32,13 +32,13 @@ type ReverseProxySource interface {
type ReverseProxy struct {
*httputil.ReverseProxy
EnableAccessLogs bool
IncludeIdToken bool
IncludeIDToken bool
}
func NewUpstreamProxy(upstream *urllib.URL, enableAccessLogs bool, includeIdToken bool) *ReverseProxy {
func NewUpstreamProxy(upstream *urllib.URL, enableAccessLogs bool, includeIDToken bool) *ReverseProxy {
rp := NewReverseProxy(upstream, true)
rp.EnableAccessLogs = enableAccessLogs
rp.IncludeIdToken = includeIdToken
rp.IncludeIDToken = includeIDToken
return rp
}
@@ -74,9 +74,12 @@ func NewReverseProxy(upstream *urllib.URL, preserveInboundHostHeader bool) *Reve
r.Out.Header.Set("authorization", "Bearer "+accessToken)
}
idToken, ok := mw.IdTokenFrom(r.In.Context())
idToken, ok := mw.IDTokenFrom(r.In.Context())
if ok {
r.Out.Header.Set("X-Wonderwall-Id-Token", idToken)
} else {
// remove the header if it was set by the client
r.Out.Header.Del("X-Wonderwall-Id-Token")
}
},
Transport: httpinternal.Transport(),
@@ -140,9 +143,8 @@ func (rp *ReverseProxy) Handler(src ReverseProxySource, w http.ResponseWriter, r
if isAuthenticated {
ctx = mw.WithAccessToken(ctx, accessToken)
span.SetAttributes(attribute.Bool("proxy.with_access_token", true))
if rp.IncludeIdToken && sess != nil {
idToken := sess.IDToken()
ctx = mw.WithIdToken(ctx, idToken)
if rp.IncludeIDToken && sess != nil {
ctx = mw.WithIDToken(ctx, sess.IDToken())
span.SetAttributes(attribute.Bool("proxy.with_id_token", true))
}
+22 -2
View File
@@ -444,7 +444,7 @@ func TestReverseProxy(t *testing.T) {
assertUpstreamOKResponse(t, resp)
})
t.Run("request should not include idToken by default", func(t *testing.T) {
t.Run("request should not include id_token by default", func(t *testing.T) {
cfg := mock.Config()
cfg.UpstreamHost = up.URL.Host
idp := mock.NewIdentityProvider(cfg)
@@ -464,7 +464,7 @@ func TestReverseProxy(t *testing.T) {
assertUpstreamOKResponse(t, resp)
})
t.Run("request should include idToken", func(t *testing.T) {
t.Run("request should include id_token", func(t *testing.T) {
cfg := mock.Config()
cfg.UpstreamHost = up.URL.Host
cfg.UpstreamIncludeIdToken = true
@@ -484,4 +484,24 @@ func TestReverseProxy(t *testing.T) {
resp := get(t, rpClient, idp.RelyingPartyServer.URL)
assertUpstreamOKResponse(t, resp)
})
t.Run("request should strip incoming id_token if unauthenticated", func(t *testing.T) {
cfg := mock.Config()
cfg.UpstreamHost = up.URL.Host
cfg.UpstreamIncludeIdToken = true
idp := mock.NewIdentityProvider(cfg)
defer idp.Close()
up.SetIdentityProvider(idp)
rpClient := idp.RelyingPartyClient()
up.requestCallback = func(r *http.Request) {
assert.Empty(t, r.Header.Get("x-wonderwall-id-token"))
}
resp := get(t, rpClient, idp.RelyingPartyServer.URL, header{
"x-wonderwall-id-token", "some-id-token",
})
assertUpstreamUnauthorizedResponse(t, resp)
})
}
+5 -5
View File
@@ -11,7 +11,7 @@ type contextKey string
const (
ctxAccessToken = contextKey("AccessToken")
ctxIdToken = contextKey("IdToken")
ctxIDToken = contextKey("IDToken")
ctxIngress = contextKey("Ingress")
ctxPath = contextKey("Path")
)
@@ -25,13 +25,13 @@ func WithAccessToken(ctx context.Context, accessToken string) context.Context {
return context.WithValue(ctx, ctxAccessToken, accessToken)
}
func IdTokenFrom(ctx context.Context) (string, bool) {
idToken, ok := ctx.Value(ctxIdToken).(string)
func IDTokenFrom(ctx context.Context) (string, bool) {
idToken, ok := ctx.Value(ctxIDToken).(string)
return idToken, ok
}
func WithIdToken(ctx context.Context, idToken string) context.Context {
return context.WithValue(ctx, ctxIdToken, idToken)
func WithIDToken(ctx context.Context, idToken string) context.Context {
return context.WithValue(ctx, ctxIDToken, idToken)
}
func IngressFrom(ctx context.Context) (ingress.Ingress, bool) {