Update SproutGate

This commit is contained in:
2026-05-13 12:19:36 +08:00
parent f7db8aa053
commit a37b92e144
51 changed files with 12034 additions and 254 deletions

View File

@@ -0,0 +1,123 @@
# 使用 Gitea 登录OAuth 2.0 接入指南Golang
**Gitea** 实例提供与平台绑定的 OAuth2 端点:授权地址、令牌地址都带**你的 Gitea 根 URL** 前缀。本文说明通用注册方式与 Go 中的 `oauth2.Endpoint` 拼法,与具体业务项目无关。
## 1. 协议与端点
对 Gitea 根地址 `BASE`(无尾斜杠,如 `https://git.example.com`
| 步骤 | URL |
|------|-----|
| 授权 | `{BASE}/login/oauth/authorize` |
| 换 token | `{BASE}/login/oauth/access_token` |
| 当前用户 API | `{BASE}/api/v1/user`Header`Authorization: token {access_token}` |
Scope 常用:`read:user`(以你 Gitea 版本界面为准)。
官方概念说明见 Gitea 文档中的 OAuth2 / Applications 章节(不同版本路径可能为站点管理或用户 **Applications**)。
## 2. 在 Gitea 中注册应用
1. 在目标 Gitea 上创建 **OAuth2 应用** / **应用OAuth2**,填写 **Redirect URI**(你自有服务的回调,须 HTTPS 生产或本地开发约定)。
2. 记录 **Client ID**、**Client Secret**。
**注意**`redirect_uri` 在授权与换 token 两阶段须一致,且与 Gitea 中登记项一致。
## 3. Golang`oauth2.Endpoint` 与示例
`golang.org/x/oauth2``Config.Endpoint` 需手动设为:
```go
endpoint := oauth2.Endpoint{
AuthURL: base + "/login/oauth/authorize",
TokenURL: base + "/login/oauth/access_token",
}
```
调用 Gitea `GET /api/v1/user` 时,使用 **token** 头:`Authorization: token ` + `accessToken`Gitea 传统写法;若你的版本支持 Bearer 以实际文档为准)。
## 4. 最小示例片段
```go
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"golang.org/x/oauth2"
)
func giteaEndpoint(base string) oauth2.Endpoint {
base = trimRightSlash(base)
return oauth2.Endpoint{
AuthURL: base + "/login/oauth/authorize",
TokenURL: base + "/login/oauth/access_token",
}
}
func trimRightSlash(s string) string {
for len(s) > 0 && s[len(s)-1] == '/' {
s = s[:len(s)-1]
}
return s
}
// 示例:构造 ConfigBASE、ClientID、Secret、RedirectURL 从环境或配置读取)
func newGiteaConfig() *oauth2.Config {
base := os.Getenv("GITEA_BASE_URL") // 如 https://git.example.com
return &oauth2.Config{
ClientID: os.Getenv("GITEA_CLIENT_ID"),
ClientSecret: os.Getenv("GITEA_CLIENT_SECRET"),
RedirectURL: os.Getenv("GITEA_REDIRECT_URL"),
Scopes: []string{"read:user"},
Endpoint: giteaEndpoint(base),
}
}
type giteaUser struct {
ID int64 `json:"id"`
Login string `json:"login"`
FullName string `json:"full_name"`
Email string `json:"email"`
AvatarURL string `json:"avatar_url"`
}
func fetchGiteaUser(ctx context.Context, base, access string) (*giteaUser, error) {
base = trimRightSlash(base)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, base+"/api/v1/user", nil)
req.Header.Set("Authorization", "token "+access)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
b, _ := io.ReadAll(res.Body)
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("gitea: %d %s", res.StatusCode, b)
}
var u giteaUser
if err := json.Unmarshal(b, &u); err != nil {
return nil, err
}
return &u, nil
}
```
`main` 中挂载 HTTP 路由:首页生成 `state` 并重定向到 `cfg.AuthCodeURL`;在 `/oauth/gitea/callback``cfg.Exchange` 后调用 `fetchGiteaUser` 即可。整体与 [github.md](github.md) 中示例同构,仅将 `Config.Endpoint` 换为 `giteaEndpoint`、将 GitHub `Bearer` 换为 Gitea 的 `token` 头。
## 5. 常见错误
- **401**token 头格式或 API 版本与实例不一致。
- **redirect_uri 不匹配**:授权与回调的 host/scheme/端口与 Gitea 中登记的不一致。
- 多 Gitea 实例时:每个 **Client****base URL** 一一对应,勿混用。
## 6. 安全建议
- Secret 只放服务端。
- 以 Gitea 返回的**数字 user id** 作为绑定主键更稳妥。