mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-13 18:02:14 +03:00
fix(api): return 401 for invalid Bearer token instead of 404 (#6459)
When Authorization: Bearer is present but does not match (or is disabled), respond with 401 Unauthorized so script authors can distinguish auth failure from a wrong webBasePath. Requests with no Authorization header still get 404 masking; wrong base paths continue to 404 via NoRoute. Fixes #6255 Co-authored-by: mrchatam <mrchatam@users.noreply.github.com>
This commit is contained in:
@@ -61,7 +61,11 @@ func (a *APIController) checkAPIAuth(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
if !session.IsLogin(c) {
|
||||
if c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
||||
// A presented Bearer token is not an anonymous scan: return 401 so
|
||||
// callers can distinguish a bad/disabled token from a wrong base path
|
||||
// (NoRoute still 404s). XHR keeps 401; bare unauthenticated stays 404.
|
||||
authHdr := c.GetHeader("Authorization")
|
||||
if strings.HasPrefix(authHdr, "Bearer ") || c.GetHeader("X-Requested-With") == "XMLHttpRequest" {
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
} else {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
|
||||
@@ -219,18 +219,22 @@ func TestCheckAPIAuth_EmptyVerifiedChainsFallsThrough(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckAPIAuth_RejectsUnauthenticated characterizes the reject paths: no
|
||||
// bearer token and no session yields 401 for XHR callers and 404 otherwise.
|
||||
// TestCheckAPIAuth_RejectsUnauthenticated characterizes the reject paths:
|
||||
// no credential → 404 (masking); XHR or a presented (but invalid) Bearer → 401
|
||||
// so script authors can tell auth failure from a wrong base path.
|
||||
func TestCheckAPIAuth_RejectsUnauthenticated(t *testing.T) {
|
||||
engine, _ := newAPIAuthTestEngine(t)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
xhr bool
|
||||
want int
|
||||
name string
|
||||
xhr bool
|
||||
bearer string // empty = omit Authorization header
|
||||
want int
|
||||
}{
|
||||
{"xhr gets 401", true, http.StatusUnauthorized},
|
||||
{"non-xhr gets 404", false, http.StatusNotFound},
|
||||
{"xhr gets 401", true, "", http.StatusUnauthorized},
|
||||
{"non-xhr gets 404", false, "", http.StatusNotFound},
|
||||
{"invalid bearer gets 401", false, "definitely-not-a-token", http.StatusUnauthorized},
|
||||
{"invalid bearer xhr gets 401", true, "definitely-not-a-token", http.StatusUnauthorized},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
@@ -238,6 +242,9 @@ func TestCheckAPIAuth_RejectsUnauthenticated(t *testing.T) {
|
||||
if c.xhr {
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
}
|
||||
if c.bearer != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+c.bearer)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
engine.ServeHTTP(w, req)
|
||||
if w.Code != c.want {
|
||||
|
||||
Reference in New Issue
Block a user