React Native SDK

The Stytch React Native SDKs provide the easiest way for you to use Stytch in your React Native projects. We provide separate SDKs for Expo managed workflows as well as bare React Native projects to ensure ease of installation and use, as well as compatibility with iOS and Android builds. Using the SDKs allows you to easily authenticate your users without adding extra endpoints to your backend.

Compatibility and requirements

The Expo SDK is compatible with all actively supported Expo versions (currently Expo 41+).
The Expo SDK also supports all versions of iOS and Android supported by Expo (currently Android 5+ and iOS 11+)

The React Native SDK is compatible with React Native 0.60+

Supported authentication products

  • Email magic links
  • One-time passcodes
  • Biometrics
  • Passwords
  • Session management
  • User management


Installation

Install the relevant package(s) as a project dependency via npm or yarn.

npm install @stytch/react-native @stytch/react-native-modules react-native-keychain react-native-device-info react-native-get-random-values react-native-inappbrowser-reborn --save
npm install @stytch/react-native-expo --save

If you are using Stytch's Biometrics or OAuth product, you will also need to install @stytch/react-native-modules. If you are using an "ejected" Expo setup (required for installing packages with custom native modules, learn more in the Expo docs), you will also need to install @react-native-async-storage/async-storage and react-native-keychain.

Initialize the SDK by creating a new StytchClient object provided in the package. We recommend storing the created client instance in a common file to use it in JavaScript methods such as Redux actions.

You do not need to manually link, but will need to run pod install if you are using @stytch/react-native.

StytchClient parameters


public_token*string

Provide the public token from the Stytch dashboard. This value is safe to embed client side.

At the root level of your application, initialize a new StytchProvider object using your public token. to provide all components with access to the StytchClient as well as User and Session data via hooks.

StytchProvider props


stytch*StytchClient | null

Provide the previously created StytchClient object.

Example

import { StytchClient, StytchProvider } from '@stytch/react-native';

const stytch = new StytchClient('PUBLIC_TOKEN');

const App = () => {
  return (
    <StytchProvider stytch={stytch}>
      <Component />
    </StytchProvider>
  );
};

Configuration

To authorize the Stytch SDK to run in your application, you must first add an Authorized Environment Bundle ID to your Stytch SDK Configuration.

Any application published to the Apple App Store or the Google Play Store must have a unique ID. In iOS development, this is referred to as a bundle identifier, and in Android it is referred to as the Application ID or package name.

If the bundle identifier / package name does not match the Authorized Environment Bundle ID in your Stytch SDK Configuration, then you will receive an invalid_mobile_identifier error on any requests made through the SDK. If you are encountering this error, ensure that you've followed the steps in this configuration correctly.

For bare React Native projects, the process takes a few extra steps, and there are separate processes for updating iOS bundle identifier vs. the Android package name.

For iOS, you first open the general settings for the project in Xcode. To get there, open the project workspace, and select the appname project from the project navigator. Once you've opened the general settings for the project, simply update the Bundle Identifier field.

For Android, there a number of steps. Firstly, you'll need to rename the folder structure inside android/app/src/main/java to reflect the package name for your project. If you had initialized your project with the name HelloWorld, you would find two files MainActivity.java and MainApplication.java inside android/app/src/main/java/com/helloworld. If you wanted to change your package name to com.myapp.test you'd have to update the folder structure so that those two files were insideandroid/app/src/main/java/com/myapp/test. After you've updated your folder structure, you need to update:

  • The package name on line 1 of MainActivity.java
  • The package name on line 1 of MainApplication.java
  • Line 2 of /android/app/src/main/AndroidManifest.xml
  • The package value for android_build_config and android_resource in /android/app/BUCK
  • The applicationId value in defaultConfig for the /android/app/build.gradle file.


Example apps

Demo React Native app

Example React Native app with bare workflow


Demo Expo app

Example React Native app with Expo managed workflow


One-time passcodes

One-time passcodes can be sent via email, phone number, or WhatsApp. One-time passcodes allow for a quick and seamless login experience on their own, or can layer on top of another login product like Email magic links to provide extra security as a multi-factor authentication (MFA) method.

Methods

To call these methods, One-time passcodes must be enabled in the SDK Configuration page of the Stytch dashboard.

Login or create via SMS

Wraps Stytch's login_or_create via SMS API endpoint. Call this method to send an SMS passcode to new or existing users.


Method parameters


phone_number*string

Configurationobject

Additional configuration.

expiration_minutesint

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.sms.loginOrCreate('+12025550162', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
      "status_code": 200,
      "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
      "method_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0"
    }

Send via SMS

Wraps Stytch's send via SMS API endpoint. Call this method to send an SMS passcode to existing users.

This method is also used when you need to add a phone number to an existing Stytch User. If there is a currently valid Stytch session, and the user inputs a phone number that does not match one on their Stytch User object, upon successful authentication the new phone number will be added to the existing user. Note, this does expose a potential account enumeration vector, see our article on preventing account enumeration for more details.


Method parameters


phone_number*string

Configurationobject

Additional configuration.

expiration_minutesint

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.sms.send('+12025550162', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "method_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0"
  }

Login or create via email

Wraps Stytch's login_or_create via email API endpoint. Call this method to send an email passcode to new or existing users.


Method parameters


email*string

Configurationobject

Additional configuration.

expiration_minutesint
login_template_idstring
signup_template_idstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.email.loginOrCreate('sandbox@stytch.com', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "method_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953"
  }

Send via email

Wraps Stytch's send via email API endpoint. Call this method to send an email passcode to existing users.

This method is also used when you need to add a phone number to an existing Stytch User. If there is a currently valid Stytch session, and the user inputs a phone number that does not match one on their Stytch User object, upon successful authentication the new phone number will be added to the existing user. Note, this does expose a potential account enumeration vector, see our article on preventing account enumeration for more details.


Method parameters


email*string

Configurationobject

Additional configuration.

expiration_minutesint
login_template_idstring
signup_template_idstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.email.send('sandbox@stytch.com', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "method_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953"
  }

Login or create via WhatsApp

Wraps Stytch's login_or_create via WhatsApp API endpoint. Call this method to send an WhatsApp passcode to new or existing users.


Method parameters


phone_number*string

Configurationobject

Additional configuration.

expiration_minutesint

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.whatsapp.loginOrCreate('+12025550162', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "method_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0"
  }

Send via WhatsApp

Wraps Stytch's send via WhatsApp API endpoint. Call this method to send an WhatsApp passcode to existing users.

This method is also used when you need to add a phone number to an existing Stytch User. If there is a currently valid Stytch session, and the user inputs a phone number that does not match one on their Stytch User object, upon successful authentication the new phone number will be added to the existing user. Note, this does expose a potential account enumeration vector, see our article on preventing account enumeration for more details.


Method parameters


phone_number*string

Configurationobject

Additional configuration.

expiration_minutesint

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';
export const Login = () => {
  const stytchClient = useStytch();
  const sendPasscode = useCallback(() => {
    stytchClient.otps.whatsapp.send('+12025550162', {
      expiration_minutes: 5,
    });
  }, [stytchClient]);
  return <button onClick={sendPasscode}>Send passcode</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "method_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0"
  }

Authenticate

The Authenticate method wraps the authenticate one-time passcode API method which validates the code passed in.

If this method succeeds, the user will be logged in, granted an active session, and the session data will be persisted on device.

You can listen for successful login events anywhere in the codebase with the stytch.session.onChange() method or useStytchSession hook.


Method parameters


method_id*string

code*string

Configuration*object

Additional configuration.

session_duration_minutes*string

Example

Vanilla
import React, { useCallback, useState } from 'react';
import { useStytch } from '@stytch/stytch-react';

export const Authenticate = () => {
  const stytchClient = useStytch();
  const [code, setCode] = useState('');

  const method_id = 'phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0';
  // returned from calling loginOrCreate for OTPs on SMS, WhatsApp or Email

  const authenticate = useCallback(
    (e) => {
      e.preventDefault();
      stytchClient.otps.authenticate(code, method_id, {
        session_duration_minutes: 60,
      });
    },
    [stytchClient, code],
  );

  const handleChange = useCallback((e) => {
    setCode(e.target.value);
  }, []);

  return (
    <form>
      <label for="otp-input">Enter code</label>
      <input id="otp-input" autocomplete="one-time-code" inputtype="numeric" pattern="[0-9]*" onChange={handleChange} />
      <button onClick={authenticate} type="submit">
        Submit
      </button>
    </form>
  );
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    "session_token": "mZAYn5aLEqKUlZ_Ad9U_fWr38GaAQ1oFAhT8ds245v7Q",
    "session_jwt": "eyJ...",
    "session": {...},
    "method_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0",
  }


Passwords

The React Native SDK provides methods for creating, storing, and authenticating password based users, as well as support for account recovery (password reset) and account deduplication with passwordless login methods.

Methods

The SDK provides methods that can be used to create and authenticate password based users.

To call these methods, Passwords must be enabled in the SDK Configuration page of the Stytch dashboard.

Create

The send method wraps the create Password API endpoint.


Method parameters


email*string

password*string

session_duration_minutes*string

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const createPassword = useCallback(() => {
    stytchClient.passwords.create({
      email: '${exampleEmail}',
      password: 'LSebBNY@(yrN7Hkk',
      session_duration_minutes: 60,
    });
  }, [stytchClient]);

  return <button onClick={createPassword}>Create Password</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    "email_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953"
  }

Authenticate

The authenticate method wraps the authenticate Password API endpoint.

If this method succeeds, the user will be logged in, granted an active session, and the session data will be persisted on device.

You can listen for successful login events anywhere in the codebase with the stytch.session.onChange() method or useStytchSession hook.


Method parameters


email*string

password*string

session_duration_minutes*string

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const authenticatePassword = useCallback(() => {
    stytchClient.passwords.authenticate({
      email: '${exampleEmail}',
      password: 'LSebBNY@(yrN7Hkk',
      session_duration_minutes: 60,
    });
  }, [stytchClient]);

  return <button onClick={authenticatePassword}>Authenticate Password</button>;
};

RESPONSE

200
{
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "session": null,
    "session_jwt": "",
    "session_token": "",
    "status_code": 200,
    "user": {...},
    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
  }

Reset by email start

The resetByEmailStart method wraps the reset_by_email_start Password API endpoint.

If this method succeeds, the user will be logged in, granted an active session, and the session data will be persisted on device.

You can listen for successful login events anywhere in the codebase with the stytch.session.onChange() method or useStytchSession hook.


Method parameters


email*string

Configurationobject

Additional configuration.

reset_password_redirect_urlstring
login_redirect_urlstring
reset_password_expiration_minutesint
reset_password_template_idstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const resetPasswordStart = useCallback(() => {
    stytchClient.passwords.resetByEmailStart({
      email: '${exampleEmail}',
    });
  }, [stytchClient]);

  return <button onClick={resetPasswordStart}>Reset Password Start</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    "email_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953"
  }

Reset by email

The resetByEmail method wraps the reset_by_email Password API endpoint.

If this method succeeds, the user will be logged in, granted an active session, and the session data will be persisted on device.

You can listen for successful login events anywhere in the codebase with the stytch.session.onChange() method or useStytchSession hook.


Method parameters


token*string

password*string

session_duration_minutes*string

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const token = new URLSearchParams(window.location.search).get('token');

  const resetPassword = useCallback(() => {
    stytchClient.passwords.resetByEmail({
      token: token,
      password: 'LSebBNY@(yrN7Hkk',
      session_duration_minutes: 60,
    });
  }, [stytchClient, token]);

  return <button onClick={resetPassword}>Reset Password</button>;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6"
  }

Strength check

This method allows you to check whether or not the user’s provided password is valid, and to provide feedback to the user on how to increase the strength of their password. All passwords must pass the strength requirements to be accepted as valid.

The strengthCheck method wraps the strength_check Password API endpoint.


Method parameters


password*string

emailstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const strengthCheck = useCallback(() => {
    stytchClient.passwords.strengthCheck({
      email: '${exampleEmail}',
      password: 'LSebBNY@(yrN7Hkk',
    });
  }, [stytchClient]);

  return <button onClick={strengthCheck}>Strength Check</button>;
};

RESPONSE

200 - LUDS invalid
{
    "breach_detection_on_create": true,
    "breached_password": false,
    "feedback": {
      "suggestions": null,
      "warning": null,
      "has_digit": true,
      "has_lower_case": false,
      "has_symbol": false,
      "has_upper_case": false,
      "missing_characters": 6,
      "missing_complexity": 1
    },
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "score": 0,
    "status_code": 200,
    "strength_policy": "luds",
    "valid_password": false
  }

Biometrics

Biometric authentication enables your users to leverage their devices' built-in biometric authenticators such as FaceID and TouchID for quick and seamless login experiences.

Configuration

Biometrics requires importing native modules into your React Native or Expo app. If you are using Expo, you may need to review using native modules in the Expo docs.

After installing the latest version of @stytch/react-native or @stytch/react-native-expo, you'll also need to install the latest version of @stytch/react-native-modules. You'll then need to run pod install from the ios folder in order to install the native modules required for biometrics.

Stytch's Biometrics product supports iOS 13.0+ and Android M+

Important: When testing your biometrics implementation with an iOS emulator, you must use iOS version 13 or 14. The minimum target for the Stytch SDKs is iOS 13.0, and biometrics do not work on emulators running iOS 15. This should not be an issue on real devices for testing and production.

Methods

The SDK provides methods that can be used to authenticate users with biometric factors. Biometric factors must first be added to an existing user, and then later used as a primary or secondary authentication factor. To call these methods, Biometrics must be enabled in the SDK Configuration page of the Stytch dashboard.


Register

If an active session is present, this method will add a biometric registration for the current user. The user will later be able to start a new session with biometrics or use biometrics as an additional authentication factor.


Method parameters


Options*object

Configuration options.

prompt*string
cancelButtonTextstring
allowDeviceCredentialsstring
allowFallbackToCleartextstring
sessionDurationMinutesstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch, useStytchSession } from '@stytch/react-native';

export const Register = () => {
  const stytchClient = useStytch();
  const session = useStytchSession();

  const registerBiometrics = useCallback(() => {
    if (session) {
      stytchClient.biometrics.register({
        prompt: 'Register Your Biometric Factor',
      });
    }
  }, [stytchClient]);

  return (
    <View>
      <TouchableOpacity onPress={registerBiometrics}>
        <Text>Register with Biometrics</Text>
      </TouchableOpacity>
    </View>
  );
};

Authenticate

If a valid biometric registration exists, this method confirms the current device owner via the device's built-in biometric reader and returns a session object by either starting a new session or adding a biometric factor to an existing session.


Method parameters


Options*object

Configuration options.

prompt*string
session_duration_minutes*string
cancelButtonTextstring
allowDeviceCredentialsstring

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Authenticate = () => {
  const stytchClient = useStytch();

  const authenticateBiometrics = useCallback(() => {
    stytchClient.biometrics.authenticate({
      prompt: 'Login with Biometrics',
      sessionDurationMinutes: 60,
    });
  }, [stytchClient]);

  return (
    <View>
      <TouchableOpacity onPress={authenticateBiometrics}>
        <Text>Authenticate with Biometrics</Text>
      </TouchableOpacity>
    </View>
  );
};

Keystore available

Indicates whether or not the Keystore is available on the device. This will always return true for iOS devices. A percentage of Android devices will return false. To learn more about the implications of the Android Keystore being unavailable, read our Android Keystore resource.

Example

Vanilla
import React, { useCallback, useMemo } from 'react';
import { useStytch, useStytchSession } from '@stytch/react-native';

export const Register = () => {
  const stytchClient = useStytch();
  const session = useStytchSession();

  const isKeystoreAvailable = useMemo(async () => {
    if (stytchClient) {
      return await stytchClient.biometrics.isKeystoreAvailable();
    } else {
      return false;
    }
  }, [stytchClient]);

  const registerBiometrics = useCallback(() => {
    if (session) {
      stytchClient.biometrics.register({
        prompt: 'Register Your Biometric Factor',
      });
    }
  }, [stytchClient]);

  return isKeystoreAvailable ? (
    <View>
      <TouchableOpacity onPress={registerBiometrics}>
        <Text>Register with Biometrics</Text>
      </TouchableOpacity>
    </View>
  ) : null;
};

Registration available

Indicates if there is an existing biometric registration on device. This method can be used to determine whether or not to show biometric login or registration options.

Example

Vanilla
import React, { useCallback, useMemo } from 'react';
import { useStytch } from '@stytch/react-native';

export const Authenticate = () => {
  const stytchClient = useStytch();

  const isBiometricsAvailable = useMemo(async () => {
    if (stytchClient) {
      return await stytchClient.biometrics.isRegistrationAvailable();
    } else {
      return false;
    }
  }, [stytchClient]);

  const authenticateBiometrics = useCallback(() => {
    stytchClient.biometrics.authenticate({
      prompt: 'Login with Biometrics',
      sessionDurationMinutes: 60,
    });
  }, [stytchClient]);

  return isBiometricsAvailable ? (
    <View>
      <TouchableOpacity onPress={authenticateBiometrics}>
        <Text>Authenticate with Biometrics</Text>
      </TouchableOpacity>
    </View>
  ) : null;
};

Remove registration

Clears the existing biometric registration stored on device. This method is useful for removing a user from a given device.

Example

Vanilla
import React, { useCallback, useMemo } from 'react';
import { useStytch } from '@stytch/react-native';

export const RemoveRegistration = () => {
  const stytchClient = useStytch();

  const isBiometricsAvailable = useMemo(async () => {
    if (stytchClient) {
      return await stytchClient.biometrics.isRegistrationAvailable();
    } else {
      return false;
    }
  }, [stytchClient]);

  const removeBiometricsRegistration = useCallback(() => {
    stytchClient.biometrics.removeRegistration();
  }, [stytchClient]);

  return isBiometricsAvailable ? (
    <View>
      <TouchableOpacity onPress={removeBiometricsRegistration}>
        <Text>Delete Biometric Factor</Text>
      </TouchableOpacity>
    </View>
  ) : null;
};

Get sensor

Checks if biometric sensors are available on the device. This method can be used to determine whether or not to show biometric registration options.


Method parameters


allowDeviceCredentialsstring

Example

Vanilla
import React, { useCallback, useMemo } from 'react';
import { useStytch } from '@stytch/react-native';

export const Register = () => {
  const stytchClient = useStytch();

  const isSensorAvailable = useMemo(async () => {
    if (stytchClient) {
      try {
        await stytchClient.biometrics.getSensor();
        return true;
      } catch {
        return false;
      }
    } else {
      return false;
    }
  }, [stytchClient]);

  const registerBiometrics = useCallback(() => {
    stytchClient.biometrics.register({
      prompt: 'Register with Biometrics',
    });
  }, [stytchClient]);

  return isSensorAvailable ? (
    <View>
      <TouchableOpacity onPress={registerBiometrics}>
        <Text>Register with Biometrics</Text>
      </TouchableOpacity>
    </View>
  ) : null;
};

Errors

biometrics_sensor_error

There was an error with the biometric sensor on the device. This usually means that the biometric sensor is currently unavailable for use.

device_credentials_not_allowed

This means that allowDeviceCredentials was set to false in register(), but allowDeviceCredentials was set to true in authenticate(). Consider changing these parameters to be the same.

device_hardware_error

The device's hardware does not support biometrics. This might be because the hardware is unavailable for use, or there is a security vulnerability with the hardware.

internal_error

An internal error has occurred. Please contact Stytch if this occurs.

key_invalidated

The biometrics enrollment on the device has changed, so biometric authentication cannot use the current registration. Try deleting the registration and authenticating again.

keystore_unavailable

The Android keystore is unavailable on the device. Learn more about the Android Keystore here.

no_biometrics_enrolled

No biometric factor is enrolled on the device. Consider encouraging the user to enroll in biometrics on their device.

no_biometrics_registration

No valid biometrics registration exists on the device. Please use the register method to create a new biometric registration.

session_expired

There is currently no valid session token. Consider encouraging the user to log in.

user_cancellation

The user canceled the biometric prompt. Consider providing other methods of authentication, and a way for the user to return to the biometric prompt.

user_locked_out

The user has been locked out from authentication using biometrics due to too many failed attempts. Consider providing other methods of authentication.


OAuth

OAuth is a popular authentication framework that delegates authentication to an external identity provider (often shortened to IdP) like Google, Facebook, Apple, and Microsoft. A user relies on their membership from that provider to sign in instead of creating another password, and developers can enrich their users' experiences with the information shared by the providers.

The React Native SDK provides methods to start and authenticate OAuth flows that you can connect to your own UI.

Configuration

OAuth requires importing native modules into your React Native or Expo app. If you are using Expo, you may need to review using native modules in the Expo docs.

After installing the latest version of @stytch/react-native or @stytch/react-native-expo, you'll also need to install the latest version of @stytch/react-native-modules. You'll then need to run pod install from the ios folder in order to install the native modules required for OAuth.

Methods

The SDK provides methods that can be used to get the URL to start an OAuth flow and authenticate tokens in the links later.

To call these methods, OAuth must be enabled in the SDK Configuration page of the Stytch dashboard.

Start

The oauth.$provider.start() methods start OAuth flows by redirecting the browser to one of Stytch's oauth start endpoints. The method will also generate a PKCE code_verifier and store it in local storage on the device (See the PKCE OAuth guide for details). If your application is configured to use a custom subdomain with Stytch, it will be used automatically.

  • oauth.amazon.start()
  • oauth.apple.start()
  • oauth.bitbucket.start()
  • oauth.coinbase.start()
  • oauth.discord.start()
  • oauth.facebook.start()
  • oauth.github.start()
  • oauth.gitlab.start()
  • oauth.google.start()
  • oauth.linkedin.start()
  • oauth.microsoft.start()
  • oauth.salesforce.start()
  • oauth.slack.start()
  • oauth.twitch.start()

Method parameters


Configurationobject

Additional configuration.

login_redirect_urlstring
signup_redirect_urlstring
custom_scopesstring
session_duration_minutesstring
onCompleteCallback(OAuthNativeAuthenticateResponse) => void

Example

Vanilla
import { useStytch } from '@stytch/react-native';

export const OAuthStart = () => {
  const stytch = useStytch();

  const loginWithGoogle = () => {
    stytch.oauth.google.start({
      login_redirect_url: 'https://example.com/authenticate',
      signup_redirect_url: 'https://example.com/authenticate',
    });
  };

  return (
    <View>
      <TouchableOpacity onPress={loginWithGoogle}>
        <Text>Login with Google</Text>
      </TouchableOpacity>
    </View>
  );
};

Authenticate

The authenticate method wraps the authenticate OAuth API endpoint which validates the OAuth token passed in.

If this method succeeds, the user will be logged in, granted an active session, and the session data will be persisted on device.

You can listen for successful login events anywhere in the codebase with the stytch.session.onChange() method or useStytchSession hook.


Method parameters


token*string

Configuration*object

Additional configuration.

session_duration_minutes*string

Example

Vanilla
import { useEffect, useState } from 'react';
import { useStytch } from '@stytch/react-native';

export const OAuthAuthenticate = ({ route }) => {
  const stytch = useStytch();
  const [sessionData, setSessionData] = useState();

  const authenticate = (token) => {
    stytch.oauth.authenticate(token, { session_duration_minutes: 60 }).then((resp) => setSessionData(resp));
  };

  useEffect(() => {
    if (stytch) {
      // Note: For Facebook OAuth, slice the token to the correct length (44 characters).
      const token = route.params.token.slice(0, 44);
      authenticate(token);
    }
  }, []);

  return <Text>{sessionData}</Text>;
};

Users

Once a user has successfully logged in, the SDK can be used to view and manage that user's information, add additional authentication factors, or delete factors that are no longer used.

Methods

To call these methods, Manage user data must be enabled in the SDK Configuration page of the Stytch dashboard.

Get user

The SDK provides two methods for getting a user. The recommended approach is to use the synchronous method, user.getSync, and listen to changes with the user.onChange method.

If logged in, the user.getSync method returns the cached user object. Otherwise it returns null. This method does not refresh the user's data. The @stytch/react-native and @stytch/react-native-expo libraries provide the useStytchUser hook that implements these methods for you to easily access the user and listen for changes.

The user.onChange method takes in a callback that gets called whenever the user object changes. It returns an unsubscribe method for you to call when you no longer want to listen for such changes.

The asynchronous method, user.get, wraps the get user endpoint. It fetches the user's data and refreshes the cached object if changes are detected. The Stytch SDK will invoke this method automatically in the background, so you probably won't need to call this method directly.

Example

Vanilla
import React from 'react';
import { useStytchUser } from '@stytch/react-native';

export const Home = () => {
  const { stytchUser } = useStytchUser();

  return stytchUser ? <p>Welcome, {stytchUser.name.first_name}</p> : <p>Log in to continue!</p>;
};

Update user

Wraps Stytch's update user endpoint. Use this method to change the user's name, untrusted metadata, and attributes.

You can listen for successful user updates anywhere in the codebase with the stytch.user.onChange() method or useStytchUser() hook.

Note: If a user has enrolled another MFA method, this method will require MFA. See the Multi-factor authentication section for more details.


Method parameters


nameobject

untrusted_metadataobject

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const updateName = useCallback(() => {
    stytchClient.user.update({
      name: {
        first_name: 'Jane',
        last_name: 'Doe',
      },
      untrusted_metadata: {
        display_theme: 'DARK_MODE',
      },
    });
  }, [stytchClient]);

  return (
    <>
      <button onClick={updateName}>Update name</button>
    </>
  );
};

RESPONSE

200
{
  "emails": [
    {
      "email_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953",
      "email": "sandbox@stytch.com",
      "verified": false
    }
  ],
  "phone_numbers": [
    {
      "phone_id": "phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0",
      "phone_number": "+12025550162",
      "verified": false
    }
  ],
  "crypto_wallets": [
    {
      "crypto_wallet_id": "crypto-wallet-test-dbbd372e-79f8-48ea-907c-5f0755e7d328",
      "crypto_wallet_address": "0x6df2dB4Fb3DA35d241901Bd53367770BF03123f1",
      "crypto_wallet_type": "ethereum",
      "verified": true
    }
  ],
  "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
  "status_code": 200,
  "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
  "user": {...}
}

Delete authentication factors

Wraps Stytch's delete authentication factors family of endpoints and can be used to remove factors from a user.

These methods cannot be used to remove all factors from a user. A user must have at least one email, phone number, or OAuth provider associated with their account at all times, otherwise they will not be able to log in again.

You can listen for successful user updates anywhere in the codebase with the stytch.user.onChange() method or useStytchUser() hook.

Note: If a user has enrolled another MFA method, this method will require MFA. See the Multi-factor authentication section for more details.


Method parameters


method_id*string

Example

Vanilla
import React, { useCallback } from 'react';
import { useStytch } from '@stytch/react-native';

export const Login = () => {
  const stytchClient = useStytch();

  const deleteEmail = useCallback(() => {
    stytchClient.user.deleteEmail('email-test-81bf03a8-86e1-4d95-bd44-bb3495224953');
  }, [stytchClient]);

  const deletePhoneNumber = useCallback(() => {
    stytchClient.user.deletePhoneNumber('phone-number-test-d5a3b680-e8a3-40c0-b815-ab79986666d0');
  }, [stytchClient]);

  const deleteWebauthnRegistration = useCallback(() => {
    stytchClient.user.deleteWebauthnRegistration('webauthn-registration-test-5c44cc6a-8af7-48d6-8da7-ea821342f5a6');
  }, [stytchClient]);

  return (
    <>
      <button onClick={deleteEmail}>Delete email</button>
      <button onClick={deletePhoneNumber}>Delete phone number</button>
      <button onClick={deleteWebauthnRegistration}>Delete WebAuthn registration</button>
    </>
  );
};

RESPONSE

200
{
"status_code": 200,
"request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
"user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
"user": {...}
}

Session management

The SDK may be used to check whether a user has a cached session, view the current session, refresh the session, and revoke the session. To authenticate a session on your backend, you must use either the Stytch API or a Stytch Backend SDK.

Methods

Get session

The SDK provides the session.getSync method to retrieve the current session. The session.onChange method can be used to listen to session changes.

If logged in, the session.getSync method returns the cached session object. Otherwise it returns null. The @stytch/react-native and @stytch/react-native-expo libraries provide the useStytchSession hook that implements these methods for you to easily access the session and listen for changes.

The session.onChange method takes in a callback that gets called whenever the user object changes. It returns an unsubscribe method for you to call when you no longer want to listen for such changes.

Example

Vanilla
import React, { useEffect } from 'react';
import { useStytchSession } from '@stytch/react-native';

export const withMfaRequired = (Component) => (props) => {
  const { stytchSession } = useStytchSession();

  useEffect(() => {
    if (!stytchSession || stytchSession.authentication_factors.length < 2) {
      window.location.replace('/home');
    }
  }, [stytchSession]);

  if (!stytchSession || stytchSession.authentication_factors.length) {
    return null;
  }

  return <Component {...props} />;
};

Refresh session

Wraps Stytch's authenticate Session endpoint and validates that the session issued to the user is still valid. The SDK will invoke this method automatically in the background. You probably won't need to call this method directly. It's recommended to use session.getSync and session.onChange instead.


Method parameters


Configuration*object

Additional configuration.

session_duration_minutes*string

Example

Vanilla
import React, { useEffect } from 'react';
import { useStytch } from '@stytch/react-native';

export const RefreshSession = () => {
  const stytchClient = useStytch();

  useEffect(() => {
    const refresh = () => {
      if (stytchClient.session.getSync()) {
        stytchClient.session.authenticate({
          session_duration_minutes: 60,
        });
      }
    };
    // Refresh session every 50 minutes
    let interval = setInterval(refresh, 3000000);
    return () => clearInterval(interval);
  }, [stytchClient]);

  return null;
};

RESPONSE

200
{
    "status_code": 200,
    "request_id": "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141",
    "session": {
      "attributes": {
        "ip_address": "203.0.113.1",
        "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"
      },
      "authentication_factors": [
        {
          "delivery_method": "email",
          "email_factor": {
            "email_address": "sandbox@stytch.com",
            "email_id": "email-test-81bf03a8-86e1-4d95-bd44-bb3495224953"
          },
          "last_authenticated_at": "2021-08-09T07:41:52Z",
          "type": "magic_link"
        }
      ],
      "custom_claims": {
        "claim1": "value1",
        "claim2": "value2",
      },
      "expires_at": "2021-08-10T07:41:52Z",
      "last_accessed_at": "2021-08-09T07:41:52Z",
      "session_id": "session-test-fe6c042b-6286-479f-8a4f-b046a6c46509",
      "started_at": "2021-08-09T07:41:52Z",
      "user_id": "user-test-16d9ba61-97a1-4ba4-9720-b03761dc50c6",
    },
    "session_jwt": "example_jwt"
    "session_token": "mZAYn5aLEqKUlZ_Ad9U_fWr38GaAQ1oFAhT8ds245v7Q"
    "user": {...},
  }

Get tokens

Returns the session_token and session_jwt for the active session. Otherwise retuns null.

Example

Vanilla
import React, { useEffect } from 'react';
import { useStytch, useStytchSession } from '@stytch/react-native';

export const SendSessionTokensToBackend = () => {
  const stytchClient = useStytch();
  const { session } = useStytchSession();

  // this will update the sessionTokens everytime the session updates (on login, session refresh, MFA, etc)
  const sessionTokens = useMemo(() => {
    return stytchClient.session.getTokens();
  }, [session]);

  // This useEffect block will trigger anytime the sessionTokens change
  useEffect(() => {
    if (!!sessionTokens) {
      console.log('session token', tokens.session_token);
      console.log('session jwt', tokens.session_jwt);
      // Do something with tokens
    }
  }, [sessionTokens]);

  return null;
};

Update session

Update a user's session tokens to hydrate a front-end session from the backend. For example, if you log your users in with one of our backend SDKs, you can pass the resulting session_token and session_jwt to this method to prime the frontend SDK with a valid set of tokens. You must then make an authenticate call to authenticate the session tokens and retrieve the user's current session.

Example

Vanilla
import React, { useCallback, useEffect } from 'react';
import { useStytch, useStytchUser } from '@stytch/react-native';

export const App = () => {
  const stytchClient = useStytch();
  const user = useStytchUser();

  useEffect(() => {
    if (user) {
      // redirect to logged in experience
    }
  }, [user]);

  const authenticate = useCallback(() => {
    stytchClient.session.updateSession({
      session_token: 'a session token from your backend',
      session_jwt: 'a session JWT from your backend',
    });

    stytchClient.session.authenticate({ session_duration_minutes: 60 });
  }, [stytchClient]);

  return <button onClick={authenticate}>Hydrate session</button>;
};

Resources

Migration Guide

@stytch/react-native-expo@0.7.0 and @stytch/react-native@0.6.0 updates the useStytchUser and useStytchSession hooks.

The useStytchUser and useStytchSession hooks now return envelope objects, { user, fromCache } and { session, fromCache } respectively.

On first render, the SDK will read the user or session out of local storage, and serve them with fromCache: true.

The SDK will make network requests to update the user and session objects, and serve them with fromCache: false.

Android Keystore Considerations

When a user registers a biometric factor with Stytch, our SDKs rely on the device keystore to securely store sensitive data associated with the authentication factor.

Unfortunately, Android KeyStore has had significant functionality and reliability issues throughout its lifespan in the Android ecosystem. Not all devices that support biometrics have suitable Keystore implementations that can be relied on.

The official Tink documentation references this instability (1, 2) and they have provided developers with a mechanism to ensure the device you're operating will not encounter implementation problems (3).

If KeyStore is found to be unreliable, this code will fall back to storing sensitive information in the application sandbox, as opposed to the KeyStore. This is not itself a security vulnerability, as the Android platform enforces access barriers between applications to prevent unauthorized access to your application's private data.

That said, this method comes with less security guarantees than using KeyStore, such as protecting the data in the event of a privilege escalation in Android. There are some things you should watch out for when developing an application that allows this fallback.

The biggest issue which would potentially expose your application's sandbox data is writing insecure IPC (inter-process communication). Developers can accidentally create an arbitrary file read vulnerability by misusing Android IPC (4), or by writing sensitive information to public storage (sdcard). Be extra cautious with any functionality that accepts arbitrary file paths as arguments, which may allow an attacker to redirect the flow of data to an area they can access it.