mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-09 01:46:46 +08:00
Co-authored-by: Multica Eve <[email protected]> Co-authored-by: multica-agent <[email protected]>
65 lines
2.4 KiB
Go
65 lines
2.4 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/multica-ai/multica/server/internal/auth"
|
|
)
|
|
|
|
// RefreshCloudFrontCookies is middleware that refreshes CloudFront signed cookies
|
|
// on authenticated requests when the cookie is missing (expired or first request
|
|
// after login). This prevents 403s from the CDN when cookies expire before the
|
|
// user's session does.
|
|
//
|
|
// Renewal-time re-signing is NOT done here — it belongs to the auth
|
|
// middleware, which is the only place that knows a session was renewed and is
|
|
// mounted on every group that can renew one. This middleware only skips its
|
|
// own work when that already happened, so a single response does not carry
|
|
// two sets of CDN cookies (MUL-7436).
|
|
//
|
|
// Non-browser clients (CLI, daemon) are skipped entirely: they never keep a
|
|
// cookie jar, so the cookie is "missing" on every request and each one paid an
|
|
// RSA signature for Set-Cookie headers that were thrown away — ~18% of API CPU
|
|
// in production (MUL-8190). Trusting the client-controlled X-Client-Platform
|
|
// header is safe here because a spoofed value can only make the server
|
|
// withhold CDN cookies from the spoofer; it never grants anything.
|
|
func RefreshCloudFrontCookies(signer *auth.CloudFrontSigner) func(http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
if signer == nil {
|
|
return next
|
|
}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if isCookielessClient(r) {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
_, err := r.Cookie("CloudFront-Policy")
|
|
if err != nil && !SessionRenewed(r) {
|
|
ttl := auth.AuthTokenTTL()
|
|
for _, cookie := range signer.SignedCookies(time.Now().Add(ttl)) {
|
|
http.SetCookie(w, cookie)
|
|
}
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
}
|
|
|
|
// cookielessClientPlatforms are X-Client-Platform values sent by first-party
|
|
// clients that never store cookies (server/internal/cli and
|
|
// server/internal/daemon). Browser-based clients (web, desktop, mobile) are
|
|
// intentionally absent: they rely on the CDN cookies to load attachments.
|
|
var cookielessClientPlatforms = map[string]struct{}{
|
|
"cli": {},
|
|
"daemon": {},
|
|
}
|
|
|
|
// isCookielessClient reads the header directly rather than the
|
|
// ClientMetadata context value so the check does not depend on middleware
|
|
// ordering.
|
|
func isCookielessClient(r *http.Request) bool {
|
|
_, ok := cookielessClientPlatforms[r.Header.Get(HeaderClientPlatform)]
|
|
return ok
|
|
}
|