# Expo 集成 (/docs/integrations/expo)

将 Better Auth 与 Expo 集成。



Expo 是一个使用 React Native 构建跨平台应用的流行框架。Better Auth 支持 Expo 原生和 Web 应用。

<Callout type="info">
  本指南适用于 **Expo SDK 55**（React Native 0.83，React 19.2）。SDK 55 需要使用 [New Architecture](https://docs.expo.dev/guides/new-architecture/)，旧的架构不再受支持。如果正在从旧版 SDK 升级，请参考 [Expo SDK 55 升级指南](https://expo.dev/blog/upgrading-to-sdk-55)。
</Callout>

## 安装 [#安装]

<Steps>
  <Step>
    ## 配置 Better Auth 后端 [#配置-better-auth-后端]

    在 Expo 中使用 Better Auth 之前，请确保已设置 Better Auth 后端。可以使用单独的服务器，或利用 Expo 新的 [API 路由](https://docs.expo.dev/router/reference/api-routes) 功能来托管 Better Auth 实例。

    要开始使用，请先查看我们的 [安装](/docs/installation) 指南，了解如何在服务器上配置 Better Auth。如果你愿意，也可以查看 [完整示例](https://github.com/better-auth/examples/tree/main/expo-example)。

    若要在 Expo 中使用新的 API 路由功能来托管 Better Auth 实例，可以在 Expo 应用中创建一个新的 API 路由，并挂载 Better Auth 处理程序。

    ```ts title="app/api/auth/[...auth]+api.ts"
    import { auth } from "@/lib/auth"; // 导入 Better Auth 处理程序

    const handler = auth.handler;
    export { handler as GET, handler as POST }; // 导出 GET 和 POST 请求的处理程序
    ```
  </Step>

  <Step>
    ## 安装服务端依赖 [#安装服务端依赖]

    在服务端应用中安装 Better Auth 包和 Expo 插件。

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add better-auth @better-auth/expo
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    <Callout type="info">
      如果使用的是 Expo 的 API 路由，可以参考以下步骤。
    </Callout>
  </Step>

  <Step>
    ## 安装客户端依赖 [#安装客户端依赖]

    * 同样需要在 Expo 应用中安装 Better Auth 包和 Expo 插件。

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add better-auth @better-auth/expo
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add better-auth @better-auth/expo
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    * 需要安装 `expo-network` 以检测网络状态。

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install expo-network
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add expo-network
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add expo-network
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add expo-network
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    * <small className="text-xs">(可选)</small> 如果使用默认 Expo 模板，这些依赖项已包含，可跳过此步骤。否则，如果计划使用社交提供程序（如 Google、Apple），则需额外安装以下依赖：

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install expo-linking expo-web-browser expo-constants
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add expo-linking expo-web-browser expo-constants
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add expo-linking expo-web-browser expo-constants
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add expo-linking expo-web-browser expo-constants
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Step>

  <Step>
    ## 在服务端添加 Expo 插件 [#在服务端添加-expo-插件]

    在 Better Auth 服务端添加 Expo 插件。

    ```ts title="lib/auth.ts"
    import { betterAuth } from "better-auth";
    import { expo } from "@better-auth/expo";

    export const auth = betterAuth({
        plugins: [expo()],
        emailAndPassword: { 
            enabled: true, // 启用邮箱和密码认证。
          }, 
    });
    ```
  </Step>

  <Step>
    ## 初始化 Better Auth 客户端 [#初始化-better-auth-客户端]

    在 Expo 应用中初始化 Better Auth 时，需要使用 `createAuthClient` 并传入 Better Auth 后端的基准 URL。确保从 `/react` 导入客户端。

    同时需要在 Expo 应用中安装 `expo-secure-store` 包，用于安全存储会话数据和 Cookie。

    <CodeBlockTabs defaultValue="npm" groupId="persist-install">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm install expo-secure-store
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add expo-secure-store
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add expo-secure-store
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add expo-secure-store
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    初始化认证客户端时，还需要导入 `@better-auth/expo/client` 插件并将其添加到 `plugins` 数组中。

    这样做是因为：

    * **社交认证支持：** 在 Expo 浏览器中处理授权 URL 和回调。
    * **安全 Cookie 管理：** 安全存储 Cookie 并自动将其添加到认证请求的头部。

    ```ts title="lib/auth-client.ts"
    import { createAuthClient } from "better-auth/react";
    import { expoClient } from "@better-auth/expo/client";
    import * as SecureStore from "expo-secure-store";

    export const authClient = createAuthClient({
        baseURL: "http://localhost:8081", // Better Auth 后端的基准 URL。
        plugins: [
            expoClient({
                scheme: "myapp",
                storagePrefix: "myapp",
                storage: SecureStore,
            })
        ]
    });
    ```

    <Callout>
      如果更改了默认路径 `/api/auth`，请确保包含完整的 URL，包括路径。
    </Callout>
  </Step>

  <Step>
    ## Scheme 和可信来源 [#scheme-和可信来源]

    Better Auth 使用深度链接在认证后将用户重定向回应用。为此，需要在 Better Auth 配置中将应用 Scheme 添加到 `trustedOrigins` 列表中。

    首先，在 `app.json` 文件中定义 Scheme。

    ```json title="app.json"
    {
        "expo": {
            "scheme": "myapp"
        }
    }
    ```

    然后，在 Better Auth 配置中将 Scheme 添加到 `trustedOrigins` 列表。

    ```ts title="auth.ts"
    export const auth = betterAuth({
        trustedOrigins: ["myapp://"]
    })
    ```

    如果有多个 Scheme 或需要支持不同路径的深度链接，可以使用特定模式或通配符：

    ```ts title="auth.ts"
    export const auth = betterAuth({
        trustedOrigins: [
            // 基本 Scheme
            "myapp://", 
            
            // 生产与暂存 Scheme
            "myapp-prod://",
            "myapp-staging://",
            
            // 支持 Scheme 后的所有路径（通配符）
            "myapp://*"
        ]
    })
    ```

    ### 开发模式 [#开发模式]

    开发期间，Expo 使用 `exp://` Scheme 和设备的本地 IP 地址。为支持此模式，可以使用通配符匹配常见的本地 IP 范围：

    ```ts title="auth.ts"
    export const auth = betterAuth({
        trustedOrigins: [
            "myapp://",
            
            // 开发模式 - Expo 的 exp:// Scheme 及本地 IP 范围
            ...(process.env.NODE_ENV === "development" ? [
                "exp://",                      // Trust any host of the exp:// scheme
                "exp://**",                    // Trust all Expo URLs (wildcard matching)
                "exp://192.168.*.*:*/**",      // Trust 192.168.x.x IP range with any port and path
            ] : [])
        ]
    })
    ```

    更多关于可信来源的信息，请参阅 [可信来源文档](/docs/reference/options#trustedorigins)。

    <Callout type="warn">
      开发环境中 `exp://` 的通配符模式仅应用于开发环境。在生产环境中，请使用应用特定的 Scheme（如 `myapp://`）。
    </Callout>
  </Step>

  <Step>
    ## 配置 Metro 打包器 [#配置-metro-打包器]

    Better Auth 依赖 `package.json` 的 exports 来解析模块。从 &#x2A;*Expo SDK 53+**（包括 SDK 55）开始，Metro 默认启用对 package exports 的支持，因此**无需额外的 Metro 配置**。

    如果有自定义的 `metro.config.js`，请确保没有禁用 package exports：

    ```js title="metro.config.js"
    const { getDefaultConfig } = require("expo/metro-config");

    const config = getDefaultConfig(__dirname);

    // 不要将其设置为 false - Better Auth 需要 package exports
    // config.resolver.unstable_enablePackageExports = false;

    module.exports = config;
    ```

    修改 Metro 配置后，别忘了清除缓存。

    ```bash
    npx expo start --clear
    ```
  </Step>
</Steps>

## 用法 [#用法]

Better Auth 初始化后，可以在 Expo 应用中使用 `authClient` 进行用户认证。

使用 Better Auth 初始化后，现在可以使用 `authClient` 在 Expo 应用中认证用户。

<Tabs items="[&#x22;登录&#x22;, &#x22;注册&#x22;]">
  <Tab value="sign-in">
    ```tsx title="app/sign-in.tsx"
    import { useState } from "react"; 
    import { View, TextInput, Button } from "react-native";
    import { authClient } from "@/lib/auth-client";

    export default function SignIn() {
        const [email, setEmail] = useState("");
        const [password, setPassword] = useState("");

        const handleLogin = async () => {
            await authClient.signIn.email({
                email,
                password,
            })
        };

        return (
            <View>
                <TextInput
                    placeholder="邮箱"
                    value={email}
                    onChangeText={setEmail}
                />
                <TextInput
                    placeholder="密码"
                    value={password}
                    onChangeText={setPassword}
                />
                <Button title="登录" onPress={handleLogin} />
            </View>
        );
    }
    ```
  </Tab>

  <Tab value="sign-up">
    ```tsx title="app/sign-up.tsx"
    import { useState } from "react";
    import { View, TextInput, Button } from "react-native";
    import { authClient } from "@/lib/auth-client";

    export default function SignUp() {
        const [email, setEmail] = useState("");
        const [name, setName] = useState("");
        const [password, setPassword] = useState("");

        const handleLogin = async () => {
            await authClient.signUp.email({
                    email,
                    password,
                    name
            })
        };

        return (
            <View>
                <TextInput
                    placeholder="姓名"
                    value={name}
                    onChangeText={setName}
                />
                <TextInput
                    placeholder="邮箱"
                    value={email}
                    onChangeText={setEmail}
                />
                <TextInput
                    placeholder="密码"
                    value={password}
                    onChangeText={setPassword}
                />
                <Button title="登录" onPress={handleLogin} />
            </View>
        );
    }
    ```
  </Tab>
</Tabs>

#### 社交登录 [#社交登录]

对于社交登录，可以使用 `authClient.signIn.social` 方法，传入提供者名称和回调 URL。如果传入相对路径（如 "/dashboard"），Expo 插件会自动使用 `Linking.createURL` 将其转换为深度链接。

将 auth 后端升级到带用途前缀的验证标识符或用途专属的 state cookie 密钥时，请重新启动待处理的基于重定向的社交登录。请遵循[验证存储](/docs/reference/options#verification)和 [state cookie](/docs/reference/options#storestatestrategy)切换说明。这些格式变化不需要新的 Expo 客户端配置，也不会影响不使用重定向 state 的原生 [ID token 登录](#idtoken-sign-in)。

```tsx title="app/social-sign-in.tsx"
import { Button } from "react-native";
import { router } from "expo-router";
import { authClient } from "@/lib/auth-client";

export default function SocialSignIn() {
    const handleLogin = async () => {
        const { error } = await authClient.signIn.social({
            provider: "google",
            callbackURL: "/dashboard" // [!code highlight]
        })
        if (error) {
            // 处理错误
            return;
        }
        router.replace("/dashboard"); // [!code highlight]
    };
    return <Button title="使用 Google 登录" onPress={handleLogin} />;
}
```

<Callout type="info">
  在原生平台（iOS/Android）上，`signIn.social` 不会自动导航。它完成后请自行处理导航。请注意，[底层浏览器行为因平台而异](https://docs.expo.dev/versions/latest/sdk/webbrowser/#webbrowseropenauthsessionasyncurl-redirecturl-options)。
</Callout>

#### ID Token 登录 [#id-token-登录]

如果希望在移动设备上发起提供者请求，然后在服务器端验证 ID Token，可以使用带有 `idToken` 选项的 `authClient.signIn.social`。

```tsx title="app/social-sign-in.tsx"
import { Button } from "react-native";

export default function SocialSignIn() {
    const handleLogin = async () => {
        await authClient.signIn.social({
            provider: "google", // 目前仅支持 google、apple 和 facebook 的 ID Token 登录
            idToken: {
                token: "...", // 来自提供者的 ID Token
                nonce: "...", // 提供者提供的 nonce（可选）
            }
            callbackURL: "/dashboard" // 在原生平台，这会被转换为深度链接（如 `myapp://dashboard`）
        })
    };
    return <Button title="使用 Google 登录" onPress={handleLogin} />;
}
```

<Accordions>
  <Accordion title="示例：使用 @react-native-google-signin/google-signin 实现 Google 登录">
    本示例展示了如何使用 ID Token 实现 Google 登录。不需要在 Better Auth 中进行额外配置。但是，由于此方法使用原生代码，需要在 Expo 中配置以获取 ID Token。

    按照 [Expo Google 认证指南](https://docs.expo.dev/guides/google-authentication/) 设置 `@react-native-google-signin/google-signin`，然后将 ID Token 传递给 Better Auth：

    ```tsx title="app/sign-in.tsx"
    import {
      GoogleSignin,
      GoogleSigninButton,
      isSuccessResponse,
    } from "@react-native-google-signin/google-signin";
    import { View } from "react-native";
    import { router } from "expo-router";
    import { authClient } from "@/lib/auth-client";

    GoogleSignin.configure({
      webClientId: process.env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID,
      iosClientId: process.env.EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID,
    });

    export default function GoogleSignIn() {
      const handleGoogle = async () => {
        await GoogleSignin.hasPlayServices();
        const response = await GoogleSignin.signIn();

        if (isSuccessResponse(response) && response.data.idToken) {
          const { error } = await authClient.signIn.social({
            provider: "google",
            idToken: { token: response.data.idToken },
          });
          if (!error) {
            router.replace("/dashboard");
          }
        }
      };

      return (
        <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
          <GoogleSigninButton onPress={handleGoogle} />
        </View>
      );
    }
    ```
  </Accordion>
</Accordions>

### 会话管理 [#会话管理]

Better Auth 提供了 `useSession` 钩子，用于在应用中访问当前用户会话。

```tsx title="app/index.tsx"
import { Text } from "react-native";
import { authClient } from "@/lib/auth-client";

export default function Index() {
    const { data: session } = authClient.useSession();

    return <Text>欢迎，{session?.user.name}</Text>;
}
```

在原生平台，会话数据会缓存在 SecureStore 中，避免每次加载应用时出现加载动画。可以通过传入 `disableCache` 选项来禁用此行为。

### 向服务端发送已认证请求 [#向服务端发送已认证请求]

要向需要用户会话的服务端发送已认证请求，需要从 `SecureStore` 获取会话 Cookie 并手动将其添加到请求头中。

```tsx
import { authClient } from "@/lib/auth-client";

const makeAuthenticatedRequest = async () => {
  const cookies = await authClient.getCookie(); // [!code highlight]
  const headers = {
    "Cookie": cookies, // [!code highlight]
  };
  const response = await fetch("http://localhost:8081/api/secure-endpoint", { 
    headers,
    // 设置 'include' 可能会与我们手动设置的 Cookie 冲突
    credentials: "omit" // [!code highlight]
  });
  const data = await response.json();
  return data;
};
```

**示例：与 TRPC 配合使用**

```tsx title="lib/trpc-provider.tsx"
// ...其他导入
import { authClient } from "@/lib/auth-client"; // [!code highlight]

export const api = createTRPCReact<AppRouter>();

export function TRPCProvider(props: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    api.createClient({
      links: [
        httpBatchLink({
          //...your other options
          async headers() {
            const headers = new Map<string, string>(); // [!code highlight]
            const cookies = await authClient.getCookie(); // [!code highlight]
            if (cookies) { // [!code highlight]
              headers.set("Cookie", cookies); // [!code highlight]
            } // [!code highlight]
            return Object.fromEntries(headers); // [!code highlight]
          },
        }),
      ],
    }),
  );

  return (
    <api.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        {props.children}
      </QueryClientProvider>
    </api.Provider>
  );
}
```

## 选项 [#选项]

### Expo 客户端 [#expo-客户端]

**storage**：用于缓存会话数据和 Cookie 的 SecureStore 兼容存储。访问相同存储数据的客户端应复用同一个存储实例。

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";
import * as SecureStore from "expo-secure-store";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    plugins: [
        expoClient({
            storage: SecureStore,
            // ...
        })
    ],
});
```

**scheme**：用于 OAuth 提供者认证后深度链接回调的 scheme。默认情况下，Better Auth 会尝试从 `app.json` 读取。如果需要覆盖，传入该选项即可。

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    plugins: [
        expoClient({
            scheme: "myapp",
            // ...
        }),
    ],
});
```

**disableCache**：默认情况下，客户端会将会话数据缓存到 SecureStore。通过传入 `disableCache` 可以禁用此缓存。

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    plugins: [
        expoClient({
            disableCache: true,
            // ...
        }),
    ],
});
```

**cookiePrefix**：用于识别属于 Better Auth 的服务器 Cookie 名称前缀，避免第三方 Cookie 导致无限重新请求。可以是字符串或字符串数组以匹配多个前缀。默认值为 `"better-auth"`。

```ts title="lib/auth-client.ts"
import { createAuthClient } from "better-auth/react";
import { expoClient } from "@better-auth/expo/client";
import * as SecureStore from "expo-secure-store";

const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    plugins: [
        expoClient({
            storage: SecureStore,
            // 单个前缀
            cookiePrefix: "better-auth"
        })
    ]
});
```

你也可以提供多个前缀以匹配不同认证系统的 Cookie：

```ts title="lib/auth-client.ts"
const authClient = createAuthClient({
    baseURL: "http://localhost:8081",
    plugins: [
        expoClient({
            storage: SecureStore,
            // 多个前缀
            cookiePrefix: ["better-auth", "my-app", "custom-auth"]
        })
    ]
});
```

<Callout type="info">
  **重要提示：** 如果你使用了如 passkey 这类插件，并设置了自定义的 `webAuthnChallengeCookie`，请确保将对应前缀包含在 `cookiePrefix` 数组内。例如若配置了 `webAuthnChallengeCookie: "my-app-passkey"`，则应在 `cookiePrefix` 中包含 `"my-app"`。详情请参考[Passkey 插件文档](/docs/plugins/passkey#expo-集成)。
</Callout>

### Expo 服务端 [#expo-服务端]

**disableOriginOverride**：是否禁用 Expo API Routes 的 Origin 覆盖，默认值为 false。如果你遇到 Expo API Routes 的 CORS Origin 问题，可以启用此配置。

