在 Redwood 中使用 GoTrue 构建自托管身份认证(Sign Up / Sign In / Sign Out 全流程)
发布时间:2026/9/25 8:05:15来源:尧图网络
后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载这篇指南将带你脱离 Netlify Identity Widget 的“一键式”束缚改用 GoTrue-JS 客户端库直接对接 Netlify Identity 的 GoTrue API在 Redwood 应用中完全掌控认证界面与交互流程。读完本文你将掌握如何用yarn redwood setup auth goTrue生成认证配置、手写 Sign Up / Sign In 表单、实现 Sign Out 按钮并通过useAuth的认证状态在导航中条件渲染正确的操作入口。本文以 Redwood 官方文档《GoTrue Auth》为基础并结合当前仓库源码CLI 认证脚手架、Netlify 认证提供者实现进行纵深讲解。为什么放弃 Widget、拥抱 GoTrue-JS如果你已完成 Redwood 教程的认证章节会发现用 Netlify Identity Widget 几分钟内就能给应用加上认证。但 Widget 是一套封装好的弹窗交互你很难定制界面与行为。GoTrue-JS 是 Netlify Identity 的 GoTrue API 的官方 JavaScript 客户端库它把注册、登录、登出、令牌刷新等能力以编程接口的形式暴露出来让你完全自定义认证表单的 UI 与校验逻辑在开发阶段保持较低的接入复杂度复用 Redwood 的useAuth钩子以统一方式订阅认证状态。在本文的实操中我们将完成四件事用 Redwood CLI 生成 GoTrue 认证配置创建 Sign Up注册表单创建 Sign In登录表单创建 Sign Out登出按钮添加根据认证状态显示正确按钮的导航链接。前置条件动手之前请确保已完成以下步骤创建一个 Redwood 应用注册 Netlify 账户启用 Netlify Identity启动开发服务器yarn redwood dev。启用 Netlify Identity登录 Netlify进入站点Dashboard打开Identity标签页点击Enable Identity。启用后你会看到形如https://my-bodacious-app.netlify.app/.netlify/identity的 Identity API 端点。请把该地址复制保存稍后实例化 GoTrue-JS 时需要用到。生成认证配置安装所需依赖包并生成 Redwood Auth 的样板代码只需要一条 CLI 命令yarn redwood setup auth goTrue指定goTrue作为提供者后Redwood 会自动把 GoTrue-JS 的配置写入web/src/App.js。打开该文件你应该看到import { AuthProvider } from redwoodjs/auth import GoTrue from gotrue-js import { FatalErrorBoundary } from redwoodjs/web import { RedwoodApolloProvider } from redwoodjs/web/apollo import FatalErrorPage from src/pages/FatalErrorPage import Routes from src/Routes import ./index.css const goTrueClient new GoTrue({ APIUrl: https://MYAPP.netlify.app/.netlify/identity, setCookie: true, }) const App () ( FatalErrorBoundary page{FatalErrorPage} AuthProvider client{goTrueClient} typegoTrue RedwoodApolloProvider Routes / /RedwoodApolloProvider /AuthProvider /FatalErrorBoundary ) export default App现在把刚才复制的 API 端点替换到APIUrl中// imports... const goTrueClient new GoTrue({ APIUrl: https://gotrue-recipe.netlify.app/.netlify/identity, setCookie: true, })配置到此结束。核心要点是AuthProvider接收clientGoTrue-JS 实例和typegoTrue两个关键属性之后整个组件树都能通过useAuth访问该客户端及其全部方法。关于“goTrue”的现状说明当前仓库的 CLI 认证脚手架 中goTrue已作为“不再单独支持”的历史提供者被重定向redirectCommand(goTrue)官方现在建议改用netlify基于 netlify-identity-widget或 Custom Auth。但 GoTrue 的接入思路——用统一clienttype注入AuthProvider、通过useAuth消费——在 Redwood 的所有认证提供者中一脉相承理解本文流程仍可直接迁移。在 v6.x 时代setup auth goTrue会直接生成上面的代码。Sign Up注册页先确保在 Netlify Dashboard 的Identity Settings and usage中进入Emails Confirmation template Edit settings勾选Allow users to sign up without verifying their email address并保存。这样首版注册流程不需要处理邮箱确认其余进阶能力邮箱确认、密码找回等留待后续扩展。生成注册页yarn redwood generate page Signup该命令会为 Signup 添加一条 路由并创建 SignupPage 组件。在web/src/pages/SignupPage/SignupPage.js中导入 Redwood Form 组件加入一个最基础的注册表单import { Form, TextField, PasswordField, Submit } from redwoodjs/forms const SignupPage () { return ( h1Sign Up/h1 Form TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } export default SignupPageTextField与PasswordField的name属性会直接映射到onSubmit回调收到的数据对象字段上这是 Redwood Form 的核心约定。接入 useAuth 与注册逻辑接下来让表单在提交时真正执行注册。先为Form增加onSubmit处理函数// imports... const SignupPage () { const onSubmit (data) { // do something here } return ( h1Sign Up/h1 Form onSubmit{onSubmit} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } //...现在需要一条与AuthProvider /及 GoTrue-JS 客户端通信的通道——正是useAuth钩子。它让我们订阅认证状态及其属性此处通过它拿到clientGoTrue-JS 实例import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth const SignupPage () { const { client } useAuth() const onSubmit (data) { // do something here } return ( h1Sign Up/h1 Form onSubmit{onSubmit} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } export default SignupPage在onSubmit中调用client.signup(email, password)创建新用户import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth const SignupPage () { const { client } useAuth() const onSubmit (data) { client .signup(data.email, data.password) .then((res) console.log(res)) .catch((error) console.log(error)) } return ( h1Sign Up/h1 Form onSubmit{onSubmit} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } export default SignupPage错误处理与成功跳转注册本身已经能工作但把响应打印到控制台显然不够。我们用React.useState管理错误状态在每次提交前用setError(null)重置并在表单中条件渲染错误消息import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth const SignupPage () { const { client } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) client .signup(data.email, data.password) .then((res) console.log(res)) .catch((error) setError(error.message)) } return ( h1Sign Up/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } export default SignupPage注册成功后最自然的做法是把用户引导到即将建好的登录页。先生成 Sign In 页面yarn redwood generate page Signin再从 Redwood Router 导入routes与navigate在注册成功的then回调中跳转import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth import { routes, navigate } from redwoodjs/router const SignupPage () { const { client } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) client .signup(data.email, data.password) .then(() navigate(routes.signin())) .catch((error) setError(error.message)) } return ( h1Sign Up/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form / ) } export default SignupPage至此注册页完成提交表单 → 调用client.signup()→ 失败显示错误成功跳转登录页。Sign In登录页在上一节生成的 SigninPage 中加入包含email、password的基础表单、错误状态与空的onSubmitimport { Form, TextField, PasswordField, Submit } from redwoodjs/forms const SigninPage () { const [error, setError] React.useState(null) const onSubmit (data) { // do sign in here } return ( h1Sign In/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign In/Submit /Form / ) } export default SigninPage从useAuth中解构出logInimport { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth const SigninPage () { const { logIn } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) // do sign in here } return ( h1Sign In/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign In/Submit /Form / ) } export default SigninPage注意与注册的差异这次直接调用 Redwood Auth 的logIn函数而非client参数是一个对象包含email、password和一个remember布尔值控制是否记住登录状态import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth const SigninPage () { const { logIn } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() { // do something }) .catch((error) setError(error.message)) } return ( h1Sign In/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign In/Submit /Form / ) } export default SigninPage登录成功后把用户带回首页。先生成首页如果还没有yarn redwood generate page Home /在 SigninPage 中导入navigate与routes在then回调中跳转import { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth import { navigate, routes } from redwoodjs/router const SigninPage () { const { logIn } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() navigate(routes.home())) .catch((error) setError(error.message)) } return ( h1Sign In/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign In/Submit /Form / ) } export default SigninPage登录页完成提交表单 →logIn({ email, password, remember })→ 失败显示错误成功跳转首页。Sign Out登出按钮登出是最容易实现的认证功能只需触发useAuth的logOut方法。先生成一个组件存放登出按钮yarn redwood generate component SignoutBtn在web/src/components/SignoutBtn/SignoutBtn.js中渲染按钮并添加点击处理器const SignoutBtn () { const onClick () { // do sign out here. } return button onClick{() onClick()}Sign Out/button } export default SignoutBtn导入useAuth解构logOut并在onClick中调用import { useAuth } from redwoodjs/auth const SignoutBtn () { const { logOut } useAuth() const onClick () { logOut() } return button onClick{() onClick()}Sign Out/button } export default SignoutBtn如果用户是在应用的私密区域点击登出按钮还应该把用户导航离开当前页面import { useAuth } from redwoodjs/auth import { navigate, routes } from redwoodjs/router const SignoutBtn () { const { logOut } useAuth() const onClick () { logOut().then(() navigate(routes.home())) } return button onClick{() onClick()}Sign Out/button } export default SignoutBtn此时logOut()完成后会返回一个 Promise随后跳转到首页。Auth Links根据认证状态渲染导航现在实现条件导航未登录时显示Sign Up与Sign In已登录时显示Log Out。先生成导航组件yarn redwood generate component Navigation在web/src/components/Navigation/Navigation.js中从redwoodjs/router导入 Link 组件与 routes 对象并导入useAuth以便订阅认证状态import { Link, routes } from redwoodjs/router import { useAuth } from redwoodjs/auth const Navigation () { return nav/nav } export default Navigation从useAuth解构isAuthenticated并应用到渲染条件中import { Link, routes } from redwoodjs/router import { useAuth } from redwoodjs/auth const Navigation () { const { isAuthenticated } useAuth() return ( nav {isAuthenticated ? ( // signed in - show the Sign Out button ) : ( // signed out - show the Sign Up and Sign In links )} /nav ) } export default NavigationRedwood Auth 基于 React Context API 管理并广播认证状态因此isAuthenticated始终是最新的——即使认证状态在组件树中其他位置发生变化只要该组件是AuthProvider /的子节点React 也会自动重新渲染出正确的组件。接下来导入登出按钮并把 Sign In / Sign Up 链接放进条件分支import { Link, routes } from redwoodjs/router import { useAuth } from redwoodjs/auth import SignoutBtn from src/components/SignoutBtn/SignoutBtn const Navigation () { const { isAuthenticated } useAuth() return ( nav {isAuthenticated ? ( SignoutBtn / ) : ( Link to{routes.signup()}Sign Up/Link Link to{routes.signin()}Sign In/Link / )} /nav ) } export default Navigation把导航挂进全局布局导航组件还需渲染到应用中。先生成一个名为 Global 的布局yarn redwood generate layout Global在web/src/layouts/GlobalLayout/GlobalLayout.js中导入并渲染导航组件import Navigation from src/components/Navigation/Navigation const GlobalLayout ({ children }) { return ( header Navigation / /header main{children}/main / ) } export default GlobalLayout最后用 GlobalLayout 包裹每个生成的页面Homeimport GlobalLayout from src/layouts/GlobalLayout/GlobalLayout const HomePage () { return ( GlobalLayout h1Home/h1 pMy Gotrue Redwood Auth/p /GlobalLayout ) } export default HomePageSign Upimport { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth import { routes, navigate } from redwoodjs/router import GlobalLayout from src/layouts/GlobalLayout/GlobalLayout const SignupPage () { const { client } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) client .signup(data.email, data.password) .then(() navigate(routes.signin())) .catch((error) setError(error.message)) } return ( GlobalLayout h1Sign Up/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign Up/Submit /Form /GlobalLayout ) } export default SignupPageSign Inimport { Form, TextField, PasswordField, Submit } from redwoodjs/forms import { useAuth } from redwoodjs/auth import { navigate, routes } from redwoodjs/router import GlobalLayout from src/layouts/GlobalLayout/GlobalLayout const SigninPage () { const { logIn } useAuth() const [error, setError] React.useState(null) const onSubmit (data) { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() navigate(routes.home())) .catch((error) setError(error.message)) } return ( GlobalLayout h1Sign In/h1 Form onSubmit{onSubmit} {error p{error}/p} TextField nameemail placeholderemail / PasswordField namepassword placeholderpassword / SubmitSign In/Submit /Form /GlobalLayout ) } export default SigninPage现在导航会根据认证状态渲染正确的链接和按钮用户登录后看到Sign Out按钮登出后看到Sign Up与Sign In链接。源码视角认证提供者如何工作为加深理解可以对照当前仓库中 Redwood 官方认证提供者的实现。以 Netlify 提供者的前端实现 为例它的核心是createAuthImplementation返回的一组接口包括login打开登录弹窗并监听login/close/error事件返回 Promise 形式的用户对象logout调用netlifyIdentity.logout()在logout事件触发时 resolvesignup打开注册弹窗关闭时 resolvegetToken调用netlifyIdentity.refresh()仅在令牌过期时真正刷新返回access_tokengetUserMetadata/restoreAuthState返回当前用户。createAuth(implementation)会把这份实现接入 Redwood 的useAuth这正是我们页面中logIn、logOut、client、isAuthenticated等 API 的底层来源。GoTrue-JS 时代的接入方式AuthProvider client{goTrueClient} typegoTrue与该模式完全一致Redwood 只负责“翻译”认证客户端的能力认证的存储、刷新与校验仍由提供方完成。总结至此我们已经用yarn redwood setup auth goTrue配置了 GoTrue 与 Redwood Auth 的对接创建了 Sign Up 页面含错误提示与成功跳转创建了 Sign In 页面logIn({ email, password, remember })登录并跳转首页创建了 Sign Out 按钮logOut()后导航回首页在 GlobalLayout 中挂载了条件导航根据isAuthenticated渲染正确操作入口。这套“底层认证客户端 RedwooduseAuth统一接口 自定义表单”的模式可以平滑迁移到 Redwood 当前支持的任意认证提供者如 Netlify、Supabase、Auth0 等是深入理解 Redwood 认证体系的良好起点。后续还可以继续扩展邮箱确认、密码找回等进阶功能。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐终极指南如何使用Sign-In with Ethereum实现去中心化身份认证终极指南如何使用Sign In with Ethereum实现去中心化身份认证 Sign In with EthereumSIWE正在彻底改变我们进行身份Noodle平台认证系统实现从Sign-In到权限管理全流程Noodle平台认证系统实现从Sign In到权限管理全流程 你是否还在为开源项目的用户认证系统搭建而烦恼Noodle平台作为开源教育平台Open Sou教育前端后端Barter-rs多策略并发执行提升交易效率的高级技巧Barter rs多策略并发执行提升交易效率的高级技巧 Barter rs是一个开源的Rust框架专为构建事件驱动的实时交易和回测系统而设计。多策略并发执行金融科技上一篇GPT-NeoX数据增强终极指南文本扰动与噪声注入技术详解下一篇如何用柚坛工具箱NT轻松搞定Android设备刷机、应用管理与调试创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网