From abbc9a7093a9067e80fdc1067dcfadf42f8e9f38 Mon Sep 17 00:00:00 2001 From: ila <2+ila@noreply.git.ilapage.cn> Date: Thu, 13 Aug 2026 21:32:49 +0800 Subject: [PATCH] fix: authenticate same-origin player navigation (#54) --- Sense/server/app/sense/identity/http.go | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Sense/server/app/sense/identity/http.go b/Sense/server/app/sense/identity/http.go index 977eee0..6a3f00d 100644 --- a/Sense/server/app/sense/identity/http.go +++ b/Sense/server/app/sense/identity/http.go @@ -48,6 +48,21 @@ func Require(permission string, next http.Handler) http.Handler { }) } +// RequireSameOriginFrame authenticates browser iframe navigation that cannot +// attach the X-Product header used by API clients. Fetch Metadata keeps this +// narrow: only a same-origin iframe navigation with a valid Sense session is +// accepted. +func RequireSameOriginFrame(permission string, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + module := activeModule.Load() + if module == nil { + platform.WriteError(w, &platform.APIError{Status: http.StatusServiceUnavailable, Code: "identity_not_ready", Message: "身份服务尚未就绪"}) + return + } + module.RequireSameOriginFrame(permission, next).ServeHTTP(w, r) + }) +} + func PrincipalFromContext(ctx context.Context) (Principal, bool) { principal, ok := ctx.Value(principalContextKey{}).(Principal) return principal, ok @@ -84,6 +99,28 @@ func (m *Module) Require(permission string, next http.Handler) http.Handler { })) } +func (m *Module) RequireSameOriginFrame(permission string, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("Sec-Fetch-Site") != "same-origin" || + r.Header.Get("Sec-Fetch-Mode") != "navigate" || + r.Header.Get("Sec-Fetch-Dest") != "iframe" { + platform.WriteError(w, &platform.APIError{Status: http.StatusUnauthorized, Code: "unauthorized", Message: "登录状态无效"}) + return + } + cookie, err := r.Cookie(sessionCookieName) + if err != nil { + platform.WriteError(w, &platform.APIError{Status: http.StatusUnauthorized, Code: "unauthorized", Message: "登录状态无效"}) + return + } + principal, err := m.service.Authenticate(r.Context(), cookie.Value) + if err != nil || !HasPermission(principal.Role, permission) { + platform.WriteError(w, &platform.APIError{Status: http.StatusUnauthorized, Code: "unauthorized", Message: "登录状态无效"}) + return + } + next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), principalContextKey{}, principal))) + }) +} + func (m *Module) bootstrap(w http.ResponseWriter, r *http.Request) { var request struct { Username string `json:"username"` @@ -220,3 +257,4 @@ func SecureCookieFromEnvironment(value string, memoryMode bool) bool { } return !strings.EqualFold(value, "false") } +