React native bridge for AppAuth - an SDK for communicating with OAuth2 providers
Past documentation: 3.1
3.0
2.x
1.x
.
React Native bridge for AppAuth-iOS and AppAuth-Android SDKS for communicating with OAuth 2.0 and OpenID Connect providers.
This library should support any OAuth provider that implements the OAuth2 spec.
These providers are OpenID compliant, which means you can use autodiscovery.
- Identity Server4 (Example configuration)
- Identity Server3 (Example configuration)
- Google (Example configuration)
- Okta (Example configuration)
- Keycloak (Example configuration)
- Azure Active Directory (Example configuration)
- AWS Cognito (Example configuration)
These providers implement the OAuth2 spec, but are not OpenID providers, which means you must configure the authorization and token endpoints yourself.
- Uber (Example configuration)
- Fitbit (Example configuration)
- Dropbox (Example configuration)
- Reddit (Example configuration)
- Coinbase (Example configuration)
AppAuth is a mature OAuth client implementation that follows the best practices set out in
RFC 8252 - OAuth 2.0 for Native Apps including using
SFAuthenticationSession
and SFSafariViewController
on iOS, and
Custom Tabs on
Android. WebView
s are explicitly not supported due to the security and usability reasons
explained in Section 8.12 of RFC 8252.
AppAuth also supports the PKCE ("Pixy") extension to OAuth which was created to secure authorization codes in public clients when custom URI scheme redirects are used.
To learn more, read this short introduction to OAuth and PKCE on the Formidable blog.
See Usage for example configurations, and the included Example application for a working sample.
This is the main function to use for authentication. Invoking this function will do the whole login flow and returns the access token, refresh token and access token expiry date when successful, or it throws an error when not successful.
import { authorize } from 'react-native-app-auth';
const config = {
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
const result = await authorize(config);
ANDROID This will prefetch the authorization service configuration. Invoking this function is optional and will speed up calls to authorize. This is only supported on Android.
import { prefetchConfiguration } from 'react-native-app-auth';
const config = {
warmAndPrefetchChrome: true,
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
prefetchConfiguration(config);
This is your configuration object for the client. The config is passed into each of the methods with optional overrides.
- issuer - (
string
) base URI of the authentication server. If noserviceConfiguration
(below) is provided, issuer is a mandatory field, so that the configuration can be fetched from the issuer's OIDC discovery endpoint. - serviceConfiguration - (
object
) you may manually configure token exchange endpoints in cases where the issuer does not support the OIDC discovery protocol, or simply to avoid an additional round trip to fetch the configuration. If noissuer
(above) is provided, the service configuration is mandatory.- authorizationEndpoint - (
string
) REQUIRED fully formed url to the OAuth authorization endpoint - tokenEndpoint - (
string
) REQUIRED fully formed url to the OAuth token exchange endpoint - revocationEndpoint - (
string
) fully formed url to the OAuth token revocation endpoint. If you want to be able to revoke a token and noissuer
is specified, this field is mandatory. - registrationEndpoint - (
string
) fully formed url to your OAuth/OpenID Connect registration endpoint. Only necessary for servers that require client registration.
- authorizationEndpoint - (
- clientId - (
string
) REQUIRED your client id on the auth server - clientSecret - (
string
) client secret to pass to token exchange requests.⚠️ Read more about client secrets - redirectUrl - (
string
) REQUIRED the url that links back to your app with the auth code - scopes - (
array<string>
) the scopes for your token, e.g.['email', 'offline_access']
. - additionalParameters - (
object
) additional parameters that will be passed in the authorization request. Must be string values! E.g. settingadditionalParameters: { hello: 'world', foo: 'bar' }
would addhello=world&foo=bar
to the authorization request. - clientAuthMethod - (
string
) ANDROID Client Authentication Method. Can be eitherbasic
(default) for Basic Authentication orpost
for HTTP POST body Authentication - dangerouslyAllowInsecureHttpRequests - (
boolean
) ANDROID whether to allow requests over plain HTTP or with self-signed SSL certificates.⚠️ Can be useful for testing against local server, should not be used in production. This setting has no effect on iOS; to enable insecure HTTP requests, add a NSExceptionAllowsInsecureHTTPLoads exception to your App Transport Security settings. - customHeaders - (
object
) ANDROID you can specify custom headers to pass during authorize request and/or token request.- authorize - (
{ [key: string]: value }
) headers to be passed during authorization request. - token - (
{ [key: string]: value }
) headers to be passed during token retrieval request.
- authorize - (
- useNonce - (
boolean
) IOS (default: true) optionally allows not sending the nonce parameter, to support non-compliant providers - usePKCE - (
boolean
) (default: true) optionally allows not sending the code_challenge parameter and skipping PKCE code verification, to support non-compliant providers.
This is the result from the auth server
- accessToken - (
string
) the access token - accessTokenExpirationDate - (
string
) the token expiration date - authorizeAdditionalParameters - (
Object
) additional url parameters from the authorizationEndpoint response. - tokenAdditionalParameters - (
Object
) additional url parameters from the tokenEndpoint response. - additionalParameters - (
Object
)⚠️ DEPRECATED legacy implementation. Will be removed in a future release. Returns justtokenAdditionalParameters
for Android andauthorizeAdditionalParameters
on iOS - idToken - (
string
) the id token - refreshToken - (
string
) the refresh token - tokenType - (
string
) the token type, e.g. Bearer - scopes - ([
string
]) the scopes the user has agreed to be granted
This method will refresh the accessToken using the refreshToken. Some auth providers will also give you a new refreshToken
import { refresh } from 'react-native-app-auth';
const config = {
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
const result = await refresh(config, {
refreshToken: `<REFRESH_TOKEN>`
});
This method will revoke a token. The tokenToRevoke can be either an accessToken or a refreshToken
import { revoke } from 'react-native-app-auth';
const config = {
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
const result = await revoke(config, {
tokenToRevoke: `<TOKEN_TO_REVOKE>`
});
npm install react-native-app-auth --save
react-native link react-native-app-auth
Then follow the Setup steps to configure the native iOS and Android projects.
If you are not using react-native link
, perform the Manual installation
steps instead.
- In XCode, in the project navigator, right click
Libraries
➜Add Files to [your project's name]
- Go to
node_modules
➜react-native-app-auth
and addRNAppAuth.xcodeproj
- In XCode, in the project navigator, select your project. Add
libRNAppAuth.a
to your project'sBuild Phases
➜Link Binary With Libraries
- Run your project (
Cmd+R
)<
- Open up
android/app/src/main/java/[...]/MainApplication.java
- Add
import com.rnappauth.RNAppAuthPackage;
to the imports at the top of the file - Add
new RNAppAuthPackage()
to the list returned by thegetPackages()
method
- Append the following lines to
android/settings.gradle
:include ':react-native-app-auth' project(':react-native-app-auth').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-app-auth/android')
- Insert the following lines inside the dependencies block in
android/app/build.gradle
:compile project(':react-native-app-auth')
To setup the iOS project, you need to perform three steps:
This library depends on the native AppAuth-ios project. To keep the React Native library agnostic of your dependency management method, the native libraries are not distributed as part of the bridge.
AppAuth supports three options for dependency management.
-
CocoaPods
With CocoaPods, add the following line to your
Podfile
:pod 'AppAuth', '>= 0.94'
Then run
pod install
. Note that version 0.94 is the first of the library to support iOS 11. -
Carthage
With Carthage, add the following line to your
Cartfile
:github "openid/AppAuth-iOS" "master"
Then run
carthage update --platform iOS
.Drag and drop
AppAuth.framework
fromios/Carthage/Build/iOS
underFrameworks
inXcode
.Add a copy files build step for
AppAuth.framework
: open Build Phases on Xcode, add a new "Cope Files" phase, choose "Frameworks" as destination, addAppAuth.framework
and ensure "Code Sign on Copy" is checked. -
Static Library
You can also use AppAuth-iOS as a static library. This requires linking the library and your project and including the headers. Suggested configuration:
- Create an XCode Workspace.
- Add
AppAuth.xcodeproj
to your Workspace. - Include libAppAuth as a linked library for your target (in the "General -> Linked Framework and Libraries" section of your target).
- Add
AppAuth-iOS/Source
to your search paths of your target ("Build Settings -> "Header Search Paths").
If you intend to support iOS 10 and older, you need to define the supported redirect URL schemes in
your Info.plist
as follows:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.your.app.identifier</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.identityserver.demo</string>
</array>
</dict>
</array>
CFBundleURLName
is any globally unique string. A common practice is to use your app identifier.CFBundleURLSchemes
is an array of URL schemes your app needs to handle. The scheme is the beginning of your OAuth Redirect URL, up to the scheme separator (:
) character.
You need to retain the auth session, in order to continue the authorization flow from the redirect. Follow these steps:
RNAppAuth
will call on the given app's delegate via [UIApplication sharedApplication].delegate
.
Furthermore, RNAppAuth
expects the delegate instance to conform to the protocol RNAppAuthAuthorizationFlowManager
.
Make AppDelegate
conform to RNAppAuthAuthorizationFlowManager
with the following changes to AppDelegate.h
:
+ #import "RNAppAuthAuthorizationFlowManager.h"
- @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
+ @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RNAppAuthAuthorizationFlowManager>
+ @property(nonatomic, weak)id<RNAppAuthAuthorizationFlowManagerDelegate>authorizationFlowManagerDelegate;
The approach mentioned above should also be possible to employ with Swift. In this case one should have to import RNAppAuth
and make AppDelegate
conform to RNAppAuthAuthorizationFlowManager
. Note that this has not been tested.
AppDelegate.swift
should look something like this:
@import RNAppAuth
class AppDelegate: UIApplicationDelegate, RNAppAuthAuthorizationFlowManager {
public weak var authorizationFlowManagerDelegate: RNAppAuthAuthorizationFlowManagerDelegate?
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
return authorizationFlowManagerDelegate?.resumeExternalUserAgentFlowWithURL(with: url) ?? false
}
}
Note: for RN >= 0.57, you will get a warning about compile being obsolete. To get rid of this warning, use patch-package to replace compile with implementation as in this PR - we're not deploying this right now, because it would break the build for RN < 57.
To setup the Android project, you need to perform two steps:
This library depends on the AppAuth-Android project. The native dependencies for Android are automatically installed by Gradle, but you need to add the correct Android Support library version to your project:
- Add the Google Maven repository in your
android/build.gradle
repositories { google() }
- Make sure the appcompat version in
android/app/build.gradle
matches the one expected by AppAuth. If you generated your project usingreact-native init
, you may have an older version of the appcompat libraries and need to upgdrade:dependencies { implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}" }
- If necessary, update the
compileSdkVersion
to 25:android { compileSdkVersion 25 }
To
capture the authorization redirect,
add the following property to the defaultConfig in android/app/build.gradle
:
android {
defaultConfig {
manifestPlaceholders = [
appAuthRedirectScheme: 'io.identityserver.demo'
]
}
}
The scheme is the beginning of your OAuth Redirect URL, up to the scheme separator (:
) character.
import { authorize } from 'react-native-app-auth';
// base config
const config = {
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
// use the client to make the auth request and receive the authState
try {
const result = await authorize(config);
// result includes accessToken, accessTokenExpirationDate and refreshToken
} catch (error) {
console.log(error);
}
See example configurations for different providers below.
Some authentication providers, including examples cited below, require you to provide a client secret. The authors of the AppAuth library
strongly recommend you avoid using static client secrets in your native applications whenever possible. Client secrets derived via a dynamic client registration are safe to use, but static client secrets can be easily extracted from your apps and allow others to impersonate your app and steal user data. If client secrets must be used by the OAuth2 provider you are integrating with, we strongly recommend performing the code exchange step on your backend, where the client secret can be kept hidden.
Having said this, in some cases using client secrets is unavoidable. In these cases, a clientSecret
parameter can be provided to authorize
/refresh
calls when performing a token request.
g
Active: Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.