跳转至

React Native SDK

本指南介绍如何将 Authany 集成到 React Native 应用中。支持的平台包括:

  • React Native 0.60.0 及以上版本

按照本指南,🕐 10 分钟即可将 Authany 添加到您的 React Native 应用。

说明

本教程示例应用的完整代码,请参见 这个 GitHub 仓库。

说明

React Native 自 0.68 版本起,支持选择性启用 New Architecture。由于 New Architecture 目前仍被认为不稳定,我们暂不支持该架构。

React Native 视频指南

在 Authany 中设置应用

在 https://manage.authany.com/ 注册 Authany 管理台账号。您也可以使用自建部署的 Authany。

在项目列表中,创建一个新项目或选择一个已有项目。接下来,我们需要在该项目中创建一个应用。

步骤 1:在 Authany 管理台创建应用

在左侧菜单栏中,进入 应用。

Authany 导航到应用页面

您会看到“新建应用”页面,或者在顶部工具栏中点击 ⊕添加应用。

输入应用名称,并将应用类型选择为 原生应用。点击“保存”。

进入下一个界面后,您会看到一系列可以帮助您完成设置的指南,点击“下一步”继续。

创建应用

步骤 2:配置应用

在您的 IDE 中,定义一个自定义 URI scheme,用于在用户完成 Authany 身份验证后将其重定向回您的应用。例如,在我们的示例应用中,将定义以下 URI scheme:

com.authgear.example.rn://host/path

关于在 React Native 中设置自定义 URI scheme 的更多说明,请参见 https://reactnative.dev/docs/linking

现在返回 Authany 管理台,将您定义的 URI(本例中为 com.authgear.example.rn://host/path)添加为 授权的重定向 URI。

点击“保存”,并记下 Authany 客户端应用的 客户端 ID(Client ID) 和 端点(Endpoint)。您之后也可以在 应用 列表中重新获取这些信息。

设置重定向 URI

使用 Authany SDK 为 React Native 应用添加用户身份验证

在本节中,我们将逐步介绍如何创建一个新的 React Native 应用,并使用 Authany SDK 为该应用添加用户身份验证。

步骤 1:创建 React Native 应用

运行以下命令,创建一个新的 React Native 项目:

npx @react-native-community/cli init myapp
cd myapp

关于如何创建项目并搭建 React Native 开发环境的详细指南,请参见 React Native 官方文档。

步骤 2:安装 SDK

在 React Native 项目的根目录下运行以下命令,安装 Authany SDK:

npm install --exact @authgear/react-native
(cd ios && pod install)

步骤 3:初始化 Authany

在这一步中,我们将编写代码来初始化一个 Authany SDK 实例,用于与前面创建的 Authany 客户端应用进行交互。

首先,打开项目中的 App.tsx 文件,在顶部添加以下 import 语句:

import React, { useCallback, useEffect, useMemo, useState } from 'react';
import authgear, { Page, ReactNativeContainer, SessionState, SessionStateChangeReason } from "@authgear/react-native";

在 App.tsx 的 App() 函数内部顶部添加以下代码,配置一个新的 Authany 实例,并设置一个 delegate,帮助应用了解用户当前的会话状态(是否已登录):

const [sessionState, setSessionState] = useState<SessionState | null>(() => {
  return authgear.sessionState;
});

const loggedIn = sessionState === "AUTHENTICATED";
const delegate = useMemo(() => {
  const d = {
    onSessionStateChange: (
      container: ReactNativeContainer,
      _reason: SessionStateChangeReason
    ) => {
      setSessionState(container.sessionState);
    },
    sendWechatAuthRequest: () => {},
  };
  return d;
}, [setSessionState]);

useEffect(() => {
  authgear.delegate = delegate;

  return () => {
      authgear.delegate = undefined;
  };
}, [delegate]);

const postConfigure = useCallback(async () => {
  const sessionState = authgear.sessionState;

  // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
  if (sessionState === "AUTHENTICATED") {
    await authgear.fetchUserInfo();
  }
}, []);

useEffect(() => {
  const configure = async () => {
    try {
      await authgear
      .configure({
        clientID: "<CLIENT_ID>",
        endpoint: "<AUTHGEAR_ENDPOINT>",
      });
      await postConfigure();
    } catch (error) {
      console.log("Error:" + error);
    }
  };

  configure();
}, [postConfigure]);

将 <CLIENT_ID> 和 <AUTHGEAR_ENDPOINT> 替换为您 Authany 客户端应用的客户端 ID 和端点。

上面的代码包含一个 postConfigure() 方法,用于获取此前已登录用户的真实会话状态。

步骤 4:添加登录按钮

将 App.tsx 文件中 App() 函数里 return 语句的内容替换为以下代码:

return (
  <SafeAreaView>
    <StatusBar />
      <View style={{justifyContent: 'center', alignItems: 'center'}}>
      {!loggedIn ?
        <View>
          <Text style={{paddingTop: 50, paddingBottom: 16, fontSize: 40}}>Welcome</Text>
          <Button onPress={authenticate} title="Login" />
        </View> :
        <View>
          <Text style={{paddingTop: 50, paddingBottom: 16}}>Welcome User</Text>
        </View>
      }
      </View>
  </SafeAreaView>
);

在 App.tsx 顶部导入所需的组件:

import {
  Alert,
  Button,
  SafeAreaView,
  StatusBar,
  Text,
  View,
} from 'react-native';

步骤 5:启动身份验证流程

在这一步中,您将实现一个 authenticate() 方法,用于调用 Authany SDK 的 authenticate() 方法。前面添加的登录按钮会调用这个 authenticate() 方法来启动身份验证流程。

在步骤 3 中 configure() 方法的 useEffect() 之后,为 App() 函数添加以下代码:

const authenticate = useCallback(async () => {
  try {
    authgear
        .authenticate({
          redirectURI: "com.authgear.example.rn://host/path",
        });
  } catch (error) {
    console.log("Authentication Error:" + error);
  }
}, []);

检查点

此时,App.tsx 中的完整代码应如下所示:

import React, {useCallback, useEffect, useMemo, useState} from 'react';
import authgear, {
  Page,
  ReactNativeContainer,
  SessionState,
  SessionStateChangeReason,
} from '@authgear/react-native';

import {Alert, Button, SafeAreaView, StatusBar, Text, View} from 'react-native';

function App(): React.JSX.Element {
  const [sessionState, setSessionState] = useState<SessionState | null>(() => {
    return authgear.sessionState;
  });

  const loggedIn = sessionState === 'AUTHENTICATED';
  const delegate = useMemo(() => {
    const d = {
      onSessionStateChange: (
        container: ReactNativeContainer,
        _reason: SessionStateChangeReason,
      ) => {
        setSessionState(container.sessionState);
      },
      sendWechatAuthRequest: () => {},
    };
    return d;
  }, [setSessionState]);

  useEffect(() => {
    authgear.delegate = delegate;

    return () => {
      authgear.delegate = undefined;
    };
  }, [delegate]);

  const postConfigure = useCallback(async () => {
    const sessionState = authgear.sessionState;

    // if user has an existing session, call SDK fetchUserInfo method to get the user's info and refresh access token when necessary
    if (sessionState === 'AUTHENTICATED') {
      await authgear.fetchUserInfo();
    }
  }, []);

  useEffect(() => {
    const configure = async () => {
      try {
        await authgear.configure({
          clientID: '<CLIENT_ID>',
          endpoint: '<AUTHGEAR_ENDPOINT>',
        });
        await postConfigure();
      } catch (error) {
        console.log('Error:' + error);
      }
    };

    configure();
  }, [postConfigure]);

  const authenticate = useCallback(async () => {
    try {
      authgear.authenticate({
        redirectURI: 'com.authgear.example.rn://host/path',
      });
    } catch (error) {
      console.log('Authentication Error:' + error);
    }
  }, []);

  return (
    <SafeAreaView>
      <StatusBar />
      <View style={{justifyContent: 'center', alignItems: 'center'}}>
        {!loggedIn ? (
          <View>
            <Text style={{paddingTop: 50, paddingBottom: 16, fontSize: 40}}>
              Welcome
            </Text>
            <Button onPress={authenticate} title="Login" />
          </View>
        ) : (
          <View>
            <Text style={{paddingTop: 50, paddingBottom: 16}}>
              Welcome User
            </Text>
          </View>
        )}
      </View>
    </SafeAreaView>
  );
}

export default App;

现在保存代码,并使用以下任一命令,尝试在 Android 或 iOS 上运行应用:

Android

npm run android

iOS

npm run ios

应用打开后,点击登录按钮,您应该会被重定向到身份验证界面。不过,由于尚未处理重定向 URI,此时无法完成身份验证。

步骤 6:设置重定向 URI

为完成集成,需要设置应用来处理 Authany 客户端应用中指定的重定向 URI。这部分需要针对具体平台进行集成。

Android

在 React Native 项目的 android/app/src/main/AndroidManifest.xml 中添加以下 activity 条目。intent 系统会将重定向 URI 分发给 OAuthRedirectActivity,之后的处理由 SDK 完成。

<!-- Your application configuration. Omitted here for brevity -->
<application>
  <!-- Other activities or entries -->

  <!-- Add the following activity -->
  <!-- android:exported="true" is required -->
  <!-- See https://developer.android.com/about/versions/12/behavior-changes-12#exported -->
  <activity android:name="com.authgear.reactnative.OAuthRedirectActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!-- Configure data to be the exact redirect URI your app uses. -->
                <!-- Here, we are using com.authgear.example://host/path as configured in the portal -->
                <!-- NOTE: The redirectURI supplied in AuthenticateOptions *has* to match as well -->
                <data android:scheme="com.authgear.example.rn"
                    android:host="host"
                    android:pathPrefix="/path"/>
            </intent-filter>
  </activity>
</application>

您还需要在 AndroidManifest.xml 中添加一个 queries 部分。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <!-- Other elements such <application> -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
</manifest>

iOS

在 Info.plist 中声明 URL 处理

在 ios/<your_project>/Info.plist 中,添加匹配的重定向 URI。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
      <!-- Other entries -->
      <key>CFBundleURLTypes</key>
      <array>
              <dict>
                      <key>CFBundleTypeRole</key>
                      <string>Editor</string>
                      <key>CFBundleURLSchemes</key>
                      <array>
                              <string>com.authgear.example.rn</string>
                      </array>
              </dict>
      </array>
</dict>
</plist>
可选:处理微信登录的深层链接 如果不需要支持“微信登录”,可以跳过这部分。 或者,也可以使用任意常用的深层链接库,然后在 React Native 应用的 JavaScript 一侧编写代码,将深层链接转发给我们的 SDK。 要处理微信深层链接,请在 `AppDelegate.m` 中添加以下代码片段:
// Other imports...
#import <authgear-react-native/AGAuthgearReactNative.h>

// Other methods...

// For handling deeplink
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:
                (NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
    return [AGAuthgearReactNative application:app openURL:url options:options];
}

// For handling deeplink
// deprecated, for supporting older devices (iOS < 9.0)
- (BOOL)application:(UIApplication *)application
              openURL:(NSURL *)url
    sourceApplication:(NSString *)sourceApplication
          annotation:(id)annotation {
    return [AGAuthgearReactNative application:application
                                      openURL:url
                            sourceApplication:sourceApplication
                                  annotation:annotation];
}

// for handling universal link
- (BOOL)application:(UIApplication *)application
    continueUserActivity:(NSUserActivity *)userActivity
      restorationHandler:
          (void (^)(NSArray<id<UIUserActivityRestoring>> *_Nullable))
              restorationHandler {
    return [AGAuthgearReactNative application:application
                        continueUserActivity:userActivity
                          restorationHandler:restorationHandler];
}

步骤 7:登出

现在,让我们为示例应用添加登出功能,让用户可以登出并结束会话。

我们将添加一个登出按钮,并实现一个 logout() 方法,用于调用 Authany SDK 对应的 logout() 方法:

在前面添加的 authenticate() 方法之后,为 App() 函数添加一个 logout() 方法:

const logout = useCallback( () => {
    try {
      authgear
      .logout();
    } catch (error) {
      console.log("Error:" + error);
    }
}, []);

接下来,添加登出按钮,并在 onPress 中调用 logout() 方法。将该按钮添加到 App() 的 return 语句中,紧跟在 <Text style={{paddingTop: 50, paddingBottom: 16}}> Welcome User</Text> 下方:

<Button onPress={logout} title="Logout" />

保存代码并运行应用。点击登出按钮,用户应该会被登出。

步骤 8:显示用户信息

Authany SDK 包含一个 fetchUserInfo() 方法,可以返回当前用户的 User ID、邮箱、手机号等信息。

在这一步中,我们将为应用添加一个 Show User Info 按钮。点击该按钮会调用 fetchUserInfo(),演示该方法的用法。

在 logout() 方法下方,为 App() 函数添加一个 showUserInfo() 方法:

const showUserInfo = useCallback(async () => {
  try {
    const userInfo = await authgear.fetchUserInfo();
    Alert.alert('User Info', JSON.stringify(userInfo, null, 2));
  } catch (error) {
    console.log('Error:' + error);
  }
}, []);

showUserInfo() 方法会调用 Authany SDK 的 fetchUserInfo() 方法,并将返回的数据显示在一个 Alert 中。

接下来,在 App() 的 return 语句中,将 Show User Info 按钮添加到上一步登出按钮的下方:

<View style={{height: 8}} />
<Button onPress={showUserInfo} title="Show User Info" />

步骤 9:打开用户设置页面

Authany 提供了一个预置的用户设置页面,供应用的用户查看和修改自己的资料及安全信息。

在这一步中,我们将使用 Authany SDK 的 open() 方法,在已登录用户点击按钮时打开这个用户设置页面。

在上一步 showUserInfo() 方法下方,为 App() 函数添加一个 openSettings() 方法:

const openSettings = useCallback(() => {
  try {
    authgear.open(Page.Settings);
  } catch (error) {
    console.log('Error:' + error);
  }
}, []);

接下来,在 App() 的 return 语句中,将 User Settings 按钮添加到 Show User Info 按钮下方:

<View style={{height: 8}} />
<Button onPress={openSettings} title="User Settings" />

现在运行应用,点击 User Settings 按钮,您应该就能访问用户设置页面了。

其他操作

获取登录状态

启动应用时,您可能需要知道用户是否已登录(例如,如果用户尚未登录,则显示登录页面)。

sessionState 反映的是用户在 SDK 本地状态中的登录状态。这意味着即使 sessionState 为 AUTHENTICATED,如果会话已在远程被撤销,该会话也可能已失效。因此,在初始化 Authany SDK 后,应在合适的时机调用 fetchUserInfo 来更新 sessionState。我们在示例应用中通过 postConfigure() 方法演示了这一点。

// After authgear.configure, it only reflect SDK local state.
// value can be NO_SESSION or AUTHENTICATED
let sessionState = authgear.sessionState;

if (sessionState === "AUTHENTICATED") {
    authgear
        .fetchUserInfo()
        .then((userInfo) => {
            // sessionState is now up to date
        })
        .catch((e) => {
            // sessionState is now up to date
            // it will change to NO_SESSION if the session is invalid
        });
}

sessionState 的取值可以是 UNKNOWN、NO_SESSION 或 AUTHENTICATED。初始时,sessionState 为 UNKNOWN。调用 authgear.configure() 之后,如果找到此前的会话,则会话状态变为 AUTHENTICATED;如果没有找到,则变为 NO_SESSION。

在 HTTP 请求中使用访问令牌

要在发往应用服务器的 HTTP 请求中携带访问令牌,有两种方式可以实现。

方式一:使用 Authany SDK 提供的 fetch 函数

Authany SDK 提供了 fetch 函数,供您调用应用服务器。fetch 函数会在应用请求中自动携带 Authorization 请求头,并自动处理访问令牌的刷新。authgear.fetch 实现了 fetch 规范。

authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));

方式二:将访问令牌添加到您的 HTTP 请求中

您可以通过 authgear.accessToken 获取访问令牌。每次使用访问令牌前,都应先调用 refreshAccessTokenIfNeeded()。refreshAccessTokenIfNeeded() 函数只有在访问令牌已过期时,才会发起网络请求进行刷新。

在应用请求的 Authorization 请求头中携带访问令牌。

authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });

后续步骤

为了保护您的应用服务器免受未经授权的访问,您需要将 Authany 集成到后端。

后端/API 集成

JavaScript SDK 参考

关于 JavaScript React Native SDK 的详细文档,请访问 @authgear/react-native 参考文档