Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions BRANCH_ORGANIZATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# OAuth2 库分支组织

## 📋 分支说明

这个项目使用功能分支来组织不同的 OAuth2 提供者实现,以保持代码整洁和功能分离。

## 🌳 分支结构

### 主分支
- **`master`** - 稳定的主分支,包含经过测试的功能
- 包含: 基础 OAuth2 框架和成熟的提供者(GitHub、Google 等)

### 功能分支

#### 🔐 Supabase 分支
- **分支名**: `cursor/implement-supabase-authentication-86da`
- **功能**: Supabase OAuth2 提供者
- **状态**: ✅ 完成
- **包含**:
- `supabase/supabase.go` - Supabase OAuth2 实现
- `supabase/README.md` - 详细文档
- `example/supabase/` - 完整示例应用

#### 🔑 WebAuthn 分支
- **分支名**: `feature/webauthn-support`
- **功能**: WebAuthn 无密码认证
- **状态**: ✅ 完成
- **包含**:
- `webauthn/webauthn.go` - WebAuthn OAuth2 适配器
- `webauthn/user.go` - 用户和会话管理
- `webauthn/README.md` - 详细文档和使用指南
- `example/webauthn/` - 完整示例应用(中文界面)
- `webauthn/webauthn_test.go` - 单元测试

## 🚀 特性对比

| 功能 | Supabase 分支 | WebAuthn 分支 |
|-----|-------------|-------------|
| 认证方式 | 传统 OAuth2 | 无密码认证 |
| 安全性 | 标准 OAuth2 | 生物识别 + 硬件密钥 |
| 用户体验 | 传统登录流程 | 现代无密码体验 |
| 浏览器要求 | 所有现代浏览器 | Chrome 67+, Firefox 60+, Safari 14+ |
| 硬件要求 | 无特殊要求 | 需要支持 WebAuthn 的设备 |

## 📦 如何使用

### 切换到 Supabase 分支
```bash
git checkout cursor/implement-supabase-authentication-86da
cd example/supabase
go run main.go
```

### 切换到 WebAuthn 分支
```bash
git checkout feature/webauthn-support
cd example/webauthn
go run main.go
```

## 🔄 分支合并策略

1. **功能分支 → master**: 当功能稳定且经过充分测试后
2. **独立开发**: 各功能分支独立开发,避免冲突
3. **向前兼容**: 新功能不影响现有功能

## 📝 开发建议

### Supabase 分支开发
- 专注于 Supabase 特定功能和改进
- 保持与 Supabase API 的同步
- 增强错误处理和用户体验

### WebAuthn 分支开发
- 关注无密码认证的安全性和兼容性
- 扩展支持更多浏览器和设备
- 优化用户体验和错误处理

## 🛡️ 安全考虑

### Supabase 分支
- ✅ HTTPS 要求
- ✅ 标准 OAuth2 安全流程
- ✅ 令牌安全存储

### WebAuthn 分支
- ✅ 生物识别验证
- ✅ 硬件密钥支持
- ✅ 防钓鱼攻击
- ✅ 域名绑定验证

## 📊 测试状态

| 分支 | 编译状态 | 测试状态 | 示例运行 |
|-----|---------|---------|---------|
| Supabase | ✅ 通过 | ⚠️ 需要测试 | ✅ 正常 |
| WebAuthn | ✅ 通过 | ✅ 全部通过 | ✅ 正常 |

---

**💡 提示**: 这种分支组织确保了功能的独立性,便于维护和合并,同时避免了不同功能之间的冲突。
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,43 @@ To install the package, run:
go get github.com/go-zoox/oauth2
```

## Supported Providers

This library supports many OAuth2 providers, including:

- **Supabase** - Full-featured authentication platform
- **GitHub** - Version control and collaboration
- **Google** - Google services authentication
- **Auth0** - Identity and access management
- **Microsoft Azure** - Microsoft cloud services
- **Slack** - Team communication platform
- **Discord** - Gaming and community communication
- **Facebook** - Social networking
- **GitLab** - DevOps platform
- **Twitter** - Social media platform
- **WeChat** - Chinese messaging platform
- **Doreamon** - Custom authentication provider
- And many more...

### Supabase Provider

The Supabase provider offers seamless integration with Supabase Auth:

```go
import "github.com/go-zoox/oauth2/supabase"

// Create Supabase client
client, err := supabase.New(&supabase.SupabaseConfig{
BaseURL: "https://your-project.supabase.co",
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
RedirectURI: "http://localhost:8080/auth/callback",
Scope: "openid email profile",
})
```

For detailed Supabase setup instructions, see the [Supabase provider documentation](supabase/README.md).

## Getting Started

### Example 1: Using only one oauth2 provider => doreamon
Expand Down
16 changes: 16 additions & 0 deletions example/supabase/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Supabase OAuth2 Configuration
# Copy this file to .env and fill in your actual values

# Your Supabase project URL (found in your Supabase dashboard)
SUPABASE_BASE_URL=https://your-project-id.supabase.co

# OAuth2 credentials from your Supabase project
# These can be found in: Authentication > Settings > OAuth2
SUPABASE_CLIENT_ID=your-client-id
SUPABASE_CLIENT_SECRET=your-client-secret

# Callback URL - make sure this matches what you set in Supabase dashboard
SUPABASE_REDIRECT_URI=http://localhost:8080/auth/callback

# Optional: Port for the example server (defaults to 8080)
PORT=8080
163 changes: 163 additions & 0 deletions example/supabase/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package main

import (
"fmt"
"log"
"net/http"
"os"

"github.com/go-zoox/oauth2"
"github.com/go-zoox/oauth2/supabase"
)

func main() {
// Environment variables needed:
// SUPABASE_BASE_URL=https://your-project.supabase.co
// SUPABASE_CLIENT_ID=your-client-id
// SUPABASE_CLIENT_SECRET=your-client-secret
// SUPABASE_REDIRECT_URI=http://localhost:8080/auth/callback

baseURL := os.Getenv("SUPABASE_BASE_URL")
clientID := os.Getenv("SUPABASE_CLIENT_ID")
clientSecret := os.Getenv("SUPABASE_CLIENT_SECRET")
redirectURI := os.Getenv("SUPABASE_REDIRECT_URI")

if baseURL == "" || clientID == "" || clientSecret == "" || redirectURI == "" {
log.Fatal("Missing required environment variables: SUPABASE_BASE_URL, SUPABASE_CLIENT_ID, SUPABASE_CLIENT_SECRET, SUPABASE_REDIRECT_URI")
}

// Create Supabase OAuth2 client
client, err := supabase.New(&supabase.SupabaseConfig{
BaseURL: baseURL,
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURI: redirectURI,
Scope: "openid email profile",
})
if err != nil {
log.Fatal("Failed to create Supabase client:", err)
}

// Home page
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
html := `
<!DOCTYPE html>
<html>
<head>
<title>Supabase OAuth2 Example</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
.button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
text-decoration: none;
border: none;
border-radius: 4px;
cursor: pointer;
display: inline-block;
margin: 10px 0;
}
.button:hover { background-color: #45a049; }
</style>
</head>
<body>
<h1>Supabase OAuth2 Example</h1>
<p>This is a simple example of using Supabase OAuth2 authentication.</p>
<a href="/login" class="button">Login with Supabase</a>
<a href="/logout" class="button">Logout</a>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(html))
})

// Login endpoint
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
client.Authorize("oauth2-state", func(loginURL string) {
http.Redirect(w, r, loginURL, http.StatusFound)
})
})

// Logout endpoint
http.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
client.Logout(func(logoutURL string) {
if logoutURL == "" {
// If no logout URL provided, redirect to home
http.Redirect(w, r, "/", http.StatusFound)
} else {
http.Redirect(w, r, logoutURL, http.StatusFound)
}
})
})

// OAuth callback endpoint
http.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
state := r.URL.Query().Get("state")

client.Callback(code, state, func(user *oauth2.User, token *oauth2.Token, err error) {
if err != nil {
log.Printf("OAuth callback error: %v", err)
http.Error(w, "Authentication failed: "+err.Error(), http.StatusInternalServerError)
return
}

// Successful authentication
log.Printf("User authenticated: %+v", user)
log.Printf("Token: %+v", token)

// Create a simple success page
html := `
<!DOCTYPE html>
<html>
<head>
<title>Authentication Success</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
.success { background-color: #d4edda; color: #155724; padding: 20px; border-radius: 4px; margin: 20px 0; }
.user-info { background-color: #f8f9fa; padding: 20px; border-radius: 4px; margin: 20px 0; }
.button {
background-color: #007bff;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 4px;
display: inline-block;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>Authentication Success!</h1>
<div class="success">
<h3>Welcome, you have successfully authenticated with Supabase!</h3>
</div>
<div class="user-info">
<h4>User Information:</h4>
<p><strong>ID:</strong> %s</p>
<p><strong>Email:</strong> %s</p>
<p><strong>Username:</strong> %s</p>
<p><strong>Nickname:</strong> %s</p>
</div>
<a href="/" class="button">Go Home</a>
</body>
</html>
`
response := fmt.Sprintf(html, user.ID, user.Email, user.Username, user.Nickname)
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(response))
})
})

// Start the server
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

log.Printf("Starting server on port %s", port)
log.Printf("Visit http://localhost:%s to test the Supabase OAuth2 integration", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
Binary file added example/supabase/supabase-example
Binary file not shown.
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/go-zoox/oauth2

go 1.19
go 1.23.0

toolchain go1.24.2

require (
github.com/go-zoox/cookie v1.2.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/go-zoox/chalk v1.0.2 h1:DCWft37fogmvqF37JdbGSLg28L/tQeA8u0lMvb62KOg=
github.com/go-zoox/chalk v1.0.2/go.mod h1:z5+qvE9nEJI5uT4px2tyoFa/xxkqf3CUo22KmXLKbNI=
github.com/go-zoox/cookie v1.2.0 h1:MO33lPQ/QGJIAEzgrsAfEpJc25lcJ/XR0w+smM19sNQ=
Expand All @@ -14,13 +15,18 @@ github.com/go-zoox/headers v1.0.8/go.mod h1:WEgEbewswEw4n4qS1iG68Kn/vOQVCAKGwwuZ
github.com/go-zoox/logger v1.4.6 h1:zHUaB6KQ9rD/N3hM0JJ3/JCNdgtedf4mVBBNNSyWCOg=
github.com/go-zoox/logger v1.4.6/go.mod h1:o7ddvv/gMoMa0TomPhHoIz11ZWRbQ92pF6rwYbOY3iQ=
github.com/go-zoox/testify v1.0.2 h1:G5sQ3xm0uwCuytnMhgnqZ5BItCt2DN3n2wLBqlIJEWA=
github.com/go-zoox/testify v1.0.2/go.mod h1:L35iVL6xDKDL/TQOTRWyNL4H4nm8bzs6nde5XA7PYnY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
Expand All @@ -31,6 +37,7 @@ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
Loading