fix(auth): reject invalid bearer tokens instead of falling back

Modify the authentication handler to return an unauthorized error when
an invalid or disabled bearer token is provided, rather than silently
falling back to an anonymous request.

This ensures that clients attempting to authenticate but failing (due to
expired, malformed, or disabled tokens) are explicitly notified of the
auth failure instead of proceeding anonymously. True anonymous requests
without any Authorization header remain supported.
This commit is contained in:
2026-05-31 13:02:58 +03:00
parent d99f8ee82a
commit 61b7c283a4
28 changed files with 3503 additions and 3300 deletions

View File

@@ -100,25 +100,51 @@ func TestBearerTokenUploadActsAsUser(t *testing.T) {
t.Fatalf("OwnerID = %q, want %q", box.OwnerID, user.ID)
}
// An invalid bearer token must not authenticate as the user.
// An invalid bearer token is an authentication failure, not an anonymous upload.
badRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "x.txt", "x")
badRequest.Header.Set("Accept", "application/json")
badRequest.Header.Set("Authorization", "Bearer wbx_bogus.secret")
badResponse := httptest.NewRecorder()
app.Upload(badResponse, badRequest)
if badResponse.Code != http.StatusCreated {
t.Fatalf("anonymous fallback upload status = %d, body = %s", badResponse.Code, badResponse.Body.String())
if badResponse.Code != http.StatusUnauthorized {
t.Fatalf("invalid token upload status = %d, body = %s", badResponse.Code, badResponse.Body.String())
}
var badPayload services.UploadResult
if err := json.Unmarshal(badResponse.Body.Bytes(), &badPayload); err != nil {
t.Fatalf("json.Unmarshal returned error: %v", err)
}
func TestAnonymousUploadWithoutBearerStillWorks(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
response := httptest.NewRecorder()
app.Upload(response, multipartUploadRequest(t, "/api/v1/upload", "file", "anonymous.txt", "anonymous"))
if response.Code != http.StatusCreated {
t.Fatalf("anonymous upload status = %d, body = %s", response.Code, response.Body.String())
}
badBox, err := app.uploadService.GetBox(badPayload.BoxID)
}
func TestDisabledUserBearerTokenCannotUpload(t *testing.T) {
app, cleanup := newTestApp(t)
defer cleanup()
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
if err != nil {
t.Fatalf("GetBox returned error: %v", err)
t.Fatalf("CreateBootstrapUser returned error: %v", err)
}
if badBox.OwnerID != "" {
t.Fatalf("invalid token OwnerID = %q, want empty", badBox.OwnerID)
tokenResult, err := app.authService.CreateAPIToken(user.ID, "cli")
if err != nil {
t.Fatalf("CreateAPIToken returned error: %v", err)
}
if err := app.authService.DisableUser(user.ID, true); err != nil {
t.Fatalf("DisableUser returned error: %v", err)
}
request := multipartUploadRequest(t, "/api/v1/upload", "file", "blocked.txt", "blocked")
request.Header.Set("Accept", "application/json")
request.Header.Set("Authorization", "Bearer "+tokenResult.Plaintext)
response := httptest.NewRecorder()
app.Upload(response, request)
if response.Code != http.StatusUnauthorized {
t.Fatalf("disabled bearer upload status = %d, body = %s", response.Code, response.Body.String())
}
}