4.1 KiB
4.1 KiB
使用 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 中注册应用
- 在目标 Gitea 上创建 OAuth2 应用 / 应用(OAuth2),填写 Redirect URI(你自有服务的回调,须 HTTPS 生产或本地开发约定)。
- 记录 Client ID、Client Secret。
注意:redirect_uri 在授权与换 token 两阶段须一致,且与 Gitea 中登记项一致。
3. Golang:oauth2.Endpoint 与示例
golang.org/x/oauth2 的 Config.Endpoint 需手动设为:
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. 最小示例片段
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
}
// 示例:构造 Config(BASE、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 中示例同构,仅将 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 作为绑定主键更稳妥。