diff --git a/.editorconfig b/.editorconfig index d095d8435f..85b9cb913e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,9 +22,6 @@ insert_final_newline = false indent_style = space insert_final_newline = false -[templates/user/auth/oidc_wellknown.tmpl] -indent_style = space - [templates/shared/actions/runner_badge_*.tmpl] # editconfig lint requires these XML-like files to have charset defined, but the files don't have. charset = unset diff --git a/routers/web/auth/oauth2_provider.go b/routers/web/auth/oauth2_provider.go index 755afb14c7..8a54c0ea15 100644 --- a/routers/web/auth/oauth2_provider.go +++ b/routers/web/auth/oauth2_provider.go @@ -472,20 +472,6 @@ func GrantApplicationOAuth(ctx *context.Context) { ctx.Redirect(redirect.String(), http.StatusSeeOther) } -// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities -func OIDCWellKnown(ctx *context.Context) { - if !setting.OAuth2.Enabled { - http.NotFound(ctx.Resp, ctx.Req) - return - } - jwtRegisteredClaims := oauth2_provider.NewJwtRegisteredClaimsFromUser("well-known", 0, nil) - ctx.Data["OidcIssuer"] = jwtRegisteredClaims.Issuer // use the consistent issuer from the JWT registered claims - ctx.Data["OidcBaseUrl"] = strings.TrimSuffix(setting.AppURL, "/") - ctx.Data["SigningKeyMethodAlg"] = oauth2_provider.DefaultSigningKey.SigningMethod().Alg() - // FIXME: no need to use a Golang template to render JSON, just build the JSON response directly in the future - ctx.JSONTemplate("user/auth/oidc_wellknown") -} - // OIDCKeys generates the JSON Web Key Set func OIDCKeys(ctx *context.Context) { jwk, err := oauth2_provider.DefaultSigningKey.ToJWK() diff --git a/routers/web/auth/oauth2_wellknown.go b/routers/web/auth/oauth2_wellknown.go new file mode 100644 index 0000000000..6fb82b3cbd --- /dev/null +++ b/routers/web/auth/oauth2_wellknown.go @@ -0,0 +1,70 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "net/http" + "strings" + + "gitea.dev/modules/setting" + "gitea.dev/services/context" + "gitea.dev/services/oauth2_provider" +) + +// OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities +func OIDCWellKnown(ctx *context.Context) { + if !setting.OAuth2.Enabled { + http.NotFound(ctx.Resp, ctx.Req) + return + } + jwtRegisteredClaims := oauth2_provider.NewJwtRegisteredClaimsFromUser("well-known", 0, nil) + oidcIssuer := jwtRegisteredClaims.Issuer // use the consistent issuer from the JWT registered claims + oidcBaseUrl := strings.TrimSuffix(setting.AppURL, "/") + + m := map[string]any{ + "issuer": oidcIssuer, + "authorization_endpoint": oidcBaseUrl + "/login/oauth/authorize", + "token_endpoint": oidcBaseUrl + "/login/oauth/access_token", + "jwks_uri": oidcBaseUrl + "/login/oauth/keys", + "userinfo_endpoint": oidcBaseUrl + "/login/oauth/userinfo", + "introspection_endpoint": oidcBaseUrl + "/login/oauth/introspect", + "response_types_supported": []string{ + "code", + "id_token", + }, + "id_token_signing_alg_values_supported": []string{ + oauth2_provider.DefaultSigningKey.SigningMethod().Alg(), + }, + "subject_types_supported": []string{ + "public", + }, + "scopes_supported": oauth2_provider.GeneralScopesSupported(), + "claims_supported": []string{ + "aud", + "exp", + "iat", + "iss", + "sub", + "name", + "preferred_username", + "profile", + "picture", + "website", + "locale", + "updated_at", + "email", + "email_verified", + "groups", + }, + "code_challenge_methods_supported": []string{ + "plain", + "S256", + }, + "grant_types_supported": []string{ + "authorization_code", + "refresh_token", + }, + } + ctx.JSON(http.StatusOK, m) +} diff --git a/services/context/base.go b/services/context/base.go index 17b464e1c4..726cc3b496 100644 --- a/services/context/base.go +++ b/services/context/base.go @@ -114,7 +114,7 @@ func (b *Base) HTTPError(status int, contents ...string) { func (b *Base) JSON(status int, content any) { b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8") b.Resp.WriteHeader(status) - if err := json.NewEncoder(b.Resp).Encode(content); err != nil { + if err := json.MarshalWrite(b.Resp, content); err != nil { log.Error("Render JSON failed: %v", err) } } diff --git a/services/context/context_response.go b/services/context/context_response.go index d869c77959..9916f340cb 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -104,20 +104,6 @@ func (ctx *Context) HTML(status int, name templates.TplName) { } } -// JSONTemplate renders the template as JSON response -// keep in mind that the template is processed in HTML context, so JSON things should be handled carefully, e.g.: use JSEscape -func (ctx *Context) JSONTemplate(tmpl templates.TplName) { - t, err := ctx.Render.TemplateLookup(string(tmpl), nil) - if err != nil { - ctx.ServerError("unable to find template", err) - return - } - ctx.Resp.Header().Set("Content-Type", "application/json") - if err = t.Execute(ctx.Resp, ctx.Data); err != nil { - ctx.ServerError("unable to execute template", err) - } -} - // RenderToHTML renders the template content to a HTML string func (ctx *Context) RenderToHTML(name templates.TplName, data any) (template.HTML, error) { var buf strings.Builder diff --git a/services/lfs/server.go b/services/lfs/server.go index 285bc00eb2..9338740136 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -360,7 +360,7 @@ func UploadHandler(ctx *context.Context) { return err } } else { - log.Error("Unable to check if LFS OID[%s] stat. Error: %v", p.Oid, err) + log.Error("Unable to check LFS OID[%s] stat. Error: %v", p.Oid, err) return err } _, err = git_model.NewLFSMetaObject(ctx, repository.ID, p) @@ -378,10 +378,10 @@ func UploadHandler(ctx *context.Context) { } // Do not remove the LFS MetaObject here: this request only creates it after the content is verified and stored, // an invalid request should not remove the existing correct record. - // If two requests are loading (the file is incomplete): + // If two requests are uploading (the file is incomplete): // * one will keep writing the file content - // * one will fail the verification because it reads an incomplete file, the failure should be just ignore - // In the end, the first one will complete the upload and insert a LFS MetaObject record. + // * one will fail the verification because it reads an incomplete file, the failure should be just be ignored + // In the end, the first one will complete the upload and insert a new LFS MetaObject record. return } diff --git a/services/oauth2_provider/access_token.go b/services/oauth2_provider/access_token.go index a577085eae..7dbd90749c 100644 --- a/services/oauth2_provider/access_token.go +++ b/services/oauth2_provider/access_token.go @@ -74,16 +74,18 @@ type AccessTokenResponse struct { IDToken string `json:"id_token,omitempty"` } -// GrantAdditionalScopes returns valid scopes coming from grant -func GrantAdditionalScopes(grantScopes string) auth.AccessTokenScope { - // scopes_supported from templates/user/auth/oidc_wellknown.tmpl - generalScopesSupported := []string{ +func GeneralScopesSupported() []string { + return []string{ "openid", "profile", "email", "groups", } +} +// GrantAdditionalScopes returns valid scopes coming from grant +func GrantAdditionalScopes(grantScopes string) auth.AccessTokenScope { + generalScopesSupported := GeneralScopesSupported() var accessScopes []string // the scopes for access control, but not for general information for scope := range strings.SplitSeq(grantScopes, " ") { if scope != "" && !slices.Contains(generalScopesSupported, scope) { diff --git a/tailwind.config.ts b/tailwind.config.ts index ef1112d3d9..b7e5ae0a10 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -26,7 +26,6 @@ export default { prefix: 'tw-', important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles content: [ - '!./templates/user/auth/oidc_wellknown.tmpl', '!**/*_test.go', './{build,models,modules,routers,services}/**/*.go', './templates/**/*.tmpl', diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl deleted file mode 100644 index 52c9a1b788..0000000000 --- a/templates/user/auth/oidc_wellknown.tmpl +++ /dev/null @@ -1,49 +0,0 @@ -{ - "issuer": "{{.OidcIssuer}}", - "authorization_endpoint": "{{.OidcBaseUrl}}/login/oauth/authorize", - "token_endpoint": "{{.OidcBaseUrl}}/login/oauth/access_token", - "jwks_uri": "{{.OidcBaseUrl}}/login/oauth/keys", - "userinfo_endpoint": "{{.OidcBaseUrl}}/login/oauth/userinfo", - "introspection_endpoint": "{{.OidcBaseUrl}}/login/oauth/introspect", - "response_types_supported": [ - "code", - "id_token" - ], - "id_token_signing_alg_values_supported": [ - "{{.SigningKeyMethodAlg}}" - ], - "subject_types_supported": [ - "public" - ], - "scopes_supported": [ - "openid", - "profile", - "email", - "groups" - ], - "claims_supported": [ - "aud", - "exp", - "iat", - "iss", - "sub", - "name", - "preferred_username", - "profile", - "picture", - "website", - "locale", - "updated_at", - "email", - "email_verified", - "groups" - ], - "code_challenge_methods_supported": [ - "plain", - "S256" - ], - "grant_types_supported": [ - "authorization_code", - "refresh_token" - ] -}