关于 clerk-expo-patterns
面向使用 Clerk 的 Expo 开发者的模式参考,介绍令牌持久化、OAuth 深链接、受保护屏幕和组织切换等常见实现。
主要功能
- 01安全存储令牌缓存
- 02OAuth 深链接配置
- 03Expo Router 屏幕保护
- 04推送通知用户数据模式
- 05认证与组织组合式函数
适用场景
- 01在应用重启后保留会话
- 02配置 Google、Apple 或 GitHub OAuth
- 03限制未登录用户访问屏幕
- 04在应用中切换组织
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-expo-patterns安装后在 Claude Code 中输入/clerk-expo-patterns确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/oauth-deep-linking.mdprotected-routes.mdpush-notifications.mdtoken-storage.mdtemplates/expo-basic-auth/app.jsonpackage.jsontsconfig.jsontemplates/expo-basic-auth/app/_layout.tsxtemplates/expo-basic-auth/app/(auth)/_layout.tsxtemplates/expo-basic-auth/app/(home)/_layout.tsxindex.tsx详细介绍
Expo 模式
SDK:@clerk/expo v3+。需要 Expo 53+、React Native 0.73+。
你需要什么?
| 任务 | 参考文档 |
|---|---|
| 使用 SecureStore 持久化令牌 | references/token-storage.md |
| OAuth(Google、Apple、GitHub) | references/oauth-deep-linking.md |
| 使用 Expo Router 设置受保护的屏幕 | references/protected-routes.md |
| 使用用户数据发送推送通知 | references/push-notifications.md |
心智模型
Clerk 默认会将会话令牌存储在内存中。在原生应用中:
- SecureStore — 将令牌加密后存储在设备钥匙串中(建议用于生产环境)
tokenCache—<ClerkProvider>上用于提供自定义存储的属性useAuth— 与 Web 端相同的 API,可在任何组件中使用- OAuth — 需要使用
useSSO,并在app.json中配置深层链接 scheme
最简设置
app/_layout.tsx
import { ClerkProvider } from '@clerk/expo'
import { tokenCache } from '@clerk/expo/token-cache'
import { Stack } from 'expo-router'
const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!
export default function RootLayout() {
return (
<ClerkProvider publishableKey={publishableKey} tokenCache={tokenCache}>
<Stack />
</ClerkProvider>
)
}
重要提示:使用
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY,而不是NEXT_PUBLIC_。生产构建不会内联node_modules中的环境变量。务必显式传入publishableKey。
内置令牌缓存
import { tokenCache } from '@clerk/expo/token-cache'
这会使用 expo-secure-store,并设置 keychainAccessible: AFTER_FIRST_UNLOCK。请安装 peer dependency:
npx expo install expo-secure-store
身份验证 Hook
import { useAuth, useUser, useSignIn, useSignUp, useClerk } from '@clerk/expo'
export function ProfileScreen() {
const { isSignedIn, userId, signOut } = useAuth()
const { user } = useUser()
if (!isSignedIn) return <Redirect href="/sign-in" />
return (
<View>
<Text>{user?.fullName}</Text>
<Button title="Sign Out" onPress={() => signOut()} />
</View>
)
}
OAuth 流程(Google)
import { useSSO } from '@clerk/expo'
import * as WebBrowser from 'expo-web-browser'
WebBrowser.maybeCompleteAuthSession()
export function GoogleSignIn() {
const { startSSOFlow } = useSSO()
const handlePress = async () => {
try {
const { createdSessionId, setActive } = await startSSOFlow({
strategy: 'oauth_google',
redirectUrl: 'myapp://oauth-callback',
})
if (createdSessionId) await setActive!({ session: createdSessionId })
} catch (err) {
console.error(err)
}
}
return <Button title="Continue with Google" onPress={handlePress} />
}
切换组织
import { useOrganization, useOrganizationList } from '@clerk/expo'
export function OrgSwitcher() {
const { organization } = useOrganization()
const { setActive, userMemberships } = useOrganizationList()
return (
<View>
<Text>Current: {organization?.name ?? 'Personal'}</Text>
{userMemberships.data?.map(mem => (
<Button
key={mem.organization.id}
title={mem.organization.name}
onPress={() => setActive({ organization: mem.organization.id })}
/>
))}
</View>
)
}
常见问题
| 现象 | 原因 | 解决方法 |
|---|---|---|
生产环境中的 publishableKey 未定义 | 使用的环境变量没有 EXPO_PUBLIC_ 前缀 | 将其重命名为 EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY |
| 应用重启后令牌丢失 | 未设置 tokenCache | 传入来自 @clerk/expo/token-cache 的 tokenCache |
| OAuth 重定向无法使用 | app.json 中缺少 scheme | 在 app.json 中添加 "scheme": "myapp" |
WebBrowser.maybeCompleteAuthSession | 未调用 | 在 OAuth 回调屏幕的顶层调用它 |
找不到 useSSO | @clerk/expo 版本过旧 | 在 v3+ 中,useSSO 取代了 useOAuth |
导入速查表
| 用途 | 导入路径 |
|---|---|
ClerkProvider | @clerk/expo |
tokenCache | @clerk/expo/token-cache |
useAuth、useUser、useSignIn | @clerk/expo |
useSSO | @clerk/expo |
useOrganization、useOrganizationList | @clerk/expo |
另请参阅
clerk-setup- Clerk 初始安装clerk-custom-ui- 自定义流程和外观clerk-orgs- B2B 组织
文档
评论
支持 Markdown