Keycloak OAuth2 Authorization in React Native
Getting started
npm install react-native-app-auth --save
authorize
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);
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. - 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. character. E.g. for redirect uri iscom.myapp://oauth
- scopes — (
array<string>
) the scopes for your token, e.g.['email', 'offline_access']
.
result
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. - 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
refresh
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>`,});
revoke
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>`,includeBasicAuth: true,sendClientId: true,});
Setup
iOS Setup
To setup the iOS project, you need to perform three steps:
CocoaPods
- cd ios
- pod install
Register redirect URL scheme
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. E.g. if your redirect uri iscom.myapp://oauth
, then the url scheme will iscom.myapp
.
Define openURL callback in AppDelegate
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;
Add the following code to AppDelegate.m
(to support iOS 10 and below)
+ - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *) options {+ return [self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:url];+ }
Android Setup
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 add redirect scheme manifest placeholder:
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. E.g. if your redirect uri is com.myapp://oauth
, then the url scheme will is com.myapp
.
Error messages
service_configuration_fetch_error
- could not fetch the service configurationauthentication_failed
- user authentication failedtoken_refresh_failed
- could not exchange the refresh token for a new JWTregistration_failed
- could not registerbrowser_not_found
(Android only) - no suitable browser installed
Note about client secrets
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.