Update SproutGate
This commit is contained in:
123
docs/oauth-providers/gitea.md
Normal file
123
docs/oauth-providers/gitea.md
Normal 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
|
||||
}
|
||||
|
||||
// 示例:构造 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](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** 作为绑定主键更稳妥。
|
||||
Reference in New Issue
Block a user