React Native SDK

Link to this section

The Kinde React Native SDK allows developers to quickly and securely integrate a new or an existing React Native application into the Kinde platform. Kinde has separate SDKs for the following versions of React Native 0.5-0.59, and 0.7x.

Note: Since version 0.7x, we have already supported Typescript.

You can view the React Native v0.7x GitHub repo here.

We support both Expo and React Native versions 0.70 and higher. To use this package with older versions of React Native, please visit:

Register for Kinde

Link to this section

If you haven’t already got a Kinde account, register for free here (no credit card required).

You need a Kinde domain to get started, e.g. yourapp.kinde.com.

Before you install

Link to this section

You will need Node, the React Native command line interface, a JDK, Android Studio (for Android) and Xcode (for iOS).

Follow the installation instructions for your chosen OS to install dependencies.

The SDK can be installed with npm or yarn

// With npm
npm install @kinde-oss/react-native-sdk-0-7x --save

// With yarn
yarn add @kinde-oss/react-native-sdk-0-7x
  1. Check MainApplication.java to verify the react-native-keychain was added.

  2. If the keychain was not added, you need to install it:

    1. Edit android/settings.gradle

      ...
      
      include ':react-native-keychain'
      project(':react-native-keychain').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keychain/android')
      
      ...
    2. Edit android/app/build.gradle

      apply plugin: 'com.android.application'
      
      android {
        ...
      }
      
      dependencies {
        ...
      
        implementation project(':react-native-keychain')
      
        ...
      }
    3. Edit MainApplication.java

      ...
      
      import com.oblador.keychain.KeychainPackage;
      
      ...
      
      public class MainActivity extends extends ReactActivity {
        ...
        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                    new MainReactPackage(),
                    new KeychainPackage()
            );
        }
      
        // or
        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          packages.add(new KeychainPackage());
          return packages;
        }
        ...
      }
      ...
  1. Update iOS native dependencies using CocoaPods. We recommend installing CocoaPods using Homebrew.

    # Install CocoaPods via brew
    brew install cocoapods
    
    # Install iOS native dependencies
    cd ios && pod install
  2. If the react-native-keychain is not linked, install it.

    1. Option: With CocoaPods (Highly recommended)

      Add the following to your Podfile and run pod update:

      pod 'RNKeychain', :path => '../node_modules/react-native-keychain'
    2. Option: Manually

      • Go to the Build Phases tab and choose Link Binary With Libraries.
      • Select +
      • Add Other… => Add Files… => node_modules/react-native-keychain/RNKeychain.xcodeproj
      • Add libRNKeychain.a
      • Clean and rebuild.

[Expo] Installation in Bare React Native

Link to this section
  1. Run the below command to update the package to your npm dependencies:

    expo install expo-secure-store
    // or
    npx expo install expo-secure-store
  2. Enable Keychain Sharing entitlement for iOS 10+. For iOS 10, enable the Keychain Sharing entitlement in the Capabilities section of your build target.

Kinde configuration

Link to this section
  1. In Kinde, go to Settings > Applications.
  2. Select View details on the Frontend app.
  3. Scroll down to the Callback URLs section.
  4. Add in the callback URLs for your React Native app, which should look something like this:
    • Allowed callback URLs - myapp://myhost.kinde.com/kinde_callback
    • Allowed logout redirect URLs - myapp://myhost.kinde.com/kinde_callback

Make sure you press the Save button at the bottom of the page!

Note: The myapp://myhost.kinde.com/kinde_callback is used as an example of local URL Scheme, change to the local local URL Scheme that you use.

If you would like to use our Environments feature as part of your development process. You will need to create them first within your Kinde account. In this case you would use the Environment subdomain in the code block above.

Configure your app

Link to this section

Environment variables

Link to this section

Put these variables in your .env file. You can find these variables on the same page as where you set the callback URLs.

  • KINDE_ISSUER_URL - your Kinde domain
  • KINDE_POST_CALLBACK_URL - After the user authenticates we will callback to this address. Make sure this URL is under your allowed callback URLs
  • KINDE_POST_LOGOUT_REDIRECT_URL - where you want users to be redirected to after logging out. Make sure this URL is under your allowed logout redirect URLs
  • KINDE_CLIENT_ID - you can find this on the App Keys page
KINDE_ISSUER_URL=https://your_kinde_domain.kinde.com
KINDE_POST_CALLBACK_URL=myapp://your_kinde_domain.kinde.com/kinde_callback
KINDE_POST_LOGOUT_REDIRECT_URL=myapp://your_kinde_domain.kinde.com/kinde_callback
KINDE_CLIENT_ID=your_kinde_client_id

Configuration example:

KINDE_ISSUER_URL=https://myhost.kinde.com
KINDE_POST_CALLBACK_URL=myapp://myhost.kinde.com/kinde_callback
KINDE_POST_LOGOUT_REDIRECT_URL=myapp://myhost.kinde.com/kinde_callback
KINDE_CLIENT_ID=myclient@live
Link to this section

If your app was launched from an external URL registered to your app, you can access and handle it from any component you want with Linking:

...
import { ..., Linking, Platform, ... } from 'react-native';
...
useEffect(() => {
  Linking.getInitialURL()
    .then((url) => {
      if (url) {
        // Your code here
      }
    })
    .catch((err) => console.error("An error occurred", err));

  Linking.addEventListener('url', (event) => {
    if (event.url) {
      // Your code here
    }
  })
}, []);

You need to link RCTLinking to your project by following the steps below.

If you also want to listen to incoming app links during your app’s execution, add the following lines to your AppDelegate.m.

// iOS 9.x or newer
#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

If you’re targeting iOS 8.x or older, use the following code instead.

// iOS 8.x or older
#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}

Make sure you have a configuration URL scheme in Info.plist, so the app can be opened by deep link.

...
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>myapp</string> // you can change it
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string> // you can change it
    </array>
  </dict>
</array>
...

Open AndroidManifest.xml and update your scheme.

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" android:host="your_kinde_host" />  // Please modify sheme and host to reflect your preferences.
</intent-filter>

Linking to your app

To link to your development build or standalone app, you need to specify a custom URL scheme for your app. You can register a scheme in your Expo config (app.jsonapp.config.js) by adding a string under the scheme key:

{
  "expo": {
    "scheme": "myapp",
    ...
  }
}

Linking to Expo Go

Expo Go uses the exp:// scheme, however, if we link to exp:// without any address afterward, it will open the app to the home screen.

In development, your app will live at a url like exp://127.0.0.1:19000. When published, an experience will be hosted at a URL like  exp://u.expo.dev/[project-id]?channel-name=channel-name]&runtime-version=[runtime-version], where u.expo.dev/[project-id] is the hosted URL that Expo Go fetches from.

Caution: You also should update your callback URL to both your app and Kinde.

KINDE_ISSUER_URL=https://your_kinde_domain.kinde.com
KINDE_POST_CALLBACK_URL=exp://your_machine_ip:your_machine_port // f.e: exp://127.0.0.1:19000
KINDE_CLIENT_ID=your_kinde_client_id
KINDE_POST_LOGOUT_REDIRECT_URL=exp://your_machine_ip:your_machine_port // f.e: exp://127.0.0.1:19000

Integrate with your app

Link to this section

To create a new instance of the KindeSDK object, execute the code below.

...
import { KindeSDK } from '@kinde-oss/react-native-sdk-0-7x';
...

...
const client = new KindeSDK(YOUR_KINDE_ISSUER, YOUR_KINDE_REDIRECT_URI, YOUR_KINDE_CLIENT_ID, YOUR_KINDE_LOGOUT_REDIRECT_URI);
...

Log in and registration

Link to this section

Kinde provides methods for easily implementing a login / register flow.

As an example if you add buttons as follows.

<View>
    <View>
        <Button title="Sign In" onPress={handleSignIn} />
    </View>
    <View>
        <Button title="Sign Up" color="#000" onPress={handleSignUp} />
    </View>
</View>

Then define new functions that match for each button.

Note: Make sure you’ve already defined KindeSDK as client in the state.

...
const handleSignUp = () => {
  client.register();
};

const handleSignIn = () => {
  client.login();
};
...

Handle redirect

Link to this section

After the user logs in to Kinde, they will be redirected to your app via a deep link, which includes some information (e.g., code) as parameters, and then you need to call the getToken method to receive a token from Kinde. The SDK will store the token on the keychain so the user will be authenticated without logging in again.

Handle redirect with React Native

Your user is redirected back to your app from Kinde, using the getToken method.

...
import { Linking } from 'react-native';
...

const handleCallback = async (url) => {
  try {
    const token = await client.getToken(url);
    console.log('token here', token);
  } catch (e) {
    // Perhaps the deep link is not from Kinde authentication, or something went wrong. Logging the error to see detail
    console.error('ERR getToken', e);
  }
}

const checkAuthenticate = async () => {
  if (await client.isAuthenticated) { // Using `isAuthenticated` to check if the user is authenticated or not
    // Need to implement, e.g: call an api,etc... In this case, we will get a token:
    const token = await client.getToken();
    console.log('token here', token);
  } else {
    // Need to implement, e.g: redirect user to sign in or sign up screens,etc...
  }
}

useEffect(() => {
  Linking.getInitialURL()
    .then(url => {
      if (url) {
        return handleCallback(url);
      }
      checkAuthenticate();
    })
    .catch(err => console.error('An error occurred', err));

  const onChangeURL = (event: {url: string}) => {
    if (event.url) {
      handleCallback(event.url);
    }
  };
  Linking.addEventListener('url', onChangeURL);

  return () => {
    Linking.removeAllListeners('url');
  };
}, []);

Handle redirect with Expo

Install expo-linking to provide utilities for your app to interact with other installed apps using deep links. It also provides helper methods for constructing and parsing deep links into your app. This module is an extension of the React Native Linking module.

...
import * as Linking from "expo-linking";
...

const client = new KindeSDK(
  YOUR_KINDE_ISSUER,
  YOUR_KINDE_REDIRECT_URI,
  YOUR_KINDE_CLIENT_ID,
  YOUR_KINDE_LOGOUT_REDIRECT_URI
)

const url = Linking.useURL();

const checkAuthenticate = async () => {
  if (await client.isAuthenticated) { // Using `isAuthenticated` to check if the user is authenticated or not
    // Need to implement, e.g: call an api,etc... In this case, we will get a token:
    const token = await client.getToken();
    console.log('token here', token);
  } else {
    // Need to implement, e.g: redirect user to sign in or sign up screens,etc...
  }
}

useEffect(() => {
  checkAuthenticate();
}, []);

const handleCallback = async (url) => {
  try {
    const token = await client.getToken(url);
    console.log('token here', token);
  } catch (e) {
    // Perhaps the deep link is not from Kinde authentication, or something went wrong. Logging the error to see detail
    console.error('ERR getToken', e);
  }
}

useEffect(() => {
  if (url) {
    handleCallback(url);
  }
}, [url]);

This is implemented in much the same way as logging in or registering. The Kinde SPA client comes with a logout method:

const handleLogout = () => {
    client.logout();
};

Get user information

Link to this section

To access the user information, use the getUserDetails helper function.

const userProfile = await client.getUserDetails();
console.log(userProfile);
// output: {"given_name":"Dave","id":"abcdef","family_name":"Smith","email":"dave@smith.com"}

View users in Kinde

Link to this section

If you navigate to the “Users” page within Kinde you will see your newly registered user there.

User Permissions

Link to this section

Once a user has been verified, your product/application will return the JWT token with an array of permissions for that user. You will need to configure your product/application to read permissions and unlock the respective functions.

Set permissions in your Kinde account. Here’s an example set of permissions.

const permissions = [
    "create:todos",
    "update:todos",
    "read:todos",
    "delete:todos",
    "create:tasks",
    "update:tasks",
    "read:tasks",
    "delete:tasks"
];

We provide helper functions to more easily access permissions.

await client.getPermission("create:todos");
// {orgCode: "org_1234", isGranted: true}

await client.getPermissions();
// {orgCode: "org_1234", permissions: ["create:todos", "update:todos", "read:todos"]}

A practical example in code might look something like.

if ((await client.getPermission("create:todos")).isGranted) {
    // show Create Todo button in UI
}

An audience is the intended recipient of an access token - for example the API for your application. The audience argument can be passed to the Kinde client to request an audience be added to the token.

The audience of a token is the intended recipient of the token.

const client = new KindeSDK(
    YOUR_KINDE_ISSUER,
    YOUR_KINDE_REDIRECT_URI,
    YOUR_KINDE_CLIENT_ID,
    YOUR_KINDE_LOGOUT_REDIRECT_URI,
    YOUR_SCOPES,
    {
        audience: "api.yourapp.com"
    }
);

For details on how to connect, see Register an API.

Overriding scope

Link to this section

By default the KindeSDK requests the following scopes:

  • profile
  • email
  • offline
  • openid

You can override this by passing scope into the KindeSDK

const client = new KindeSDK(
    YOUR_KINDE_ISSUER,
    YOUR_KINDE_REDIRECT_URI,
    YOUR_KINDE_CLIENT_ID,
    YOUR_KINDE_LOGOUT_REDIRECT_URI,
    "profile email offline openid"
);

Getting claims

Link to this section

We have provided a helper to grab any claim from your id or access tokens. The helper defaults to access tokens:

await client.getClaim("aud");
// ["api.yourapp.com"]

await client.getClaim("given_name", "id_token");
// "David"

Create an organization

Link to this section

To create a new organization within your application, you will need to run a similar function to this.

<Button title="Create Organization" onPress={handleCreateOrg} />

Then define the function of the button.

Make sure you’ve already defined KindeSDK as the client in the state.

const handleCreateOrg = () => {
    client.createOrg();
};

// You can also pass `org_name` as your organization
client.createOrg({org_name: "Your Organization"});

Sign in and sign up to organizations

Link to this section

Kinde has a unique code for every organization. You’ll have to pass this code through when you register a new user.

Example function below:

client.register({org_code: "your_org_code"});

If you want a user to sign in into a particular organization, pass this code along with the sign in method.

client.login({org_code: "your_org_code"});

Following authentication, Kinde provides a json web token (jwt) to your application. Along with the standard information we also include the org_code and the permissions for that organization (this is important as a user can belong to multiple organizations and have different permissions for each). Example of a returned token:

Example of a returned token:

{
    "aud": [],
    "exp": 1658475930,
    "iat": 1658472329,
    "iss": "https://your_subdomain.kinde.com",
    "jti": "123457890",
    "org_code": "org_1234",
    "permissions": ["read:todos", "create:todos"],
    "scp": ["openid", "profile", "email", "offline"],
    "sub": "kp:123457890"
}

The id_token will also contain an array of organizations that a user belongs to - this is useful if you wanted to build out an organization switcher for example.

{
  ...
  "org_codes": ["org_1234", "org_4567"]
  ...
}

There are two helper functions you can use to extract information.

await client.getOrganization();
// {orgCode: "org_1234"}

await client.getUserOrganizations();
// {orgCodes: ["org_1234", "org_abcd"]}

Once the user has successfully authenticated, you’ll have a JWT and a refresh token and that has been stored securely. E.g. using the getAccessToken method of the Storage class to get an access token.

...
import { Storage } from '@kinde-oss/react-native-sdk-0-7x'
...

const accessToken = await Storage.getAccessToken();
console.log('access_token', accessToken);

We’re using the react-native-keychain for React Native and the expo-secure-store for Expo.

The storage handler can be found at: Storage class

Run the test suite using the following command at the root of your React Native.

npm run test

Note: Ensure you have already run npm install

SDK API Reference

Link to this section
PropertyTypeIs requiredDefaultDescription
issuerstringYesEither your Kinde instance url or your custom domain. e.g https://yourapp.kinde.com
redirectUristringYesThe url that the user will be returned to after authentication
clientIdstringYesThe id of your application - get this from the Kinde admin area
logoutRedirectUristringNoWhere your user will be redirected upon logout
scopebooleanNoopenid profile email offlineThe scopes to be requested from Kinde
additionalParametersobjectNo{}Additional parameters that will be passed in the authorization request
additionalParameters - audiencestringNoThe audience claim for the JWT

KindeSDK methods

Link to this section
PropertyDescriptionArgumentsUsageSample output
loginConstructs redirect url and sends user to Kinde to sign in{org_code?: string}kinde.login(); or kinde.login({org_code: 'your organization code'}) // Allow org_code to be provided if a specific org is to be signed up into.
registerConstructs redirect url and sends user to Kinde to sign up{org_code?: string}kinde.register(); or kinde.register({ org_code: "your organization code" }); // Allow org_code to be provided if a specific org is to be registered into.
logoutLogs the user out of Kindekinde.logout();
getTokenReturns the raw Access token from URL after logged from Kindeurl?: stringkinde.getToken(url); or kinde.getToken(); // In this case, you have already authenticated before. Otherwise, an error will be thrown in here{"access_token": "eyJhbGciOiJSUzI...","expires_in": 86400,"id_token": "eyJhbGciOiJSU...","refresh_token": "yXI1bFQKbXKLD7AIU...","scope": "openid profile email offline","token_type": "bearer"}
createOrgConstructs redirect url and sends user to Kinde to sign up and create a new org for your business{org_name?: string}kinde.createOrg(); or kinde.createOrg({org_name: 'your organization name'}); // Allow org_name to be provided if you want a specific organization name when you create
getClaimGets a claim from an access or id tokenclaim: string;tokenKey?: stringawait kinde.getClaim('given_name', 'id_token');"David"
getPermissionReturns the state of a given permissionkey: stringawait kinde.getPermission('read:todos');{"orgCode":"org_1234","isGranted":true}
getPermissionsReturns all permissions for the current user for the organization they are logged intoawait kinde.getPermissions();{"orgCode":"org_1234","permissions: ["create:todos","update:todos","read:todos"]}
getOrganizationGet details for the organization your user is logged intoawait kinde.getOrganization();{"orgCode": "org_1234"}
getUserDetailsReturns the profile for the current userawait kinde.getUserDetails();{"given_name":"Dave","id":"abcdef","family_name":"Smith","email":"dave@smith.com"}
getUserOrganizationsGets an array of all organizations the user has access toawait kinde.getUserOrganizations();{"orgCodes":["org1​234","org5​678"]}
isAuthenticatedReturn the boolean to demonstrate whether the user is authenticated or not.await kinde.isAuthenticatedtrue or false

Sometimes there will be issues related to caching when you develop React Native. There are some recommendations for cleaning the cache:

  1. Remove node_modules, yarn.lock or package-lock.json
  2. Clean cache: yarn cache clean or npm cache clean --force
  3. Make sure you have changed values in .env file
  4. Trying to install packages again: yarn install or npm install
  5. Run Metro Bundler: yarn start --reset-cache or npm start --reset-cache

Assume your StarterKit path is <StarterKit_PATH>.

Clean cache for Android

Link to this section
  1. Run this:

    cd <StarterKit_PATH>/android
    ./gradlew clean

Clean cache for iOS

Link to this section
  1. Run this:

    cd <StarterKit_PATH>/ios
    rm -rf Pods && rm Podfile.lock
  2. Clean build folders on Xcode.

If you need help connecting to Kinde, please contact us at support@kinde.com.

Talk to us

If you can’t find what you’re looking for in our help center — email our team

Contact us