Gitea: OAuth2 access token scope enforcement bypass via HTTP Basic authentication
Date: 2026-06
Affected: go/code.gitea.io/gitea
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-28699
CWE: CWE-284
CWE-863
CVSS: 8.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)

### Summary Gitea fails to enforce OAuth2 access token scopes when the token is submitted via HTTP Basic authentication instead of a Bearer token. An OAuth2 application granted only `read:user` can use the same token as `Authorization: Basic base64(<token>:x-oauth-basic)` and perform write actions, including modifying profiles, adding email addresses, creating repositories, and deleting repositories as the authorizing user. ### Details **Root cause:** `services/auth/basic.go` accepts OAuth2 access tokens through the Basic auth path but does not store the token scope in the request context: ```go // services/auth/basic.go if uid != 0 { store.GetData()["LoginMethod"] = OAuth2TokenMethodName store.GetData()["IsApiToken"] = true // scope is NOT set return u, nil } ``` The scope enforcement middleware in `routers/api/v1/api.go` exits early when `ApiTokenScope` is absent: ```go // routers/api/v1/api.go — tokenRequiresScopes scope, scopeExists := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope) if ctx.Data["IsApiToken"] != true || !scopeExists { return //<- exits without checking scope, all actions permitted } ``` When a token arrives via Bearer, `ApiTokenScope` is populated and scope checks apply normally. When the same token arrives via Basic auth, `ApiTokenScope` is never set, so `tokenRequiresScopes` returns immediately and no scope is enforced. **Suggested fix:** When an OAuth2 access token is accepted in `services/auth/basic.go`, populate `Ap


Mitigations:

Architectural layers (MAESTRO):