Skip to content

Go 集成教程

本教程介绍如何使用 Go 语言通过 Authorization Code Flow 集成 Code Bird Cloud 进行用户认证。

前置条件

  • Go 1.21 或更高版本
  • 已在 Code Bird Cloud 管理后台创建 Traditional 类型的应用,并获取 Client ID 和 Client Secret
  • 已配置 Redirect URI 为 http://localhost:8080/callback

安装依赖

bash
go get github.com/coreos/go-oidc/v3/oidc
go get golang.org/x/oauth2
依赖包说明
github.com/coreos/go-oidc/v3/oidcGo 生态中最流行的 OIDC 库,支持自动发现、令牌验证
golang.org/x/oauth2Go 官方 OAuth2 库,配合 go-oidc 使用

完整代码示例

go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/coreos/go-oidc/v3/oidc"
	"golang.org/x/oauth2"
)

var (
	oidcProvider *oidc.Provider
	oauth2Config oauth2.Config
	verifier     *oidc.IDTokenVerifier
)

func main() {
	ctx := context.Background()

	var err error
	oidcProvider, err = oidc.NewProvider(ctx, "https://your-domain.com")
	if err != nil {
		log.Fatalf("Failed to initialize OIDC provider: %v", err)
	}

	oauth2Config = oauth2.Config{
		ClientID:     os.Getenv("CLIENT_ID"),
		ClientSecret: os.Getenv("CLIENT_SECRET"),
		RedirectURL:  "http://localhost:8080/callback",
		Endpoint:     oidcProvider.Endpoint(),
		// 按需添加 scope:"phone" 获取手机号;"urn:codebird:scope:organizations" 获取组织列表;
		// "urn:codebird:scope:organization_roles" 获取组织角色
		Scopes: []string{oidc.ScopeOpenID, "profile", "email", "offline_access"},
	}

	verifier = oidcProvider.Verifier(&oidc.Config{ClientID: oauth2Config.ClientID})

	http.HandleFunc("/login", handleLogin)
	http.HandleFunc("/callback", handleCallback)

	log.Println("Server running at http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
	state := "random-state-string" // Use crypto/rand in production
	http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
}

func handleCallback(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	code := r.URL.Query().Get("code")

	token, err := oauth2Config.Exchange(ctx, code)
	if err != nil {
		http.Error(w, fmt.Sprintf("Token exchange failed: %v", err), http.StatusInternalServerError)
		return
	}

	rawIDToken, ok := token.Extra("id_token").(string)
	if !ok {
		http.Error(w, "Missing id_token", http.StatusInternalServerError)
		return
	}

	idToken, err := verifier.Verify(ctx, rawIDToken)
	if err != nil {
		http.Error(w, fmt.Sprintf("ID token verification failed: %v", err), http.StatusInternalServerError)
		return
	}

	var claims struct {
		Sub   string `json:"sub"`
		Name  string `json:"name"`
		Email string `json:"email"`
	}
	if err := idToken.Claims(&claims); err != nil {
		http.Error(w, fmt.Sprintf("Failed to parse claims: %v", err), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]any{
		"subject":       claims.Sub,
		"name":          claims.Name,
		"email":         claims.Email,
		"access_token":  token.AccessToken,
		"refresh_token": token.RefreshToken,
	})
}

代码说明

OIDC Provider 初始化

oidc.NewProvider() 会自动调用 Code Bird Cloud 的 /.well-known/openid-configuration 端点,获取授权端点、令牌端点等配置信息。oidcProvider.Endpoint() 将发现的端点传递给 oauth2.Config

ID Token 验证器

oidcProvider.Verifier() 创建一个 ID Token 验证器,用于验证令牌签名(ES256)、受众(aud 包含 client_id)以及过期时间。

登录处理 handleLogin

生成 state 参数并重定向用户到 Code Bird Cloud 的授权端点。

生产环境提醒: 示例中 state 使用了固定字符串,生产环境中应使用 crypto/rand 生成密码学安全的随机值,并将其存储到 Session 或 Cookie 中,在回调时进行验证。

回调处理 handleCallback

  1. 从回调 URL 中提取授权码 code
  2. 调用 oauth2Config.Exchange() 用授权码换取令牌
  3. 从令牌响应中提取 id_token 并使用验证器验证
  4. 解析 ID Token 中的 Claims 获取用户信息

运行

bash
# 设置环境变量
export CLIENT_ID="your_client_id"
export CLIENT_SECRET="your_client_secret"

# 运行服务
go run main.go

访问 http://localhost:8080/login 开始登录流程。

相关文档

Released under the MIT License.