快速开始
在应用后端挂载 token 路由。路由验证已登录用户,向 Nuva 获取访问 token,并返回给前端 SDK。
issuer, err := nuvaauth.NewClient(nuvaauth.ClientConfig{
BaseURL: apiBaseURL,
ServiceToken: os.Getenv("NUVA_SERVICE_TOKEN"),
})
if err != nil { return err }
handler, err := nuvaauth.NewAuthHandler(issuer, resolveNuvaGrant)
if err != nil { return err }
mux.Handle("/api/nuva-token", handler)将 apiBaseURL 设为接入配置中的 Nuva 服务地址。nuvaauth 为 Go SDK 包别名。通过环境变量或服务端密钥管理配置 NUVA_SERVICE_TOKEN。
/api/nuva-token 是应用后端的路由路径,可按项目约定调整。前端 getAccessToken 回调应请求相同路径。
服务配置
| 字段 | 类型 | 要求 |
|---|---|---|
BaseURL | string | Nuva 授权服务地址,来自接入配置。 |
ServiceToken | string | 客户级 Nuva 服务凭证,仅保存在后端。 |
issuer | TokenIssuer | 由 NewClient 创建的授权服务客户端。 |
resolve | ResolveGrant | 从请求中验证登录身份,并返回用户授权信息。 |
NewAuthHandler 要求 issuer 和 resolve 均非 nil。初始化失败时返回 error。使用 cookie 登录的应用应为此路由启用现有 CSRF 校验。
登录适配
实现 ResolveGrant。从已验证的登录会话中读取用户 ID,从服务端授权策略中读取 UI skills 和权限。
func resolveNuvaGrant(r *http.Request) (nuvaauth.ClientTokenRequest, error) {
user, err := appauth.CurrentUser(r)
if errors.Is(err, appauth.ErrNoSession) {
return nuvaauth.ClientTokenRequest{}, nuvaauth.ErrUnauthenticated
}
if err != nil { return nuvaauth.ClientTokenRequest{}, err }
grant, err := accessPolicy.NuvaAccess(r.Context(), user.ID)
if errors.Is(err, accessPolicy.ErrDenied) {
return nuvaauth.ClientTokenRequest{}, nuvaauth.ErrForbidden
}
if err != nil { return nuvaauth.ClientTokenRequest{}, err }
return nuvaauth.ClientTokenRequest{
UserID: user.ID,
UISkills: grant.UISkills,
Permissions: grant.Permissions,
}, nil
}将 appauth 和 accessPolicy 替换为项目的登录与授权模块。不要从浏览器请求体直接读取有效用户身份或权限。
| 返回错误 | HTTP 状态 |
|---|---|
ErrUnauthenticated | 401 |
ErrForbidden | 403 |
| 其他 error | 500 |
Token 申请参数
type ClientTokenRequest struct {
UserID string `json:"userId"`
UISkills []DefinitionRef `json:"uiSkills"`
Permissions json.RawMessage `json:"permissions"`
}
type DefinitionRef struct {
ID string `json:"id"`
Revision string `json:"revision"`
}| 字段 | 说明 |
|---|---|
UserID | 客户内的用户唯一标识。必须来自已验证的登录会话,且不能为空。 |
UISkills | 允许使用的 UI skill ID 和版本。空列表或 nil 仅使用平台默认工具。 |
Permissions | 服务端授权配置的 JSON。使用接入配置指定的格式,不能为空或 null。 |
DefinitionRef.ID | 已注册的 UI skill ID。 |
DefinitionRef.Revision | 该 UI skill 的版本。 |
Nuva 校验申请范围后签发用户 token。浏览器使用返回的 token 连接前端 SDK。
HTTP 响应
使用 POST /api/nuva-token,携带应用的登录 cookie 或用户凭证。该路由不需要 JSON 请求体。
成功响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
{
"token": "<Nuva user access token>",
"expiresAt": "2026-10-12T18:15:00Z"
}| 字段 | 类型 | 说明 |
|---|---|---|
token | string | Nuva 用户访问 token。 |
expiresAt | string | RFC 3339 格式的到期时间。 |
错误响应
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
{
"error": { "code": "UNAUTHENTICATED" }
}| HTTP 状态 | error.code | 含义 |
|---|---|---|
| 401 | UNAUTHENTICATED | 未登录或登录已失效。 |
| 403 | FORBIDDEN | 应用策略或 Nuva 拒绝授权。 |
| 405 | METHOD_NOT_ALLOWED | 使用了 POST 以外的方法。 |
| 500 | AUTH_CONFIGURATION_ERROR | 登录适配器出错,或授权数据无效。 |
| 502 | NUVA_AUTH_UNAVAILABLE | Nuva 授权请求失败,或返回的 token 为空、已过期。 |
响应包含 Cache-Control: no-store。前端应保留失败状态,并按应用的登录和错误处理流程处理。
API 参考
type ClientConfig struct {
BaseURL string
ServiceToken string
}
func NewClient(ClientConfig) (TokenIssuer, error)
func NewAuthHandler(TokenIssuer, ResolveGrant) (http.Handler, error)
type TokenIssuer interface {
CreateClientToken(context.Context, ClientTokenRequest) (ClientToken, error)
}
type ResolveGrant func(*http.Request) (ClientTokenRequest, error)
type ClientToken struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"`
}| 方法 | 返回值 |
|---|---|
NewClient(config) | TokenIssuer;初始化失败时返回 error。 |
CreateClientToken(ctx, request) | ClientToken,包含 Token 和 ExpiresAt;授权失败时返回 error。 |
NewAuthHandler(issuer, resolve) | http.Handler;缺少依赖时返回 error。 |