From bcd0821f3ef01c92c29a53dd4d2fb4472e4cc162 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 24 Jul 2025 17:53:11 +0200 Subject: [PATCH] [v12.0/forgejo] Revert "feat: remove API authentication methods that uses the URL query (#7924)" (#8653) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8633 This reverts commit b2a3966e648fc72eddf1bbc9383b069b0c8d59e8. weblate etc. are using this method and need to be updated before the change is enforced. Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8653 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/setting/security.go | 11 +++++++++++ routers/api/shared/middleware.go | 8 ++++++++ routers/api/v1/api.go | 12 ++++++++++++ services/auth/oauth2.go | 12 ++++++++++++ templates/swagger/v1_json.tmpl | 18 ++++++++++++++++++ tests/mysql.ini.tmpl | 1 + tests/pgsql.ini.tmpl | 1 + tests/sqlite.ini.tmpl | 1 + 8 files changed, 64 insertions(+) diff --git a/modules/setting/security.go b/modules/setting/security.go index c38d8dae79..f3480d1056 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -35,6 +35,7 @@ var ( PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int + DisableQueryAuthToken bool CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) @@ -159,4 +160,14 @@ func loadSecurityFrom(rootCfg ConfigProvider) { PasswordComplexity = append(PasswordComplexity, name) } } + + sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN") + + // TODO: default value should be true in future releases + DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false) + + // warn if the setting is set to false explicitly + if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.") + } } diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index 7d537f1ef9..f56acbe1bf 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -30,6 +30,7 @@ func Middlewares() (stack []any) { return append(stack, context.APIContexter(), + checkDeprecatedAuthMethods, // Get user from session if logged in. apiAuth(buildAuthGroup()), verifyAuthWithOptions(&common.VerifyOptions{ @@ -126,6 +127,13 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC } } +// check for and warn against deprecated authentication options +func checkDeprecatedAuthMethods(ctx *context.APIContext) { + if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + } +} + func securityHeaders() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bf08bdd249..95c3ee4791 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -22,6 +22,8 @@ // // Security: // - BasicAuth : +// - Token : +// - AccessToken : // - AuthorizationHeaderToken : // - SudoParam : // - SudoHeader : @@ -30,6 +32,16 @@ // SecurityDefinitions: // BasicAuth: // type: basic +// Token: +// type: apiKey +// name: token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. +// AccessToken: +// type: apiKey +// name: access_token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. // AuthorizationHeaderToken: // type: apiKey // name: Authorization diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 4fdd15d7ec..fa13c20a7f 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -122,6 +122,18 @@ func (o *OAuth2) Name() string { // representing whether the token exists or not func parseToken(req *http.Request) (string, bool) { _ = req.ParseForm() + if !setting.DisableQueryAuthToken { + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" { + log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true") + } // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 59c13cd9e6..9b4d79f0df 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -30058,6 +30058,12 @@ } }, "securityDefinitions": { + "AccessToken": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "access_token", + "in": "query" + }, "AuthorizationHeaderToken": { "description": "API tokens must be prepended with \"token\" followed by a space.", "type": "apiKey", @@ -30084,12 +30090,24 @@ "type": "apiKey", "name": "X-FORGEJO-OTP", "in": "header" + }, + "Token": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "token", + "in": "query" } }, "security": [ { "BasicAuth": [] }, + { + "Token": [] + }, + { + "AccessToken": [] + }, { "AuthorizationHeaderToken": [] }, diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index f44aff7594..3315d85a3f 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -92,6 +92,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/lfs diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 829fdc5b75..1e9b981800 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -97,6 +97,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] MINIO_BASE_PATH = lfs/ diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index d36388405b..df6cea44ca 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -94,6 +94,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8 +DISABLE_QUERY_AUTH_TOKEN = true [oauth2] JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko