您的位置:首页 > Web前端 > React

使用OAuth 2.0构建React Native应用并进行身份验证

2020-05-05 12:33 1966 查看

本文最初发布在Okta开发人员博客上 。 感谢您支持使SitePoint成为可能的合作伙伴。

使用Okta和OpenID Connect(OIDC),您可以轻松地将身份验证集成到React Native应用程序中,而不必自己再次构建它。 OIDC允许您直接根据Okta API进行身份验证,本文向您展示如何在React Native应用程序中进行身份验证。 今天,您将看到如何通过AppAuth库使用OIDC重定向将用户登录到React Native应用程序。

React Native是一个非常漂亮的框架。 与Ionic和其他混合移动框架不同,它允许您使用Web技术(React和JavaScript)来构建本机移动应用。 由于不涉及浏览器或WebView,因此使用React Native开发移动应用程序与使用本机SDK相似,因为您将在模拟器或设备上进行所有测试。 无法像使用Ionic一样在您的浏览器中对其进行测试。 这是有好处的,因为您不必编写在浏览器中和在设备上分别工作的代码。

如果您查看Google趋势,可以发现React Native在原生开发方面比Android和iOS还要受欢迎!

今天,我将向您展示如何开发具有最新和最佳版本的React Native应用程序。 在撰写本文时,这是React 16.2.0和React Native 0.54.0。 您将创建一个新应用,添加AppAuth进行身份验证,使用Okta进行身份验证,然后查看该应用程序是否同时在iOS和Android上运行。

AppAuth是用于本机应用程序的客户端SDK,可使用OAuth 2.0和OpenID Connect对最终用户进行身份验证和授权。 它可用于iOS,macOS,Android和Native JS环境,实现了针对本机应用程序身份验证和授权的现代安全性和可用性最佳实践

创建您的React本机应用程序

React有一个

create-react-app
命令行工具(CLI),可用于创建新的React应用。 React Native有一个类似的工具,称为Create React Native App 。 在安装它之前,请确保已安装Node v6或更高版本。

安装

create-react-native-app
并创建一个名为
okta-rn
的新项目:
npm install -g create-react-native-app
create-react-native-app okta-rn
cd okta-rn
npm start
运行这些命令将导致您的终端提示您一些选项:
To view your app with live reloading, point the Expo app to this QR code.
You'll find the QR scanner on the Projects tab of the app.

[QR Code]

Or enter this address in the Expo app's search bar:

exp://172.31.98.12:19000

Your phone will need to be on the same local network as this computer.
For links to install the Expo app, please visit https://expo.io.

Logs from serving your app will appear here. Press Ctrl+C at any time to stop.

› Press a to open Android device or emulator, or i to open iOS emulator.
› Press q to display QR code.
› Press r to restart packager, or R to restart packager and clear cache.
› Press d to toggle development mode. (current mode: development)
如果您使用的是Mac,请按i打开iOS模拟器。 系统将提示您使用Expo安装/打开,然后提供呈现的
App.js

如果您使用的是Windows或Linux,建议您尝试使用Android模拟器或Android设备(如果有)。 如果它不起作用,请不用担心,我稍后将向您展示如何进行该工作。

提示:您可以使用Microsoft的TypeScript React Native Starter在React Native应用程序中使用TypeScript代替JavaScript。 如果您决定走这条路,建议您完成本教程后,按照以下步骤转换应用程序。

React Native和OAuth 2.0

在此示例中,我将使用React Native App Auth (由Formidable创建的库)。 我使用此库的原因有三点:1)他们提供了一个很好的示例 ,使我能够在短短的几分钟内完成工作; 2)它使用AppAuth(成熟的OAuth客户端实现),以及3)无法使其他任何工作。

  • 我尝试了react-native-oauth,但发现添加新的提供程序之前需要使用现有的提供程序。 我只想让Okta作为提供者。 此外,问题数量很多,并且拉取请求也作为警告信号。
  • 我尝试了react-native-simple-auth,但是在使不赞成使用的Navigator组件与最新的React Native版本一起使用时遇到了问题。
  • 我尝试使用React Native教程进行OAuth 2操作 ,但是在重定向回我的应用程序时也遇到了问题。

在Okta中创建本机应用程序

在将AppAuth添加到React Native应用程序之前,需要一个应用程序进行授权。 如果您没有永久的Okta Developer永久帐户,请立即获取一个

登录到您的Okta Developer帐户,然后导航到Applications > Add Application 。 单击本 ,然后单击下一步 。 给应用程序起一个您会记住的名称(例如

React Native
),除了默认的
Authorization Code
之外,选择
Refresh Token
作为授予类型。 复制登录重定向URI (例如
com.oktapreview.dev-158606:/callback
)并将其保存在某处。 在配置应用程序时,您将需要此值。

单击完成 ,您应该在下一个屏幕上看到客户端ID。 也复制并保存该值。

添加React Native AppAuth进行身份验证

您需要“弹出”应用程序的本机配置,通常由create-react-native-app隐藏。

npm run eject
当提示您回答问题时,请使用以下答案:

回答
您想如何从create-react-native-app中退出?
React Native
您的应用应在用户主屏幕上显示为什么?
Okta RN
您的Android Studio和Xcode项目应命名为什么?
OktaRN

要为React Native安装App Auth,请运行以下命令:

npm i react-native-app-auth@2.2.0
npm i
react-native link
运行这些命令后,您必须配置本机iOS项目 。 为了方便起见,我已复制以下步骤。

iOS设置

React Native App Auth取决于AppAuth-ios ,因此您必须将其配置为依赖项。 最简单的方法是使用CocoaPods 。 要安装CocoaPods,请运行以下命令:

sudo gem install cocoapods
在项目的
ios
目录中创建一个
Podfile
,将AppAuth-ios指定为依赖项。 确保
OktaRN
与您在运行
npm run eject
OktaRN
时指定的应用程序名称匹配。
platform :ios, '11.0'

target 'OktaRN' do
pod 'AppAuth', '>= 0.91'
end
然后从
ios
目录运行
pod install
。 即使是快速连接,第一次也可能需要一段时间。 现在是喝咖啡或苏格兰威士忌的好时机! 🥃

通过从

ios
目录运行
open OktaRN.xcworkspace
在Xcode中打开项目。

如果打算支持iOS 10及更早版本,则需要在

ios/OktaRN/Info.plist
定义支持的重定向URL方案,如下所示:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>{yourReversedOktaDomain}</string>
</array>
</dict>
</array>
下面是我更改应用程序标识符并添加此密钥后的样子。
<key>CFBundleIdentifier</key>
<string>com.okta.developer.reactnative.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.oktapreview.dev-158606</string>
</array>
</dict>
</array>
在Xcode项目中打开
AppDelegate.h
(OktaRN> OktaRN>
AppDelegate.h
),并在下面添加带有
+
的行。
+ @protocol OIDAuthorizationFlowSession;

@interface AppDelegate : UIResponder <UIApplicationDelegate>
+ @property(nonatomic, strong, nullable) id<OIDAuthorizationFlowSession> currentAuthorizationFlow;
@property (nonatomic, strong) UIWindow *window;
@end
此属性保存在重定向到Okta之前启动的授权流信息。 Okta授权您后,它将重定向到传入的
redirect_uri

授权流程从

openURL()
应用程序委托方法开始。 要添加它,请打开
AppDelegate.m
并导入
AppAuth.h
#import "AppAuth.h"
然后在类的底部(
@end
之前),添加
openURL()
方法。
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) {
_currentAuthorizationFlow = nil;
return YES;
}
return NO;
}

构建您的React Native应用

用以下JavaScript替换
App.js
的代码。 此代码使您可以授权,刷新访问令牌并撤销它。
import React, { Component } from 'react';
import { UIManager, LayoutAnimation } from 'react-native';
import { authorize, refresh, revoke } from 'react-native-app-auth';
import { Page, Button, ButtonContainer, Form, Heading } from './components';

UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);

const scopes = ['openid', 'profile', 'email', 'offline_access'];

type State = {
hasLoggedInOnce: boolean,
accessToken: ?string,
accessTokenExpirationDate: ?string,
refreshToken: ?string
};

const config = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUrl: 'com.{yourReversedOktaDomain}:/callback',
additionalParameters: {},
scopes: ['openid', 'profile', 'email', 'offline_access']
};

export default class App extends Component<{}, State> {
state = {
hasLoggedInOnce: false,
accessToken: '',
accessTokenExpirationDate: '',
refreshToken: ''
};

animateState(nextState: $Shape<State>, delay: number = 0) {
setTimeout(() => {
this.setState(() => {
LayoutAnimation.easeInEaseOut();
return nextState;
});
}, delay);
}

authorize = async () => {
try {
const authState = await authorize(config);
this.animateState(
{
hasLoggedInOnce: true,
accessToken: authState.accessToken,
accessTokenExpirationDate: authState.accessTokenExpirationDate,
refreshToken: authState.refreshToken
},
500
);
} catch (error) {
Alert.alert('Failed to log in', error.message);
}
};

refresh = async () => {
try {
const authState = await refresh(config, {
refreshToken: this.state.refreshToken
});

this.animateState({
accessToken: authState.accessToken || this.state.accessToken,
accessTokenExpirationDate:
authState.accessTokenExpirationDate || this.state.accessTokenExpirationDate,
refreshToken: authState.refreshToken || this.state.refreshToken
});
} catch (error) {
Alert.alert('Failed to refresh token', error.message);
}
};

revoke = async () => {
try {
await revoke(config, {
tokenToRevoke: this.state.accessToken,
sendClientId: true
});
this.animateState({
accessToken: '',
accessTokenExpirationDate: '',
refreshToken: ''
});
} catch (error) {
Alert.alert('Failed to revoke token', error.message);
}
};

render() {
const {state} = this;
return (
<Page>
{!!state.accessToken ? (
<Form>
<Form.Label>accessToken</Form.Label>
<Form.Value>{state.accessToken}</Form.Value>
<Form.Label>accessTokenExpirationDate</Form.Label>
<Form.Value>{state.accessTokenExpirationDate}</Form.Value>
<Form.Label>refreshToken</Form.Label>
<Form.Value>{state.refreshToken}</Form.Value>
</Form>
) : (
<Heading>{state.hasLoggedInOnce ? 'Goodbye.' : 'Hello, stranger.'}</Heading>
)}

<ButtonContainer>
{!state.accessToken && (
<Button onPress={this.authorize} text="Authorize" color="#017CC0"/>
)}
{!!state.refreshToken && <Button onPress={this.refresh} text="Refresh" color="#24C2CB"/>}
{!!state.accessToken && <Button onPress={this.revoke} text="Revoke" color="#EF525B"/>}
</ButtonContainer>
</Page>
);
}
}
确保使用您的设置调整
config
const config = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
clientId: '{clientId}',
redirectUrl: 'com.{yourReversedOktaDomain}:/callback',
...
};
更改
index.js
以使用
OktaRN
作为应用程序的名称。
AppRegistry.registerComponent('OktaRN', () => App);
这段代码使用了styled-components ,因此您需要将其安装为依赖项。

注意:在运行以下命令之前,请确保导航到项目的根目录。

npm i styled-components
然后从Formidable的示例中将
components
目录复制到项目的根目录中。
svn export https://github.com/FormidableLabs/react-native-app-auth/trunk/Example/components
也获取
Page.js
组件中引用的背景图像。

svn export https://github.com/FormidableLabs/react-native-app-auth/trunk/Example/assets

在iOS模拟器上运行

使用

npm run ios
运行您的应用
npm run ios

您应该看到一个屏幕,上面写着“你好,陌生人”。 点击授权 ,系统将提示您继续或取消。

单击继续 ,您应该会看到Okta登录表单。 输入您的凭据,您将被重定向回该应用程序。

您可以单击刷新以查看访问令牌的值和到期日期更改。

提示:如果动画在iOS Simulator中缓慢发生,请切换调试 > 慢动画

Android设置

要配置本机Android项目,请先升级其使用的Gradle版本。

cd android
./gradlew wrapper --gradle-version 4.6
适用于Android的React Native App Auth取决于AppAuth-android ,但是您需要向项目中添加正确的Android支持库版本。

将Google Maven存储库添加到您的

android/build.gradle
并升级Android Tools依赖项:
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
android/app/build.gradle
appcompat
依赖项升级到
25.3.1
以匹配AppAuth期望的依赖项。

dependencies {
compile "com.android.support:appcompat-v7:25.3.1"
}

删除不再需要的

buildToolsVersion "23.0.1"

更新

compileSdkVersion
android {
compileSdkVersion 25
}
android/app/build.gradle
中将
appAuthRedirectScheme
属性添加为
defaultConfig
android {
defaultConfig {
...
manifestPlaceholders = [
appAuthRedirectScheme: '{yourReversedOktaDomain}'
]
}
}
进行此更改后,我的
defaultConfig
如下所示。
defaultConfig {
applicationId "com.oktarn"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
manifestPlaceholders = [
appAuthRedirectScheme: 'com.oktapreview.dev-158606'
]
}

在Android上运行

要在Android模拟器上进行尝试,请运行
npm run android
。 如果您没有插入电话或没有运行Android虚拟设备(AVD),则会看到错误消息:
* What went wrong:
Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: No connected devices!
要解决此问题,请打开Android Studio,选择打开现有项目 ,然后在项目中选择
android
目录。 如果系统提示您更新任何内容,请批准它。

要创建新的AVD,请导航至工具 > Android > AVD Manager 。 创建一个新的虚拟设备,然后单击播放。 从下面的设置中可以看到,我选择了Pixel 2。

再次运行

npm run android
。 您应该会看到一个欢迎屏幕,并且能够成功进行授权。

提示:不建议

Configuration 'compile' in project ':app' is deprecated. Use 'implementation' instead.
修复
Configuration 'compile' in project ':app' is deprecated. Use 'implementation' instead.
Configuration 'compile' in project ':app' is deprecated. Use 'implementation' instead.
,将
dependencies
下的
compile
更改为
implementation
。 有关更多信息,请参见“ 迁移到Gradle 3.0.0的Android插件”

升级到最新版本的React Native

react-native-git-upgrade工具是将项目升级为使用最新版本的便捷方法。 安装并运行它。

npm i -g react-native-git-upgrade
react-native-git-upgrade
npm i
或者,您可以只将
package.json
更改为
"react-native": "0.54.2"
,然后运行
npm i

获取和查看ID令牌

如果除了访问令牌之外还想获取ID令牌,请添加

idToken
作为
State
类型的属性,并在
App.js
添加
state
变量。
type State = {
...
idToken: ?string
};

export default class App extends Component<{}, State> {
...
state = {
...
idToken: ''
};
然后更新
authorize()
方法以从
authState
设置属性。 您将需要在
refresh()
revoke()
方法中添加类似的逻辑。
authorize = async () => {
try {
const authState = await authorize(config);
this.animateState(
{
hasLoggedInOnce: true,
accessToken: authState.accessToken,
accessTokenExpirationDate: authState.accessTokenExpirationDate,
refreshToken: authState.refreshToken,
idToken: authState.idToken
},
500
);
} catch (error) {
Alert.alert('Failed to log in', error.message);
}
};
要查看ID令牌中的内容,请安装buffer
npm i buffer
将其导入
App.js
的顶部。
import { Buffer } from 'buffer';
然后更改
render()
方法对其进行解码。
render() {
const {state} = this;
if (state.idToken) {
const jwtBody = state.idToken.split('.')[1];
const base64 = jwtBody.replace('-', '+').replace('_', '/');
const decodedJwt = Buffer.from(base64, 'base64');
state.idTokenJSON = JSON.parse(decodedJwt);
}
...
最后,在显示访问令牌的行之后添加
<Form.Label>
<Form.Value>
行。
<Form.Label>ID Token</Form.Label>
<Form.Value>{JSON.stringify(state.idTokenJSON)}</Form.Value>
运行
npm run ios
(或
npm run android
),使用Okta授权后,您应该在ID令牌中看到声明。 下面是一个截图,证明它可以在iOS Simulator中使用。

使用您的访问令牌调用API

现在您有了访问令牌,该怎么办? 您可以在

Authorization
标头中调用受Okta保护的API!

我写了关于如何使用Spring Boot和ReactBootiful Development中创建“ Good Beers” API的文章 。 您可以使用该应用程序的后端来证明它可以工作。

从GitHub克隆项目,并查看

okta
分支。

git clone https://github.com/oktadeveloper/spring-boot-react-example.git
git checkout okta

修改

spring-boot-react-example/server/src/main/resources/application.properties
以设置
issuer
clientId
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId={clientId}
注意:您需要安装Java 8才能运行此Spring Boot应用程序。

通过从

server
目录运行
./mvnw
来启动应用程序。

回到React Native客户端。 在

App.js
,将
beers
添加为
state
的属性。
state = {
...
beers: []
};
revoke()
方法中将其设置为相同的值。 添加一个
fetchGoodBeers()
方法,该方法使用访问令牌来调用后端。
fetchGoodBeers = async () => {
if (this.state.beers.length) {
// reset to id token if beers is already populated
this.animateState({beers: []})
} else {
fetch('http://localhost:8080/good-beers', {
headers: {
'Authorization': `Bearer ${this.state.accessToken}`
}
}).then(response => response.json())
.then(data => {
this.animateState({beers: data})
})
.catch(error => console.error(error));
}
};
提示:为了使其在Android模拟器(和真实电话)中正常运行,您需要将
localhost
更改为您的IP地址。

在底部的

<ButtonContainer>
中,添加一个“ Good Beers”按钮,使您可以调用API,然后再次按一下以查看ID令牌。
{!!state.accessToken && <Button onPress={this.fetchGoodBeers} text={!this.state.beers.length ? 'Good Beers' : 'ID Token'} color="#008000" />}
修改显示ID令牌的行,以显示API中的JSON。
<Form.Label>{state.beers.length ? 'Good Beers' : 'ID Token'}</Form.Label>
<Form.Value>{JSON.stringify(state.beers.length ? state.beers : state.idTokenJSON)}</Form.Value>
在iOS Simulator中,按Command + R重新加载所有内容,并且在单击Good Beers按钮时应该看到JSON。 您可以使用Command + M (在Mac上为CTRL + M,在其他操作系统上)在Android中重新加载。

注意: react-native-app-auth中存在一个未解决的问题 ,即由于未发送

Authorization
标头,因此撤销不能与Okta一起使用。

了解有关React Native和React的更多信息

我希望您喜欢这个如何使用Okta和React Native进行身份验证的旋风之旅。 您可以在React Native 的官方网站上了解更多信息。 您还可以在GitHub上添加约6万颗星。

您可以在https://github.com/oktadeveloper/okta-react-native-app-auth-example中找到此应用程序的源代码。

如果您有兴趣了解如何使用Okta进行常规的React开发,建议您查看以下资源:

如果您对本文有任何疑问,请在Twitter @mraible上打我。

From: https://www.sitepoint.com/build-a-react-native-application-authenticate-with-oauth-2-0/

dingshi7798 原创文章 0获赞 0访问量 1万+ 关注 私信
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐