refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens * feat(auth): harden session issuance and distributed enforcement * fix(proxy): preserve trusted proxy compatibility defaults * refactor: address dashboard auth review feedback * refactor: remove classic frontend and flatten web app
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SessionCookieOriginGuard protects cookie-authenticated refresh/logout
|
||||
// endpoints when secure cookie mode is enabled. In insecure local development
|
||||
// mode it preserves the legacy behavior and intentionally performs no Origin
|
||||
// validation. It never adds CORS response headers and must not be installed on
|
||||
// relay routes.
|
||||
func SessionCookieOriginGuard() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if !common.SessionCookieSecure {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
origin, ok := requestBrowserOrigin(c.Request)
|
||||
if !ok || !isAllowedSessionOrigin(c.Request, origin) {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
|
||||
"success": false,
|
||||
"code": "AUTH_ORIGIN_FORBIDDEN",
|
||||
"message": "request origin is not allowed",
|
||||
})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func requestBrowserOrigin(request *http.Request) (string, bool) {
|
||||
originValues := request.Header.Values("Origin")
|
||||
if len(originValues) > 1 {
|
||||
return "", false
|
||||
}
|
||||
if len(originValues) == 1 {
|
||||
if strings.Contains(originValues[0], ",") {
|
||||
return "", false
|
||||
}
|
||||
origin, err := common.NormalizeOrigin(originValues[0])
|
||||
return origin, err == nil
|
||||
}
|
||||
refererValues := request.Header.Values("Referer")
|
||||
if len(refererValues) != 1 {
|
||||
return "", false
|
||||
}
|
||||
referer, err := url.Parse(strings.TrimSpace(refererValues[0]))
|
||||
if err != nil || referer.Scheme == "" || referer.Host == "" || referer.User != nil {
|
||||
return "", false
|
||||
}
|
||||
origin, err := common.NormalizeOrigin(referer.Scheme + "://" + referer.Host)
|
||||
return origin, err == nil
|
||||
}
|
||||
|
||||
func isAllowedSessionOrigin(request *http.Request, origin string) bool {
|
||||
requestScheme := "http"
|
||||
if request.TLS != nil {
|
||||
requestScheme = "https"
|
||||
}
|
||||
requestOrigin, err := common.NormalizeOrigin(requestScheme + "://" + request.Host)
|
||||
if err == nil && subtle.ConstantTimeCompare([]byte(origin), []byte(requestOrigin)) == 1 {
|
||||
return true
|
||||
}
|
||||
for _, trustedOrigin := range common.SessionCookieTrustedURLs {
|
||||
if subtle.ConstantTimeCompare([]byte(origin), []byte(trustedOrigin)) == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user