oauth0 oauth2
In this tutorial, we will set up a React Native application to work with OAuth. We’ll use FusionAuth for auth, but the React Native code should work with any OAuth compliant server.
在本教程中,我们将设置一个可用于OAuth的React Native应用程序。 我们将使用FusionAuth进行身份验证,但React Native代码应可与任何符合OAuth的服务器一起使用。
First, we’ll be installing and configuring FusionAuth. Then we’ll set up a React Native project. We’ll then perform an Authorization Code grant from within the React Native app. Finally, we’ll request information from an OIDC endpoint. With that data, we’ll display the user’s email in the React Native application.
首先,我们将安装和配置FusionAuth。 然后,我们将建立一个React Native项目。 然后,我们将在React Native应用程序中执行授权代码授予。 最后,我们将从OIDC端点请求信息。 有了这些数据,我们将在React Native应用程序中显示用户的电子邮件。
This is a lot, but the ride will be fun. Ready to get going?
这很多,但是旅程会很有趣。 准备出发了吗?
Here’s what you need to get started:
这是您需要开始的:
NodeJS version >= 8.0 NodeJS版本> = 8.0 VScode or any other text editor VScode或任何其他文本编辑器git
git
npx
npx
Xcode, if building for iOS Xcode(如果针对iOS构建) Homebrew (optional) 自酿(可选)If you are a web developer, you may be familiar with OAuth. With web development, we have three players:
如果您是网络开发人员,则可能熟悉OAuth。 通过网络开发,我们拥有三个参与者:
The browser -> The server -> The OAuth serverThe browser talks to the server, which talks to the OAuth server. The OAuth server generates access tokens which are given to the server. The server stores them securely in the session, and when needed, passes them to other APIs for authorization. This is the architecture we used when securing a React application with OAuth.
浏览器与服务器对话,后者与OAuth服务器对话。 OAuth服务器生成分配给服务器的访问令牌。 服务器将它们安全地存储在会话中,并在需要时将它们传递给其他API进行授权。 这是我们使用OAuth保护React应用程序时使用的架构。
However with a mobile device, things change a bit. A corresponding scenario might be like this:
但是,使用移动设备时,情况会有所变化。 相应的情况可能是这样的:
The mobile device -> The server -> The OAuth serverHowever, this architecture can be simplified. The server can be removed; the mobile device can handle the callbacks directly from the OAuth server. In this tutorial, we’ll use the Authorization Code grant with the PKCE extension. Below is a suggested flow from RFC 8252, and this is what we’ll be implementing.
但是,可以简化此体系结构。 可以卸下服务器; 移动设备可以直接从OAuth服务器处理回调。 在本教程中,我们将使用带有PKCE扩展名的授权代码授予。 以下是RFC 8252的建议流程,这就是我们将要实现的流程。
Next up, let’s configure the OAuth server and set up our coding environment.
接下来,让我们配置OAuth服务器并设置我们的编码环境。
In order to set up FusionAuth, follow the 5-minute setup guide. It is simple and quick. By default, the OAuth server will run at the address http://localhost:9011.
为了设置FusionAuth,请按照5分钟的设置指南进行操作。 简单,快捷。 默认情况下,OAuth服务器将在地址http://localhost:9011 。
In this step, we are going to configure a FusionAuth application. This is different from the FusionAuth server instance or the React Native application. In FusionAuth, an application is anything a user might log in to. To configure this, sign into the FusionAuth administrative interface and navigate to “Applications”. From there, create a new application.
在此步骤中,我们将配置FusionAuth应用程序。 这与FusionAuth服务器实例或React Native应用程序不同。 在FusionAuth中,应用程序是用户可以登录的任何东西。 要进行配置,请登录FusionAuth管理界面并导航至“应用程序”。 从那里创建一个新的应用程序。
Once you’ve done that, navigate to the “OAuth” tab and add in a redirect URI of fusionauth-demo:/oauthredirect. We’ll use this redirect URL in our React Native application later.
完成此操作后,导航至“ OAuth”选项卡,并添加fusionauth-demo:/oauthredirect的重定向URI。 稍后,我们将在React Native应用程序中使用此重定向URL。
Also, note the value of “Client Id”; we’ll need that later too. Click Save. When properly configured, the application details screen should look like this:
另外,请注意“客户ID”的值; 我们稍后也需要。 点击保存。 正确配置后,应用程序详细信息屏幕应如下所示:
Make sure to register your user to the new application. Doing so creates a relationship between a user and the newly created application.
确保将您的用户注册到新应用程序。 这样做会在用户和新创建的应用程序之间建立关系。
If you want, you can add more users in the “Users” tab, but make sure to register them with your new application. Now, we move on to setting up the React Native project.
如果需要,可以在“用户”选项卡中添加更多用户,但请确保将其注册到新应用程序中。 现在,我们继续设置React Native项目。
Since we are going to use the React Native command line interface (CLI) for development, we must have the React Native development environment installed. For installation instructions, please follow the official documentation. You’ll also want to make sure you select react-native-cli rather than expo. These instructions also walk you through starting your application, so if you are new to React Native, make sure you give them a read.
由于我们将使用React Native命令行界面(CLI)进行开发,因此我们必须安装React Native开发环境。 有关安装说明,请遵循官方文档。 您还需要确保选择选择react-native-cli而不是expo 。 这些说明还会引导您启动应用程序,因此,如果您是React Native的新手,请确保您已阅读它们。
We also need to install development environments for iOS, Android, or both. We are going to use brew to install needed packages as well. If you are following along, make sure that brew is installed, or install the packages in a different way.
我们还需要安装适用于iOS和/或Android的开发环境。 我们还将使用brew安装所需的软件包。 如果要继续,请确保已安装brew ,或以其他方式安装软件包。
First, we’ll install watchman, which is used to automatically rebuild files when they change:
首先,我们将安装watchman,它用于在文件更改时自动重建文件:
brew install watchmanThen we need to install the Xcode CLI tools, which are not normally present and can’t be done with brew. To install, open Xcode and navigate to “Preferences” and then “Locations”. Pick the Xcode version for command-line tools as shown in the screenshot below:
然后,我们需要安装Xcode CLI工具,这些工具通常不存在,并且无法通过brew完成。 要安装,请打开Xcode并导航至“首选项”,然后导航至“位置”。 选择命令行工具的Xcode版本,如下面的屏幕快照所示:
iOS is ready to go.
iOS已准备就绪。
For Android, JDK 8 is required, as other versions may result in errors. We can download this version from the Oracle website or using brew as shown in the snippet below:
对于Android,需要JDK 8,因为其他版本可能会导致错误。 我们可以从Oracle网站下载此版本,也可以使用如下brew所示的brew :
brew cask install adoptopenjdk/openjdk/adoptopenjdk8Next, we need to download and install the Android studio.
接下来,我们需要下载并安装Android studio 。
Then, we need to configure the ANDROID_HOME environment variable in our system path. We can add the following lines to our $HOME/.bash_profile or $HOME/.bashrc. If you are using zsh, the files are ~/.zprofile or ~/.zshrc.
然后,我们需要在系统路径中配置ANDROID_HOME环境变量。 我们可以$HOME/.bash_profile添加到$HOME/.bash_profile或$HOME/.bashrc 。 如果使用zsh,则文件为~/.zprofile或~/.zshrc ~/.zprofile 。
export ANDROID_HOME=$HOME/Library/Android/sdkexport PATH=$PATH:$ANDROID_HOME/emulatorexport PATH=$PATH:$ANDROID_HOME/toolsexport PATH=$PATH:$ANDROID_HOME/tools/binexport PATH=$PATH:$ANDROID_HOME/platform-toolsNow the setup for the Android platform is done.
现在,Android平台的设置已完成。
We are now going to create a new React Native project. First, create a directory to contain all of our code, then cd to that directory. Pick an application name; we chose RNfusionauth and will use that name throughout the tutorial.
现在,我们将创建一个新的React Native项目。 首先,创建一个包含我们所有代码的目录,然后使用cd进入该目录。 选择一个应用程序名称; 我们选择了RNfusionauth ,并将在整个教程中使用该名称。
Run this command to create the basic project files and configuration:
运行以下命令以创建基本项目文件和配置:
react-native init RNfusionauthWe’ll be making additional changes to these files as we build out the application.
在构建应用程序时,我们将对这些文件进行其他更改。
A key dependency of our application is the react-native-app-auth package. This sets up a bridge between the AppAuth-iOS and AppAuth-Android SDKs for communicating with OAuth 2.0 and OpenID Connect providers.
我们应用程序的关键依赖项是react-native-app-auth软件包。 这将在AppAuth-iOS和AppAuth-Android SDK之间架起一座桥梁,以与OAuth 2.0和OpenID Connect提供程序进行通信。
This library should support any server that implements the OAuth2 spec, as FusionAuth does.
该库应该支持任何实施OAuth2规范的服务器,就像FusionAuth一样。
This package supports the Authorization Code grant and enables the PKCE extension by default. This is important because a mobile device is not a “confidential client” and we want to ensure malicious actors can’t intercept our authorization code.
该软件包支持授权码授予,并且默认情况下启用PKCE扩展。 这很重要,因为移动设备不是“机密客户端”,并且我们希望确保恶意行为者无法截获我们的授权代码。
To install react-native-app-auth, run the following in the project directory:
要安装react-native-app-auth ,请在项目目录中运行以下命令:
yarn add react-native-app-authUsing this library will help us build the OAuth integration quickly and securely. It takes care of many of the steps specified by RFC 8252; we just have to make sure to kick off the process (step 1) and receive and store the access token (step 6). As a reminder, here’s the diagram from the RFC:
使用该库将有助于我们快速安全地建立OAuth集成。 它负责RFC 8252指定的许多步骤。 我们只需要确保启动该过程(步骤1)并接收并存储访问令牌(步骤6)即可。 提醒一下,这是RFC中的图表:
Now, we’ll configure auth for an iOS build of the React Native app. The basics will be covered below, but if you want to learn more about other options, check out the docs.
现在,我们将为React Native应用程序的iOS版本配置auth。 基本知识将在下面介绍,但是如果您想了解更多有关其他选项的信息,请查看docs 。
First, we need to install the cacao pod by running the command shown below:
首先,我们需要通过运行以下命令安装可可豆荚:
cd ios ; pod installThen, we need to open the React Native project with Xcode. Edit the info.plist file and register the redirect URL scheme as shown in the code snippet below:
然后,我们需要使用Xcode打开React Native项目。 编辑info.plist文件并注册重定向URL方案,如下面的代码片段所示:
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string>com.your.app.identifier</string> <key>CFBundleURLSchemes</key> <array> <string>fusionauth.demo</string> </array> </dict> </array>Here, the CFBundleURLSchemes defines the URL schemes handled by this application. The scheme we are registering, fusionauth.demo, should look familiar, as we configured FusionAuth to redirect to a URL with that scheme in it. If you modify it here, you should modify it there as well.
在此, CFBundleURLSchemes定义了此应用程序处理的URL方案。 我们正在注册的方案fusionauth.demo应该看起来很熟悉,因为我们将FusionAuth配置为重定向到其中包含该方案的URL。 如果您在此处进行修改,则也应该在此处进行修改。
The last step is to change the AppDelegate.h file to include needed imports and properties:
最后一步是更改AppDelegate.h文件以包括所需的导入和属性:
#import <React/RCTBridgeDelegate.h>#import <UIKit/UIKit.h>#import "RNAppAuthAuthorizationFlowManager.h"@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RNAppAuthAuthorizationFlowManager>@property (nonatomic, strong) UIWindow *window;@property(nonatomic, weak)id<RNAppAuthAuthorizationFlowManagerDelegate>authorizationFlowManagerDelegate;@endFor Android, we need additional configuration to capture the authorization redirect. Add the following property to the defaultConfig object in the android/app/build.gradle file:
对于Android,我们需要其他配置以捕获授权重定向。 将以下属性添加到android/app/build.gradle文件中的defaultConfig对象:
android { defaultConfig { manifestPlaceholders = [ appAuthRedirectScheme: 'fusionauth.demo' ] }}Here, the appAuthRedirectScheme, fusionauth.demo, is another scheme definition, the same as with iOS. If you modify it here, you should modify it in the FusionAuth administrative interface and in the iOS configuration as well.
在这里, appAuthRedirectScheme , fusionauth.demo是另一个方案定义,与iOS相同。 如果您在此处进行修改,则应在FusionAuth管理界面和iOS配置中进行修改。
However, a new issue pops up when we start working on the Android application. Developing and debugging an Android app on a Mac is difficult as the emulator is not fully supported. Among other issues, the emulator is slow when compared to the iOS emulator.
但是,当我们开始使用Android应用程序时,会弹出一个新问题。 由于不完全支持模拟器,因此很难在Mac上开发和调试Android应用。 与iOS模拟器相比,该模拟器速度较慢。
A better solution is to use an actual Android mobile device. When you are doing so, how can you connect the FusionAuth server, running on localhost, to the device, which is on a wifi or cell network? The solution is to use a local tunnel service such as ngrok.
更好的解决方案是使用实际的Android移动设备。 这样做时,如何将运行在localhost上的FusionAuth服务器连接到wifi或蜂窝网络上的设备? 解决方案是使用本地隧道服务,例如ngrok。
ngrok enables us to proxy between local services and the internet. You’ll want to download it, then install it. Connect your account, by running the following command. It will add our auth token to the default ngrok.yml file, which will grant us access to more features and longer session times.
ngrok使我们能够在本地服务和Internet之间进行代理。 您将要下载它,然后安装它。 通过运行以下命令来连接您的帐户。 它将我们的身份验证令牌添加到默认的ngrok.yml文件中,这将授予我们访问更多功能和更长会话时间的权限。
./ngrok authtoken Your keyRunning tunnels will be listed on the status page of the ngrok dashboard.
正在运行的隧道将列在ngrok仪表板的状态页面上。
Now we want to start HTTP tunnel forwarding to localhost and port 9011, where FusionAuth is listening. Run the following command to launch the ngrok proxy:
现在,我们要开始将HTTP隧道转发到FusionAuth正在监听的localhost和端口9011 。 运行以下命令以启动ngrok代理:
./ngrok http 9011We’ll get a random URL which forwards traffic to our FusionAuth instance. It’ll be something like https://ce2f267ff5a5.ngrok.io. Using this, we can configure our Android device to communicate with the local instance of FusionAuth. We will also use this URL for our iOS app for consistency’s sake, even though the iOS emulator can connect to localhost without a tunnel.
我们将获得一个随机URL,它将流量转发到我们的FusionAuth实例。 就像https://ce2f267ff5a5.ngrok.io一样。 使用此工具,我们可以将Android设备配置为与FusionAuth的本地实例进行通信。 为了保持一致性,我们还将在iOS应用程序中使用此URL,即使iOS仿真器无需隧道即可连接到localhost。
Now, we can move on to coding.
现在,我们可以继续进行编码。
Finally, the code! If you want to skip ahead, grab the Apache2 licensed code from the GitHub repository.
最后,代码! 如果要跳过,请从GitHub存储库中获取Apache2许可的代码。
Big picture, we’re going to be building out our logic and views in the App.js file. For a bigger project, you’d split this code up into components, but for our tutorial having one file will make things easier. We’ll use libraries to manage authorization and secure storage of our data, however.
大图,我们将在App.js文件中建立逻辑和视图。 对于更大的项目,您可以将此代码拆分为多个组件,但是对于我们的教程而言,只有一个文件将使事情变得更容易。 但是,我们将使用库来管理授权和数据的安全存储。
Here’s what App.js will look like when we are done (don’t worry, it looks like a lot, but we’ll explain most of it):
这是完成后App.js外观(不用担心,它看起来很多,但是我们将解释其中的大部分内容):
import React, { useState, useCallback, useMemo } from 'react';import { Alert, StyleSheet, View, Image, Text, TouchableOpacity } from 'react-native';import { authorize, prefetchConfiguration } from 'react-native-app-auth';import * as Keychain from 'react-native-keychain';const defaultAuthState = { hasLoggedInOnce: false, provider: '', accessToken: '', accessTokenExpirationDate: '', refreshToken: ''};export default () => { const [authState, setAuthState] = useState(defaultAuthState); const [userinfo, setuserinfo] = useState(null) React.useEffect(() => { prefetchConfiguration({ warmAndPrefetchChrome: true, ...configs.fusionauth }); }, []); const configs = { fusionauth: { issuer: 'https://ce25267ff5a5.ngrok.io', clientId: '253eb7aa-687a-4bf3-b12b-26baa40eecbf', redirectUrl: 'fusionauth.demo:/oauthredirect', additionalParameters: {}, scopes: ['offline_access'], } } const getAccesstoken = async () => { try { // Retrieve the credentials const credentials = await Keychain.getGenericPassword(); if (credentials) { return credentials.password } else { console.log('No credentials stored'); } } catch (error) { console.log("Keychain couldn't be accessed!", error); } } const getUser = async () => { try { const access_token = await getAccesstoken(); if (access_token !== null) { fetch(configs.fusionauth.issuer + "/oauth2/userinfo", { method: "GET", headers: { Authorization: "Bearer " + access_token, }, }) .then((response) => response.json()) .then((json) => { console.log(json); setuserinfo(json); }) .catch((error) => { console.error(error); }); } } catch (e) { console.log(e); } }; const handleAuthorize = useCallback( async () => { try { const newAuthState = await authorize(configs.fusionauth); console.log(newAuthState) setAuthState({ hasLoggedInOnce: true, ...newAuthState }); await Keychain.setGenericPassword('accessToken', newAuthState.accessToken); } catch (error) { Alert.alert('Failed to log in', error.message); } }, [authState] ); return ( <View style={styles.container}> <Image source={require('./fusionauth.png')} /> {authState.accessToken ? ( <TouchableOpacity style={styles.button} onPress={() => getUser()} > <Text style={styles.buttonText}>Get User</Text> </TouchableOpacity> ) : (<TouchableOpacity style={styles.button} onPress={() => handleAuthorize()} > <Text style={styles.buttonText}>Login with FusionAuth</Text> </TouchableOpacity>)} {userinfo ? ( <View style={styles.userInfo}> <View> <Text style={styles.userInfoText}> Username:{userinfo.given_name} </Text> <Text style={styles.userInfoText}></Text> <Text style={styles.userInfoText}>Email:{userinfo.email}</Text> <Text style={styles.userInfoText}></Text> </View> </View> ) : ( <View></View> )} </View> );}const styles = StyleSheet.create({ container: { flexDirection: "column", backgroundColor: "grey", flex: 1, alignItems: "center", justifyContent: "space-evenly", }, button: { backgroundColor: "#f58321", padding: 20 }, buttonText: { color: "#000", fontSize: 20, }, userInfo: { height: 300, width: 300, alignItems: "center", }, userInfoText: { color: "#fff", fontSize: 18, }, errorText: { color: "#fff", fontSize: 18, }, profileImage: { height: 64, width: 64, marginBottom: 32, },});First, we need to add necessary imports to App.js:
首先,我们需要向App.js添加必要的导入:
//...import React, { useState, useCallback, useMemo } from 'react';import { Alert } from 'react-native';import { authorize, refresh, revoke, prefetchConfiguration } from 'react-native-app-auth';//...Next, we need to create a configs object. This will contain details such as the application’s client id:
接下来,我们需要创建一个configs对象。 这将包含详细信息,例如应用程序的客户端ID:
//...const configs = { fusionauth: { issuer: 'https://ce25267ff5a5.ngrok.io', clientId: '253eb7aa-687a-4bf3-b12b-26baa40eecbf', redirectUrl: 'fusionauth.demo:/oauthredirect', additionalParameters: {}, scopes: ['offline_access'], }}//...More on the configuration parameters, as this is something you’ll need to change in your code. The issuer is the URL for the FusionAuth server; you can see that we’ve set it to our ngrok URL. The clientId is the ID that we grabbed from the FusionAuth administrative user interface.
有关配置参数的更多信息,因为您需要在代码中对其进行更改。 issuer是FusionAuth服务器的URL。 您会看到我们已将其设置为ngrok URL。 clientId是我们从FusionAuth管理用户界面中获取的ID。
The redirectUrl is the URL that we set up in the FusionAuth application, with the scheme we used in configuring iOS and Android. The value oauthredirect is a callback path defined by the react native app auth library. Make sure you update the issuer and clientId keys in this object with your configuration values.
redirectUrl是我们在FusionAuth应用程序中设置的网址,其中包含我们在配置iOS和Android中使用的方案。 值oauthredirect是由react native应用程序auth库定义的回调路径。 确保使用配置值更新此对象中的issuer和clientId密钥。
We can also add any additional parameters (none, in this case). If you need custom scopes, this is the place to add them as well. We’re requesting the offline_access scope so that the OAuth server will return a refresh_token. Such a token can be used to request additional access tokens should our current one expire.
我们还可以添加任何其他参数(在本例中为无)。 如果需要自定义范围,也可以在此处添加它们。 我们正在请求offline_access范围,以便OAuth服务器将返回refresh_token 。 如果我们当前的令牌过期,则可以使用此类令牌来请求其他访问令牌。
Next, create a default auth state object in the file. This will be modified as our user first views the React Native app, then authenticates. This contains information like the token values and expiration dates.
接下来,在文件中创建默认的身份验证状态对象。 这将在我们的用户首先查看React Native应用然后进行身份验证时进行修改。 其中包含诸如令牌值和到期日期之类的信息。
//...const defaultAuthState = { hasLoggedInOnce: false, provider: '', accessToken: '', accessTokenExpirationDate: '', refreshToken: ''};const [authState, setAuthState] = useState(defaultAuthState);//...Now, we are ready to configure the code which receives the token.
现在,我们准备配置接收令牌的代码。
Let’s create the function to get the token; this will use the previously created configs object. It will also use the authorize function from the react-native-app-auth package. It will do all the heavy lifting and connect with the OAuth server. The implementation of the function is below:
让我们创建获取令牌的函数; 这将使用先前创建的configs对象。 它还将使用react-native-app-auth包中的authorize函数。 它将完成所有繁重的工作,并与OAuth服务器连接。 该功能的实现如下:
//...const handleAuthorize = useCallback( async provider => { try { const newAuthState = await authorize(configs.fusionauth); setAuthState({ hasLoggedInOnce: true, ...newAuthState }); } catch (error) { Alert.alert('Failed to log in', error.message); } }, [authState]);//...newAuthState is returned from the authorize function, as we can set our auth state to that returned value. Now we have the code to interface with FusionAuth, so we’ll want to give the user a way to invoke the code.
newAuthState是从authorize函数返回的,因为我们可以将auth状态设置为该返回值。 现在我们有了与FusionAuth交互的代码,因此我们希望为用户提供一种调用代码的方法。
So, we need to create a user interface (UI). We’ll create a simple UI to begin authentication. After the user has logged in, we’ll display the access token. The access token is what FusionAuth provides once a user has successfully signed in.
因此,我们需要创建一个用户界面(UI)。 我们将创建一个简单的UI来开始身份验证。 用户登录后,我们将显示访问令牌。 用户成功登录后,FusionAuth将提供访问令牌。
Of course, you typically don’t want to simply display or store the access token. You want it because it allows your application to make other API calls, often to gather more information to display to the user. Later in this tutorial we’ll use an access token to retrieve user information from an OpenID Connect endpoint, and display that in our application.
当然,您通常不想简单地显示或存储访问令牌。 您需要它,因为它允许您的应用程序进行其他API调用,通常会收集更多信息以显示给用户。 在本教程的稍后部分,我们将使用访问令牌从OpenID Connect端点检索用户信息,并将其显示在我们的应用程序中。
You can also provide the token to APIs that let the application send an email, record a todo or place an order. We won’t build those integrations today, however.
您还可以向API提供令牌,以使应用程序可以发送电子邮件,记录待办事项或下订单。 但是,我们今天不会构建这些集成。
To set up the UI, add this to App.js:
要设置UI,请将其添加到App.js :
//...return ( <View style={styles.container}> <Image source={require('./fusionauth.png')} /> {authState.accessToken ? ( <View style={styles.userInfo}> <View> <Text style={styles.userInfoText}> accessToken </Text> <Text style={styles.userInfoText}> {authState.accessToken} </Text> <Text style={styles.userInfoText}> accessTokenExpirationDate </Text> <Text style={styles.userInfoText}> {authState.accessTokenExpirationDate} </Text> </View> </View> ) : ( <TouchableOpacity style={styles.button} onPress={() => handleAuthorize()} > <Text style={styles.buttonText}> Login with FusionAuth</Text> </TouchableOpacity> )} </View>);The app will display one of two states, depending on whether we have an accessToken. Now, you we can run the app in the iOS simulator by typing npx react-native run-ios in your terminal:
该应用程序将显示两种状态之一,具体取决于我们是否具有accessToken 。 现在,您可以通过在终端中键入npx react-native run-ios在iOS模拟器中运行该应用程序:
演示地址
You can improve the look and feel of the application modifying the styles object and adding more CSS, but we’ll leave that as an exercise for the reader. Following best practices, notice that the mobile application opens up a system browser for user authentication, rather than a webview or embedded user-agent.
您可以通过修改styles对象并添加更多CSS来改善应用程序的外观,但我们将其作为练习供读者阅读。 按照最佳实践,请注意,移动应用程序打开系统浏览器进行用户身份验证,而不是打开Webview或嵌入式用户代理。
Once the user has successfully authenticated, we will have an access token, and possibly a refresh token, which should be stored securely. The access token is a JSON Web Token, also known as a JWT. Storing sensitive data like this JWT in Asyncstorage, the typical React Native client storage option, is bad practice. We can use a third-party package to access the iOS Keychain and Android secure storage, a better choice.
一旦用户成功通过身份验证,我们将获得一个访问令牌,可能还有一个刷新令牌,应将其安全存储。 访问令牌是JSON Web令牌,也称为JWT。 像JWT这样的敏感数据存储在Asyncstorage (典型的React Native客户端存储选项)中是不好的做法。 我们可以使用第三方程序包访问iOS钥匙串和Android安全存储,这是一个更好的选择。
There are many options, but the Formidable team, the creators of the react-native-app-auth package we are using, recommend react-native-keychain. Install it by running the following command:
有很多选择,但是强大的团队(我们正在使用的react-native-app-auth软件包的创建者)推荐react-native-keychain 。 通过运行以下命令进行安装:
yarn add react-native-keychainTo store the access token after successful authentication, add this to the App.js file:
要在成功认证后存储访问令牌,请将其添加到App.js文件中:
//...try { const newAuthState = await authorize(configs.fusionauth); console.log(newAuthState) setAuthState({ hasLoggedInOnce: true, ...newAuthState }); await Keychain.setGenericPassword('accessToken', newAuthState.accessToken);} catch (error) { Alert.alert('Failed to log in', error.message);}//...Before, we were calling setAuthState to store the JWT in memory, but now we’re storing it securely for future invocations. This is the line we added to do so:
以前,我们曾调用setAuthState将JWT存储在内存中,但是现在我们将其安全地存储以备将来调用。 这是我们添加的代码:
//...await Keychain.setGenericPassword('accessToken', newAuthState.accessToken);//...The flip side of storing the token in this manner is that we must create a function to check for credentials before returning the key. If it’s not there, we’ll return null:
以这种方式存储令牌的另一面是,我们必须创建一个函数以在返回密钥之前检查凭据。 如果不存在,我们将返回null :
//...const getAccesstoken = async () => { try { // Retrieve the credentials const credentials = await Keychain.getGenericPassword(); if (credentials) { return credentials.password } else { console.log('No credentials stored'); } } catch (error) { console.log("Keychain couldn't be accessed!", error); }}//...Now we can, when handed an access token, securely store and retrieve the JWT. Next, let’s look at what we can do with the token.
现在,当我们获得访问令牌时,我们可以安全地存储和检索JWT。 接下来,让我们看看如何使用令牌。
Since we have the access token, we can now retrieve user data from FusionAuth. Of course, you could also use the access token to call other services or APIs, but that’s beyond the scope of this tutorial.
由于有了访问令牌,因此我们现在可以从FusionAuth检索用户数据。 当然,您也可以使用访问令牌来调用其他服务或API,但这超出了本教程的范围。
To retrieve user information, create a new function called getUser in the App.js file. In it, we’ll construct a URL and retrieve the access token from storage, then we’ll make a call to an endpoint for user information.
要检索用户信息,请在App.js文件中创建一个名为getUser的新函数。 在其中,我们将构造一个URL并从存储中检索访问令牌,然后我们将调用一个终结点以获取用户信息。
//...const getUser = async () => { try { const access_token = await getAccesstoken(); if (access_token !== null) { fetch(configs.fusionauth.issuer+"/oauth2/userinfo", { method: "GET", headers: { Authorization: "Bearer " + access_token, }, }) .then((response) => response.json()) .then((json) => { console.log(json); setuserinfo(json); }) .catch((error) => { console.error(error); }); } } catch (e) { console.log(e); }};//...Of course, it’s not much fun to get the data, without displaying it. Let’s update the UI to show what we’ve learned about our user:
当然,在不显示数据的情况下获取数据并不是很有趣。 让我们更新UI,以显示我们对用户的了解:
//... {userinfo ? ( <View style={styles.userInfo}> <View> <Text style={styles.userInfoText}> Username:{userinfo.given_name} </Text> <Text style={styles.userInfoText}></Text> <Text style={styles.userInfoText}>Email:{userinfo.email}</Text> <Text style={styles.userInfoText}></Text> </View> </View> ) : ( <View></View> )}//...In this UI snippet, we’re checking if we have userinfo. If so, we’ll display the user’s given name and email address; this data is retrieved from FusionAuth. Here’s a video showing the emulators executing the code after these changes:
在此用户界面代码段中,我们正在检查是否有userinfo 。 如果是这样,我们将显示用户的给定名称和电子邮件地址; 该数据是从FusionAuth检索的。 这是一个视频,显示在这些更改之后仿真器执行代码的情况:
演示地址
There you have it. You have successfully configured a React Native application to interact with FusionAuth. We have authenticated a user, stored their access token securely, and displayed information from that user.
你有它。 您已成功配置React Native应用程序以与FusionAuth进行交互。 我们已经对用户进行了身份验证,安全地存储了他们的访问令牌,并显示了该用户的信息。
This tutorial has been a rollercoaster of information about mobile authentication. We were able to perform authorization and get user data from an OAuth server. As a reminder, the code for the React Native project is available on Github.
本教程是有关移动身份验证信息的过山车。 我们能够执行授权并从OAuth服务器获取用户数据。 提醒一下,Github上提供了React Native项目的代码。
I hope you enjoyed this tutorial. Do you have any comments or questions? Please post them below.
希望您喜欢本教程。 您有任何意见或疑问吗? 请在下面发布它们。
Happy coding!
编码愉快!
Enjoyed this article? If so, get more similar content by subscribing to Decoded, our YouTube channel!
喜欢这篇文章吗? 如果是这样,请订阅我们的YouTube频道解码,以获得更多类似的内容!
翻译自: https://medium.com/javascript-in-plain-english/securing-react-native-with-oauth-75fceaa5f3c8
oauth0 oauth2
相关资源:rack-oauth2:OAuth 2.0服务器和客户端库。 支持Bearer和MAC令牌类型-源码