ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpacetimeDB 内置身份认证服务 SpacetimeAuth 完全指南:OIDC 接入、项目配置与客户端集成

SpacetimeDB 内置身份认证服务 SpacetimeAuth 完全指南:OIDC 接入、项目配置与客户端集成 SpacetimeDB 内置身份认证服务 SpacetimeAuth 完全指南OIDC 接入、项目配置与客户端集成【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDBSpacetimeAuth 是 SpacetimeDB 官方提供的身份认证Authentication托管服务为发布到 Maincloud 的模块提供开箱即用的用户认证能力使开发者无需自行搭建外部认证服务或托管服务器即可完成用户登录与授权。本文基于仓库中 SpacetimeAuth 概述文档 及其配套的 创建项目、配置项目、测试、React 集成 与 Steam 会话票据认证 系列文档展开。读完本文你将掌握 SpacetimeAuth 的核心概念Projects、Users、Clients、Roles、项目创建与 Dashboard 配置流程、OIDC 标准端点与 Scope/Claim 体系、无代码验证方法以及如何在 React 前端和服务端 Reducer 中接入并利用 ID Token 实现身份识别与基于角色的访问控制RBAC。注意Beta 阶段声明SpacetimeAuth 目前处于 Beta 阶段部分功能可能尚未开放或将在未来发生变化使用过程中可能遇到 Bug。官方文档建议将遇到的问题反馈给团队以帮助改进服务。SpacetimeAuth 是什么面向 SpacetimeDB 的 OIDC 身份提供商SpacetimeAuth 是一个用于管理 SpacetimeDB 应用认证的服务其核心价值在于你可以在没有外部认证服务、甚至没有托管服务器的情况下完成用户认证。它本身是一个 OpenID Connect (OIDC) 提供商因此可以被任何兼容 OIDC 的客户端库直接使用。认证流程的终点是你的应用获得一个包含身份声明identity claims的ID Token其中携带 email、username、roles 等信息。随后你的应用可以使用该 Token 配合任意 SpacetimeDB SDK 与 SpacetimeDB 服务器完成认证和授权。主要特性认证方式Authentication methodsMagic link魔法链接SteamSession Ticket会话票据GitHubGoogleDiscordTwitchKick用户与角色管理User role management创建、更新、管理用户为用户分配角色以实施基于角色的访问控制RBAC。自定义Customization可自定义登录页主题可启用/禁用匿名登录与魔法链接认证。核心术语Terminology理解 SpacetimeAuth 的四个核心概念是后续配置的前提。官方将这一套概念与 OpenID Connect 的标准术语一一对应。Projects项目SpacetimeAuth 使用项目project来为不同应用管理认证。要点如下每个项目拥有独立的用户、角色和认证方式集合。每个项目拥有独立的配置邮件模板、网页、其他设置。每个项目独立于某个 SpacetimeDB 数据库可以被一个或多个数据库使用。典型的项目规划场景场景推荐做法Web 应用与移动应用共用同一数据库为两者创建一个SpacetimeAuth 项目共享单一用户群同一应用的多环境数据库dev / staging / production为每个环境创建独立项目隔离不同环境的用户多个应用对应多个数据库为每个应用创建独立项目隔离不同应用的用户Users用户用户是通过 SpacetimeAuth 向应用认证的个体。每个用户拥有唯一标识符user ID可被分配一个或多个角色。Clients客户端 / 依赖方注意Clients 不能与 Users 混淆。Clients 在 OpenID Connect 术语中也称为Relying Parties依赖方即依赖 SpacetimeAuth 完成认证的应用。每个 Client 与单一项目关联拥有自己的 client ID 和 client secret。Clients 是向 SpacetimeAuth 请求 OpenID Connect ID Token 的应用拿到 Token 后可用来向 SpacetimeDB 服务器认证。Roles角色角色用于管理应用内部的访问控制。每个角色是一个字符串如admin、user可分配给一个或多个用户。角色会作为 claims 包含在认证时签发给用户的 ID Token 中。在模块的 reducer 内部你可以检查用户的角色来决定允许其执行哪些操作。从源码角度看SpacetimeDB 服务端确实具备完整的 JWT 签名与身份推导机制在 crates/core/src/auth/mod.rs 中定义了JwtKeysJWT 验证/签名密钥PEM 编码的 ECDSA P256 密钥并在 token 校验流程中通过Identity::from_claims(issuer, subject)将 JWT 的iss签发者与sub主题两个必选声明组合计算出用户的Identity见 crates/core/src/auth/token_validation.rs 中的大量测试断言。这正是文档中subject 与 issuer 是必需声明、用于计算每个用户的 Identity的底层实现。创建 SpacetimeAuth 项目SpacetimeAuth 可以为任何发布到 Maincloud 的模块启用。如果还没有发布模块请先参考 部署到 Maincloud 指南。第一步为模块启用 SpacetimeAuth将模块部署到 Maincloud如果尚未部署。进入 Maincloud 上已部署模块的 Dashboard点击右上角的个人头像在下拉菜单中选择 My profile从已部署模块列表中选择目标模块。在左侧边栏中点击 SpacetimeAuth。点击 Use SpacetimeAuth 按钮完成启用。启用后即创建了一个 SpacetimeAuth 项目同时系统会自动为你创建一个默认 Client。第二步探索 Dashboard创建项目后Dashboard 提供多个标签页管理项目不同方面标签页用途Overview概览项目摘要包括近期用户列表Clients客户端所有可用于应用认证的 Client 列表创建新项目时会自动生成一个默认 ClientUsers用户项目内所有用户支持搜索、过滤与管理Identity Providers身份提供商可用于项目用户认证的身份提供商列表如 Google、GitHub 等Customization自定义实时编辑器自定义颜色、Logo 与认证方式配置项目Clients、Scopes 与 Redirect URIsSpacetimeAuth 项目可在 Dashboard 中高度定制。本节聚焦最常见的配置任务。管理 ClientsClient 代表将使用 SpacetimeAuth 进行认证的应用。每个 Client 拥有独立设置包括redirect URIs、post logout URIs 和 name。在项目 Dashboard 的 Clients 标签页管理 Client每个项目自带一个可用于快速上手的默认 Client点击 Create Client 可创建更多 Client。大多数项目只需一个 Client即可为模块认证用户但如果有多个应用例如 sidecar、管理后台等且希望为每个应用使用不同认证流程或设置则可以创建多个 Client。创建或编辑 Client 时可配置以下设置Name名称Client 名称如 My Web App。Redirect URIs重定向 URI登录成功后 SpacetimeAuth 允许重定向的 URI 集合。必须与应用中使用的 URI 完全匹配。Post Logout Redirect URIs登出后重定向 URI登出后 SpacetimeAuth 允许重定向的 URI 集合。必须与应用中使用的 URI 匹配。危险Client Secret 安全警告务必妥善保管 client secret绝不要在客户端代码或公开仓库中暴露它。client ID 可以自由分享因为它不是敏感信息。Client secret 仅在client_credentials流程中使用用于获取一个无用户上下文的 Token此时sub声明会被设置为 client ID。Scopes 与 ClaimsScopes 目前不可编辑仅限于openid、profile、email三个。对大多数应用而言这三个 Scope 已足够它们提供了已认证用户的全部必要信息。各 Scope 提供的 claims即 ID Token 中可用的用户信息如下ScopeClaimsopenid必需sub唯一用户标识profilename、family_name、given_name、middle_name、nickname、preferred_username、picture、website、gender、birthdate、zoneinfo、locale、updated_atemailemail、email_verified在应用发起认证流程时可以请求全部或部分这些 Scope。Redirect URIs 配置要点Redirect URIs 是 OAuth2 与 OpenID Connect 流程中的关键部分它确保用户在认证完成后被重定向回应用中可信的位置。配置 redirect URIs 时必须与应用实际使用的 URI 精确匹配包括 schemehttp 或 https、域名、端口如有和路径。例如如果应用托管在https://myapp.com并从https://myapp.com/login发起认证流程可将 redirect URI 设置为https://myapp.com/callback。要确定应用的正确 redirect URIs请参考所用认证库的文档或查看 React 集成指南 等各框架集成指南。配置第三方身份提供商Identity ProvidersSpacetimeAuth 支持多个第三方身份提供商让用户使用已有账户登录。当前支持的提供商包括GoogleGitHubDiscordTwitchKick未来会添加更多提供商第三方身份提供商的用户信息会被映射到 SpacetimeAuth 使用的标准 OpenID Connect claims确保无论用户使用哪个提供商体验都是一致的。例如 username 声明会被映射到标准的preferred_username声明。在项目 Dashboard 的 Identity Providers 标签页管理提供商由于 SpacetimeAuth 是外部身份提供商的客户端你需要从提供商的开发者控制台获取 client ID 和 client secret 并填入以启用该提供商你还必须在提供商的开发者控制台中配置 redirect URI 指向 SpacetimeAuth见下方表格可以选择启用或禁用该提供商填写必要信息后点击 Save该提供商即可出现在应用的登录页面上。各提供商的官方指引创建 OAuth App / OAuth ClientGoogle、GitHub、Discord、Twitch、Kick 各自都有对应的开发者文档详见原文档此处不再逐一展开。为每个启用的提供商配置以下 redirect URIProviderRedirect URIGooglehttps://auth.spacetimedb.com/interactions/federated/callback/googleGitHubhttps://auth.spacetimedb.com/interactions/federated/callback/githubDiscordhttps://auth.spacetimedb.com/interactions/federated/callback/discordTwitchhttps://auth.spacetimedb.com/interactions/federated/callback/twitchKickhttps://auth.spacetimedb.com/interactions/federated/callback/kick在写代码之前用 OIDC Debugger 验证配置在把 SpacetimeAuth 集成进应用代码之前先用 OIDC Debugger 验证 Client 与 redirect URIs 是否正确这是最省事的做法。为什么使用 OIDC DebuggerOIDC Debugger 会在浏览器中模拟 OAuth2 / OIDCAuthorization Code 流程它可以帮助你确认redirect URIs配置正确验证client ID可用检查ID Token及其 claimsemail、sub、preferred_username等在写任何代码之前发现配置问题。第一步收集配置信息Authorization Endpointhttps://auth.spacetimedb.com/oidc/authToken Endpointhttps://auth.spacetimedb.com/oidc/tokenClient ID来自 SpacetimeAuth Dashboard可使用任意可用的 ClientRedirect URI必须把https://oidcdebugger.com/debug添加到 Client 的允许 redirect URIs 列表中第二步打开 OIDC Debugger 并填写表单访问 https://oidcdebugger.com按以下字段填写其余字段保持默认如 response type code、state、nonce字段值Authorize URIhttps://auth.spacetimedb.com/oidc/authClient ID你的 SpacetimeAuth client IDScopeopenid profile email或其子集Use PKCE?勾选Token URIhttps://auth.spacetimedb.com/oidc/token警告该工具在浏览器中运行无需输入 client secret。第三步运行流程点击Send Request使用任一已配置的提供商通过 SpacetimeAuth 登录浏览器会携带 authorization code 重定向回 OIDC DebuggerOIDC Debugger 会自动用 code 换取 tokens 并展示结果。第四步检查 Token根据请求的 Scope你将收到一个 ID Token。你可以用任何 JWT 解码器如 jwt.io解码查看其中的 claims例如{ sub: user_ergqg1q5eg15fdd54, project_id: project_xyz123, email: userexample.com, email_verified: true, preferred_username: exampleuser, first_name: Example, last_name: User, name: Example User }注意其中sub是用户唯一标识project_id标识该用户所属的 SpacetimeAuth 项目preferred_username由第三方提供商映射而来roles等自定义声明则会在配置了角色后出现在 Token 中。在 React 应用中集成 SpacetimeAuth官方指南使用 react-oidc-context 库在 React 应用中处理 OpenID Connect 认证该库为 React 提供了简单的 OIDC 处理方式。前置条件按 创建项目 与 配置项目 指南创建 SpacetimeAuth 项目并配置一个 Client准备一个 React 应用Create React App 或其他 React 框架均可在 React 应用中安装react-oidc-context包。1. 添加 OIDC 配置对象用 SpacetimeAuth 项目详情创建 OIDC 配置对象将YOUR_CLIENT_ID替换为 Dashboard 中的真实 client IDconst oidcConfig { authority: https://auth.spacetimedb.com/oidc, client_id: YOUR_CLIENT_ID, redirect_uri: ${window.location.origin}/callback, // 登录后用户被重定向到的位置 post_logout_redirect_uri: window.location.origin, // 登出后用户被重定向到的位置 scope: openid profile email, response_type: code, automaticSilentRenew: true, };注意authority以/oidc结尾这是 SpacetimeAuth 的 OIDC 发现端点基址scope与上文 Scope/Claims 表格中的三个标准 Scope 对应automaticSilentRenew: true启用访问令牌的静默自动续期。2. 创建调试组件该组件会将各类认证事件与状态变化打印到控制台便于调试export function OidcDebug() { const auth useAuth(); useEffect(() { const ev auth.events; const onUserLoaded (u: any) console.log([OIDC] userLoaded, u?.profile?.sub, u); const onUserUnloaded () console.log([OIDC] userUnloaded); const onAccessTokenExpiring () console.log([OIDC] accessTokenExpiring); const onAccessTokenExpired () console.log([OIDC] accessTokenExpired); const onSilentRenewError (e: any) console.warn([OIDC] silentRenewError, e); const onUserSignedOut () console.log([OIDC] userSignedOut); ev.addUserLoaded(onUserLoaded); ev.addUserUnloaded(onUserUnloaded); ev.addAccessTokenExpiring(onAccessTokenExpiring); ev.addAccessTokenExpired(onAccessTokenExpired); ev.addSilentRenewError(onSilentRenewError); ev.addUserSignedOut(onUserSignedOut); return () { ev.removeUserLoaded(onUserLoaded); ev.removeUserUnloaded(onUserUnloaded); ev.removeAccessTokenExpiring(onAccessTokenExpiring); ev.removeAccessTokenExpired(onAccessTokenExpired); ev.removeSilentRenewError(onSilentRenewError); ev.removeUserSignedOut(onUserSignedOut); }; }, [auth.events]); useEffect(() { console.log([OIDC] state, { isLoading: auth.isLoading, isAuthenticated: auth.isAuthenticated, error: auth.error?.message, activeNavigator: auth.activeNavigator, user: !!auth.user, }); }, [ auth.isLoading, auth.isAuthenticated, auth.error, auth.activeNavigator, auth.user, ]); return null; }3. 用 AuthProvider 包裹应用用AuthProvider组件包裹 React 应用提供认证上下文import React from react; import ReactDOM from react-dom/client; import { AuthProvider, useAuth } from react-oidc-context; import App from ./App; import { OidcDebug } from ./OidcDebug; const oidcConfig {...}; function onSigninCallback() { window.history.replaceState({}, document.title, window.location.pathname); } const root ReactDOM.createRoot(document.getElementById(root) as HTMLElement); root.render( AuthProvider {...oidcConfig} onSigninCallback{onSigninCallback} OidcDebug / App / /AuthProvider );onSigninCallback会在登录回调时清理 URL 中的认证参数避免把 code/state 留在地址栏中。4. 在应用组件中实现认证逻辑在App.tsx中使用useAuth读取认证状态并用useAutoSignin钩子让未认证用户自动跳转登录import React from react; import { useAuth, useAutoSignin } from react-oidc-context; import ./App.css; function App() { const auth useAuth(); useAutoSignin(); if (auth.isLoading) { return divLoading.../div; } if (auth.error) { return divError: {auth.error.message}/div; } if (!auth.isAuthenticated) { return divRedirecting to login.../div; } return ( div classNameApp header classNameApp-header Welcome, {auth.user?.profile.name} (id: {auth.user?.profile.sub})! button onClick{() auth.signoutRedirect()}Sign Out/button /header /div ); }完成以上步骤后用户访问应用时会被重定向到 SpacetimeAuth 登录页进行认证auth.user?.profile中即可读取name、sub等标准 claims。在服务端 Reducer 中使用 Auth Claims 进行鉴权拿到 ID Token 之后服务端 reducer 需要读取其中的 claims 来完成授权。SpacetimeDB 允许在 reducer 中通过ReducerContext访问嵌入在 OIDC 兼容 JWT 中的认证 claims。读取常用 ClaimsSubject 与 Issuersubsubject签发者分配给用户的唯一标识和ississuer签发 Token 的认证提供商是 JWT 中最常访问的 claims也是计算每个用户Identity的必需声明因此提供了辅助函数直接获取。以 Rust 为例#[reducer(client_connected)] pub fn connect(ctx: ReducerContext) - Result(), String { let auth_ctx ctx.sender_auth(); let (subject, issuer) match auth_ctx.jwt() { Some(claims) (claims.subject().to_string(), claims.issuer().to_string()), None { return Err(Client connected without JWT.to_string()); } }; log::info!(sub: {}, iss: {}, subject, issuer); Ok(()) }使用 Google 签发的 Token 时示例输出为INFO: src\lib.rs:64: sub: 321321321321321, iss: https://accounts.google.com类型脚本TypeScript与 C# 的等价写法同样在 认证 claims 使用文档 中有完整示例。限制认证提供商Restricting auth providers由于用户可以使用任何有效 Token 连接 SpacetimeDB其 Token 可能来自任意认证提供商例如你只想接受 Google 的 Token用户却拿来了 GitHub 的 OIDC Token。因此在客户端连接时至少检查 issuer是最佳实践确保数据只能被本应用的用户访问。此外必须检查audaudience声明确保 issuer 确实打算让你的应用接收该 Token防止其他应用拿到的 Token 被转用于你的应用。以下是 Rust 中仅允许 SpacetimeAuth 凭据访问的示例// 设置为你 SpacetimeAuth 项目的 OIDC client或一组 client const OIDC_CLIENT_ID: str client_XXXXXXXXXXXXXXXXXXXXXX; #[reducer(client_connected)] pub fn connect(ctx: ReducerContext) - Result(), String { let jwt ctx.sender_auth().jwt().ok_or(Authentication required.to_string())?; if jwt.issuer() ! https://auth.spacetimedb.com/oidc { return Err(Invalid issuer.to_string()); } if !jwt.audience().iter().any(|a| a OIDC_CLIENT_ID) { return Err(Invalid audience.to_string()); } Ok(()) }其中 issuer 为 SpacetimeAuth 的https://auth.spacetimedb.com/oidc与 OIDC Debugger 测试时的 authority 一致audience 则是你的 OIDC client ID以client_开头。访问自定义 Claims如 roles对于辅助函数未覆盖的其他 claims例如应用自定义的roles声明可以解析完整的 JWT payload。假设 Token 带有一个roles声明权限列表需要确保只有admin角色的用户能调用某个 reducerRust 中先在Cargo.toml添加serde与serde_json[dependencies] ... serde { version 1.0.219, features [derive] } serde_json 1.0.143然后#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CustomClaims { pub roles: VecString, } /// 如果发送者拥有 admin 权限返回 Ok(())否则返回 Err。 fn ensure_admin_access(sender_auth: spacetimedb::AuthCtx) - Result(), String { if sender_auth.is_internal() { // 这是定时 reducer应被视为可信。 return Ok(()); } let jwt sender_auth.jwt().ok_or(Authentication required.to_string())?; let claims: CustomClaims serde_json::from_slice(jwt.raw_payload().as_bytes()).map_err(|e| format!(Client connected with invalid JWT: {}, e).to_string())?; if claims.roles.iter().any(|r| r admin) { return Ok(()); } Err(Admin role required.to_string()) } #[spacetimedb::reducer] pub fn admin_only_reducer(ctx: ReducerContext) - Result(), String { ensure_admin_access(ctx.sender_auth())?; // 现在可以安全地执行仅管理员操作。 Ok(()) }注意这里先判断is_internal()定时scheduledreducer 是受信任的无需 JWT。TypeScript、C# 与 C 的等价实现可参考 认证 claims 使用文档。游戏场景Steam Session Ticket 认证对于计划在 Steam 上分发的游戏应用SpacetimeAuth 支持通过 Steam 的 Session Ticket 系统认证。用户使用 Steam 账号认证后SpacetimeAuth 会为其在项目中创建账号并签发应用可用于 SpacetimeDB 认证的 ID Token。Steam ID Token 中的 ClaimsSteam 认证的 ID Token 特别包含以下 claimsClaim含义sub用户在 SpacetimeAuth 中的唯一标识provider_id用户在 Steam 中的唯一标识login_method认证提供商此处为steampreferred_username用户的 Steam 显示名picture用户 Steam 头像 URL完整尺寸steam_owned_games用户拥有的你发布的所有应用数组配置步骤创建 Steam Publisher Key在 Steamworks Dashboard 中创建 Steam Publisher Key用于验证 Steam 提供的会话票据的真实性并获取用户的游戏拥有信息。将 Publisher Key 与允许的 App ID 添加到 SpacetimeAuth在 Dashboard 的 Settings 区域添加 Publisher Key 及需要检查所有权的 app ID 列表。任何将通过 Steam Session Tickets 认证用户的 app ID 都必须加入该列表确保只有这些 app ID 签发的票据才会被接受这是防止其他应用票据导致未授权访问的重要安全措施游戏或 DLC 的 app ID 都要加入。获取 Steam Session Ticket根据开发平台使用对应 Steamworks SDK 获取会话票据然后发送到后端服务器进行认证Steamworks SDKC/Unreal Engine、Steamworks .NetC#/Unity、GodotSteamGodot Engine、Steamworks.jsJavaScript/Node.js警告请求会话票据时必须使用spacetimeauth作为 identity 参数。用 curl 换取 ID Token拿到 Steam Session Ticket 后向 SpacetimeAuth 的 token endpoint 发送请求换取 ID Tokencurl -X POST https://auth.spacetimedb.com/oidc/token \ -H content-type: application/x-www-form-urlencoded \ -d client_idclient_032xAh3p8o3zGzDghXsO5x \ -d grant_typeurn:spacetimeauth:steam-ticket \ -d steam_ticketYOUR STEAM TICKET \ -d steam_app_idAPP ID FROM WHICH YOU REQUESTED THE TICKET注意grant_type使用了 SpacetimeAuth 自定义的urn:spacetimeauth:steam-ticket并需要同时携带票据与请求票据时的 app ID。在 Reducer 中检查 App/DLC 所有权通过解析 ID Token 中的steam_owned_gamesclaim每个条目含appid和布尔值ownsapp即可实现基于游戏所有权的授权逻辑例如仅允许拥有某款游戏的用户连接#[derive(serde::Deserialize)] struct SteamGame { appid: u64, ownsapp: bool, } #[derive(serde::Deserialize)] struct CustomClaims { steam_owned_games: VecSteamGame, } #[reducer(client_connected)] pub fn connect(ctx: ReducerContext) - Result(), String { let jwt ctx.sender_auth().jwt().ok_or(JWT required)?; let claims: CustomClaims serde_json::from_slice(jwt.raw_payload().as_bytes()) .map_err(|e| format!(Invalid JWT: {}, e))?; const APP_ID: u64 2717550; let owns claims.steam_owned_games.iter().any(|g| g.appid APP_ID g.ownsapp); if !owns { return Err(format!(Unauthorized: does not own app {}, APP_ID)); } Ok(()) }总结与最佳实践先验证再写代码在集成前使用 OIDC Debugger 验证 authorization endpoint、token endpoint、client ID 与 redirect URIs可显著减少排查成本。始终校验 JWT claims 的存在与内容在应用逻辑信任它们之前必须验证。检查aud与iss连接时至少校验 issuer 与 audience限制 Token 只能来自你的 SpacetimeAuth 项目防止 Token 被转用。自定义逻辑解析完整 payload对辅助函数未解析的自定义 claims如roles、steam_owned_games反序列化完整 JWT payload 获取。保护 client secret绝不在客户端代码或公开仓库中暴露 client secret仅在client_credentials流程使用。按场景规划项目多应用共用用户群用单项目多环境/多应用隔离用户则拆分项目。基于角色的访问控制在 Dashboard 为用户分配角色并在 reducer 中读取rolesclaim 实施授权。相关文档索引创建 SpacetimeAuth 项目配置 SpacetimeAuth 项目用 OIDC Debugger 测试配置React 集成指南Steam Session Ticket 认证在 Reducer 中使用认证 Claims部署到 Maincloud【免费下载链接】SpacetimeDBDevelopment at the speed of light项目地址: https://gitcode.com/GitHub_Trending/sp/SpacetimeDB创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表