使用OAuth 2.0构建React Native应用并进行身份验证
本文最初发布在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和React在Bootiful 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万+ 关注 私信- 使用React,GraphQL和用户身份验证构建运行状况跟踪应用
- 在PHP应用中简化OAuth2.0身份验证集成:OAuth 2.0 Client
- Node.js项目实战-构建可扩展的Web应用(第一版): 6 在Node.js应用中使用session和OAuth进行用户认证和授权
- React Native:使用 JavaScript 构建原生应用 详细剖析
- 在IIS中使用Windows域服务器域摘要式身份验证对Web应用程序进行访问
- springmvc+mybatis登录验证2.0(更新了使用session来进行保存用户值,注销后清除session),并进行了拦截器的设定
- wicket基础应用(1)--使用wicket对表单中的数据进行验证
- django使用email进行身份验证
- Windows下使用AndroidStudio+ReactNative开发Android应用
- Rails使用has_secure_password进行身份验证
- 一步一步教你使用AgileEAS.NET基础类库进行应用开发-WinForm应用篇-演示使用报表构建UI-入库业务查询模块
- 在IIS中使用Windows域服务器域摘要式身份验证对Web应用程序进行访问控
- 无责任Windows Azure SDK .NET开发入门篇二[使用Azure AD 进行身份验证--2.1使用Azure AD需要了解几个概念]
- 使用react-native做一个简单的应用-05 navigator的使用
- Windows下使用AndroidStudio+ReactNative开发Android应用
- 前端自动化构建入门6-使用webpack改造我们的react应用
- shiro实现不同身份使用不同Realm进行验证
- OAuth 2.0 构建微服务身份认证(二):java实现过程
- 组建使用“智能卡”进行身份验证的***服务器