Skip to content

React SPA + Go 后端 SDK 配套接入

本文档给出当前最推荐的完整接法:

  • 前端:@codebird/react
  • 后端:codebird-go-sdk

这是第三方系统最常见的组合,也是目前最稳定、最容易排查问题的架构。

目标

按本文接入后,完整链路应该是:

  1. React SPA 发起登录
  2. callback 页面完成授权码交换
  3. React SDK 负责本地登录态恢复与 Token 自动续期
  4. React 前端向 Go 后端请求业务 API
  5. Go 后端验证 Access Token,并基于 AuthContext 做鉴权
  6. 需要最新数据库状态时,前后端调用实时会话上下文

推荐架构

text
React SPA
  -> CodeBirdProvider
  -> CodeBirdAuthGuard
  -> auth.signIn()
  -> callback
  -> auth.getAccessToken()
  -> Authorization: Bearer <token>
  -> Go API
  -> ParseBearerToken()
  -> VerifyAccessToken()
  -> AuthContext

第 1 步:前端初始化 React SDK

推荐从业务后端运行时接口读取认证配置。

tsx
import { useEffect, useState } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { CodeBirdProvider } from '@codebird/react';
import App from './App';

type RuntimeAuthConfig = {
  issuer: string;
  appId: string;
  redirectUri: string;
  postLogoutRedirectUri: string;
  resource: string;
  organizationId?: string;
  configVersion: string;
  scopes: string[];
};

export default function AuthBootstrap() {
  const [config, setConfig] = useState<RuntimeAuthConfig | null>(null);

  useEffect(() => {
    void fetch('/api/v1/admin/app/init')
      .then((response) => response.json())
      .then((payload) => setConfig(payload.result));
  }, []);

  if (!config) {
    return <div>Loading...</div>;
  }

  return (
    <CodeBirdProvider
      endpoint={config.issuer}
      appId={config.appId}
      redirectUri={config.redirectUri}
      postLogoutRedirectUri={config.postLogoutRedirectUri}
      defaultResource={config.resource}
      defaultOrganizationId={config.organizationId}
      configVersion={config.configVersion}
      scopes={config.scopes}
      automaticSilentRenew={false}
    >
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </CodeBirdProvider>
  );
}

第 2 步:前端配置 callback 和官方守卫

tsx
import { Routes, Route } from 'react-router-dom';
import { CodeBirdAuthGuard, CodeBirdCallback, useCodeBirdAuth } from '@codebird/react';

function ProtectedApp() {
  const auth = useCodeBirdAuth();

  return (
    <CodeBirdAuthGuard
      loadingFallback={<div>Loading...</div>}
      unauthenticatedFallback={<div>Redirecting...</div>}
      onUnauthenticated={() => auth.signIn()}
    >
      <Routes>
        <Route path="/" element={<DashboardPage />} />
      </Routes>
    </CodeBirdAuthGuard>
  );
}

export default function App() {
  return (
    <Routes>
      <Route path="/callback" element={<CodeBirdCallback />} />
      <Route path="/*" element={<ProtectedApp />} />
    </Routes>
  );
}

这个模式的好处是:

  • callback、恢复登录态、未认证跳登录统一收口在 SDK
  • refresh token 失效后,Guard 会稳定进入未认证逻辑
  • 第三方不用自己维护复杂的认证守卫时序

第 3 步:前端请求业务 API

tsx
import { useCodeBirdAuth } from '@codebird/react';

export default function DashboardPage() {
  const auth = useCodeBirdAuth();

  async function loadProfile() {
    const token = await auth.getAccessToken();

    const response = await fetch('/api/me', {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    const result = await response.json();
    console.log(result);
  }

  return <button onClick={() => void loadProfile()}>获取当前用户</button>;
}

当前推荐原因

因为 React SDK 已经统一处理:

  • token 是否存在
  • token 的 aud 是否匹配 defaultResource
  • token 是否需要续期
  • refresh token 失效后的本地状态清理

业务代码不要再自己做一层。

第 4 步:Go 后端验证 Token

go
package main

import (
    "encoding/json"
    "log"
    "net/http"

    codebird "github.com/lshaofan/codebird-go-sdk"
)

func main() {
    verifier, err := codebird.NewVerifier(codebird.Config{
        Issuer:   "https://auth.codebird.cloud",
        Audience: "https://api.example.com",
    })
    if err != nil {
        log.Fatalf("failed to create verifier: %v", err)
    }

    http.HandleFunc("/api/me", func(w http.ResponseWriter, r *http.Request) {
        token, err := codebird.ParseBearerToken(r.Header.Get("Authorization"))
        if err != nil {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }

        authCtx, err := verifier.VerifyAccessToken(r.Context(), token)
        if err != nil {
            http.Error(w, "unauthorized", http.StatusUnauthorized)
            return
        }

        _ = json.NewEncoder(w).Encode(map[string]any{
            "user_id":               authCtx.Subject,
            "username":              authCtx.Username,
            "organization_id":       authCtx.OrganizationID,
            "organization_roles":    authCtx.OrganizationRoles,
            "organization_is_admin": authCtx.OrganizationIsAdmin,
        })
    })

    log.Fatal(http.ListenAndServe(":8081", nil))
}

第 5 步:前后端必须对齐的配置

概念前端后端
认证中心地址endpointIssuer
业务 API 资源标识defaultResourceAudience
组织上下文defaultOrganizationIdsignIn({ organizationId })从 token / session context 读取
实时租户标识tenant.slug同样读取 tenant.slug

最关键的一条:

text
React defaultResource == Go Audience

如果不一致,典型表现是:

  • 前端拿到的 token 无法调用后端
  • 后端验签通过但 audience 校验失败
  • 前端出现重复 refresh 或错误的 token 使用

第 6 步:实时上下文的分工

只需要轻量 claims 时

前端或后端可以直接用 Access Token / AuthContext

  • 当前用户是谁
  • 当前组织是谁
  • 当前组织角色有哪些

需要数据库最新状态时

请使用实时上下文:

适用场景:

  • 组织成员关系可能实时变化
  • 页面需要最新组织列表
  • 需要最新 organization.is_admin
  • 需要 tenant.slug 拼接租户化入口

第 7 步:refresh token 过期时应该怎样工作

当前推荐行为是:

  1. React SDK 发现 Access Token 需要续期
  2. 如果 Refresh Token 仍有效,自动刷新
  3. 如果 Refresh Token 已过期,SDK 清理本地登录态
  4. CodeBirdAuthGuard 进入未认证分支
  5. 第三方自动重新调用 auth.signIn()

正确的用户体验应该是:

  • 用户重新进入登录流程
  • 而不是停留在一条技术错误信息上

如果你们的页面直接显示错误消息,说明业务项目没有正确处理未认证态。

第 8 步:推荐的业务判断方式

Go 后端拿到 AuthContext 后,推荐直接用 helper:

go
if !authCtx.HasOrganization("org_123") {
    http.Error(w, "forbidden", http.StatusForbidden)
    return
}

if !authCtx.HasOrganizationRole("org_123", "admin") {
    http.Error(w, "forbidden", http.StatusForbidden)
    return
}

不要在业务代码里自己手拆原始 claims。

当前最常见错误

错误 1:前端自己控制 token 刷新

不推荐。

请让 React SDK 统一处理。

错误 2:前端没用官方 Guard

表现通常是:

  • 刷新 token 过期后页面停在错误消息
  • 本地状态清理后没有重新发起登录

错误 3:后端只看旧 claims,不看实时上下文

当组织关系或管理员状态发生变化时,容易出现权限判断滞后。

联调清单

上线前至少做这几组测试:

场景 A:Access Token 过期,Refresh Token 仍有效

预期:

  • 前端无感续期
  • 业务页面不跳登录

场景 B:Refresh Token 过期

预期:

  • SDK 清理本地登录态
  • Guard 自动重新发起登录

场景 C:组织管理员状态变化

预期:

  • claims 可能还是旧值
  • 实时会话上下文能反映最新数据库状态

相关文档

Released under the MIT License.