diff --git a/internal/api/grpc/server/gateway.go b/internal/api/grpc/server/gateway.go index 40b3b52e8..2a54f065a 100644 --- a/internal/api/grpc/server/gateway.go +++ b/internal/api/grpc/server/gateway.go @@ -134,7 +134,6 @@ func createDialOptions(g Gateway) []grpc.DialOption { func addInterceptors(handler http.Handler, g Gateway) http.Handler { handler = http_mw.DefaultMetricsHandler(handler) handler = http_mw.DefaultTelemetryHandler(handler) - handler = http_mw.NoCacheInterceptor(handler) if interceptor, ok := g.(grpcGatewayCustomInterceptor); ok { handler = interceptor.GatewayHTTPInterceptor(handler) } diff --git a/internal/api/grpc/server/middleware/cache_interceptor.go b/internal/api/grpc/server/middleware/cache_interceptor.go new file mode 100644 index 000000000..d258bcd9d --- /dev/null +++ b/internal/api/grpc/server/middleware/cache_interceptor.go @@ -0,0 +1,32 @@ +package middleware + +import ( + "context" + "net/http" + "time" + + "github.com/caos/logging" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + _ "github.com/caos/zitadel/internal/statik" +) + +func NoCacheInterceptor() grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + headers := map[string]string{ + "cache-control": "no-store", + "expires": time.Now().UTC().Format(http.TimeFormat), + "pragma": "no-cache", + } + header := metadata.New(headers) + for key, value := range headers { + header.Append(runtime.MetadataHeaderPrefix+key, value) + } + err := grpc.SendHeader(ctx, header) + logging.Log("MIDDLE-efh41").OnError(err).WithField("req", info.FullMethod).Warn("cannot send cache-control on grpc response") + resp, err := handler(ctx, req) + return resp, err + } +} diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go index 17ae7bf8d..7e841efb2 100644 --- a/internal/api/grpc/server/server.go +++ b/internal/api/grpc/server/server.go @@ -36,6 +36,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, lang l middleware.DefaultTracingServer(), middleware.MetricsHandler(metricTypes, grpc_api.Probes...), middleware.SentryHandler(), + middleware.NoCacheInterceptor(), middleware.ErrorHandler(), middleware.AuthorizationInterceptor(verifier, authConfig), middleware.TranslationHandler(lang), diff --git a/internal/api/http/middleware/cache_interceptor.go b/internal/api/http/middleware/cache_interceptor.go index 884bcea1f..b9b0528f0 100644 --- a/internal/api/http/middleware/cache_interceptor.go +++ b/internal/api/http/middleware/cache_interceptor.go @@ -81,11 +81,29 @@ func AssetsCacheInterceptor(maxAge, sharedMaxAge time.Duration, h http.Handler) func CacheInterceptorOpts(h http.Handler, cache *Cache) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - cache.serializeHeaders(w) - h.ServeHTTP(w, req) + cachingResponseWriter := &cachingResponseWriter{ + ResponseWriter: w, + Cache: cache, + } + h.ServeHTTP(cachingResponseWriter, req) }) } +type cachingResponseWriter struct { + http.ResponseWriter + *Cache +} + +func (w *cachingResponseWriter) WriteHeader(code int) { + if code >= 400 { + NeverCacheOptions.serializeHeaders(w.ResponseWriter) + w.ResponseWriter.WriteHeader(code) + return + } + w.Cache.serializeHeaders(w.ResponseWriter) + w.ResponseWriter.WriteHeader(code) +} + func (c *Cache) serializeHeaders(w http.ResponseWriter) { control := make([]string, 0, 6) pragma := false