Documentation
/
SDK Reference
/
Frontend
/
React Native
/
Hawcx React Native SDK (V6)

Hawcx React Native SDK (V6)

Integrate Hawcx V6 in React Native with an adaptive authentication flow, redirects, backend exchange, and trusted-device support.

Overview

Hawcx V6 for React Native gives you a prompt-driven authentication flow that adapts to the authentication methods configured for your project. Your app starts a flow, renders the next prompt Hawcx returns, and completes sign-in by exchanging the resulting authorization code on your backend.

This is the recommended React Native integration path for new projects using Hawcx V6.

V6 docs, current React API names

The React Native package already exposes HawcxV6* types and helpers such as useHawcxV6Auth, startV6Flow, and hawcxV6Client. Under the hood, the bridge delegates to the production Hawcx iOS and Android SDKs.

Already using the existing React Native SDK flow?

If your app is already integrated with the existing V5 surface such as useHawcxAuth, authenticate, and submitOtp, continue using the V5 React Native guide. This page is for the new V6 integration path only.

Which Guide Should You Use?

Use this V6 guide if:

  1. you are starting a new React Native integration
  2. you want an adaptive flow with backend-selected primary, MFA, redirect, and trusted-device steps
  3. you want to use the current HawcxV6* React API surface

Use the V5 React Native guide if:

  1. your app already ships the existing V5 integration
  2. you still depend on authenticate, submitOtp, useHawcxAuth, or older auth events
  3. you are maintaining an existing production integration and are not ready to migrate yet

Incremental migration is supported

The React Native package still includes the older V5 helpers and bridge surface, so you can adopt the V6 flow in new screens while keeping existing V5 flows in place during migration.

Before You Begin

Before integrating the React Native SDK, make sure your Hawcx project is configured in the Hawcx dashboard:

  1. create or select your project
  2. configure the primary authentication step
  3. configure MFA and trusted-device behavior if needed
  4. generate a Config ID for the SDK

For product configuration details, see:

  1. Projects
  2. Config IDs
  3. Auth Flow Configuration

How the V6 Flow Works

  1. Your app initializes the React Native package with your Config ID and tenant host.
  2. You start the flow with a flowType, usually signin, plus the user's identifier.
  3. The native SDK returns the next prompt for that user and project configuration.
  4. Your React UI renders the current step:
    • choose a method
    • enter a verification code
    • set up SMS
    • set up TOTP
    • continue a redirect
    • wait for approval
  5. When the flow completes, the SDK returns authCode and, when applicable, codeVerifier.
  6. Your backend exchanges the code and creates the user session.

Integration Responsibilities

Your app is responsible for:

  1. collecting the user's identifier
  2. rendering the current prompt
  3. sending the user's input back to the SDK
  4. handling redirects when prompted
  5. sending the final authorization code to your backend

The SDK handles:

  1. native protocol requests and required headers
  2. PKCE generation when needed
  3. trusted-device storage and secure device credentials
  4. device-trust processing
  5. approval polling when the flow requires it

Your backend handles:

  1. exchanging the authorization code
  2. verifying returned claims
  3. creating the app session
  4. returning the result your app needs to continue

What the SDK handles automatically

The React Native package keeps the JavaScript surface small while the native iOS and Android SDKs manage the protocol details for you, including PKCE when needed, secure trusted-device storage, and device-trust processing.

Requirements

  1. React Native 0.73+
  2. iOS 17.5+ for the bundled Hawcx iOS framework
  3. Android API 26+
  4. A Hawcx Config ID for your project
  5. Your Hawcx tenant base URL

Example base URL:

https://stage-api.hawcx.com

For the recommended V6 path, pass the tenant host root only. The native SDKs derive the correct V5 and V6 routes from that host. Do not append /v1, /auth, or /hc_auth yourself.

Install

1. Add the package

npm install @hawcx/[email protected]

Pin to the latest released version your team has approved for production. Version 1.1.5 is the current React Native V6 release documented on this page.

2. Install iOS dependencies

cd ios
pod install
cd ..

The package vendors HawcxFramework.xcframework through its Podspec, so no manual iOS framework setup is required.

3. Configure Android Maven resolution

The React Native package depends on the released Hawcx Android SDK, so your Android app must be able to resolve the Hawcx Maven repo.

Add the Hawcx Maven repository:

android/settings.gradle
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/hawcx/hawcx_android_sdk/main/maven")
            metadataSources {
                mavenPom()
                artifact()
            }
        }
    }
}

If your project still declares flatDir { dirs("libs") } or bundles a local hawcx-*.aar, remove that and let Gradle resolve the native SDK from Maven.

Public Android Maven repo

The hosted Hawcx Maven repository is public. You do not need a GitHub token for the standard React Native setup shown here.

4. Rebuild the app

npm run ios
# or
npm run android

Android note

If your Android environment caches an older dependency graph, run cd android && ./gradlew clean once before rebuilding.

Initialize the SDK

import { initialize } from '@hawcx/react-native-sdk';

await initialize({
  projectApiKey: '<YOUR_CONFIG_ID>',
  baseUrl: 'https://stage-api.hawcx.com',
  autoPollApprovals: true,
});

Initialization Notes

  1. projectApiKey is the Hawcx value provisioned for this integration. In current public releases, this is the same value you may receive as your project API key / Config ID.
  2. baseUrl should point to your Hawcx tenant host root. Let the native SDKs derive the V5 and V6 routes from that host.
  3. autoPollApprovals defaults to true, which is the right choice for most apps.
  4. relyingParty is optional. Set it only when your backend expects the X-Relying-Party header for this integration.
  5. oauthConfig is not required for the recommended V6 flow. Keep OAuth client credentials on your backend.
  6. endpoints.authBaseUrl remains available only for backward compatibility and should be treated as deprecated in new integrations.

Build an Auth Screen

The recommended integration shape for React Native is a screen or coordinator that:

  1. holds the current HawcxV6AuthState
  2. starts the flow
  3. reacts to the current prompt
  4. forwards the user's input back to the SDK
import { useState } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import {
  useHawcxV6Auth,
  type HawcxV6Method,
} from '@hawcx/react-native-sdk';

export function V6AuthScreen() {
  const [identifier, setIdentifier] = useState('');
  const [code, setCode] = useState('');
  const [phone, setPhone] = useState('');
  const [totp, setTotp] = useState('');
  const v6 = useHawcxV6Auth(undefined, { flowType: 'signin' });

  const start = async () => {
    await v6.start({
      flowType: 'signin',
      identifier: identifier.trim(),
    });
  };

  const renderPrompt = () => {
    switch (v6.state.status) {
      case 'select_method':
        return v6.state.prompt?.prompt.type === 'select_method'
          ? v6.state.prompt.prompt.methods.map((method: HawcxV6Method) => (
              <Button
                key={method.id}
                title={method.label}
                onPress={() => void v6.selectMethod(method.id)}
              />
            ))
          : null;

      case 'enter_code':
        return (
          <>
            <TextInput value={code} onChangeText={setCode} keyboardType="number-pad" />
            <Button title="Continue" onPress={() => void v6.submitCode(code)} />
          </>
        );

      case 'setup_sms':
        return (
          <>
            <TextInput value={phone} onChangeText={setPhone} keyboardType="phone-pad" />
            <Button title="Continue" onPress={() => void v6.submitPhone(phone)} />
          </>
        );

      case 'setup_totp':
        return (
          <>
            <Text>Scan the authenticator QR code, then enter a fresh code.</Text>
            <TextInput value={totp} onChangeText={setTotp} />
            <Button title="Continue" onPress={() => void v6.submitTotp(totp)} />
          </>
        );

      case 'enter_totp':
        return (
          <>
            <TextInput value={totp} onChangeText={setTotp} />
            <Button title="Continue" onPress={() => void v6.submitTotp(totp)} />
          </>
        );

      case 'await_approval':
        return <Text>Waiting for approval...</Text>;

      case 'redirect':
        return <Text>Continue in the browser to finish this step.</Text>;

      default:
        return null;
    }
  };

  return (
    <View>
      <TextInput value={identifier} onChangeText={setIdentifier} />
      <Button title="Continue" onPress={() => void start()} />
      {renderPrompt()}
    </View>
  );
}

Starting Authentication

flowType supports:

  • signin
  • signup
  • account_manage

Most apps should start with signin:

await v6.start({
  flowType: 'signin',
  identifier: '[email protected]',
});

Additional fields are available when your tenant policy requires them:

await v6.start({
  flowType: 'signin',
  identifier: '[email protected]',
  startToken: '<optional-start-token>',
  inviteCode: '12345678',
});

startToken is optional and is only needed when your backend or hosted flow gives your app a pre-issued token to resume or continue a specific start path.

You can also pass codeChallenge, but in most cases you do not need to. If you omit it, the native SDK generates PKCE automatically and returns codeVerifier in the completed event.

Flow State and Prompt Types

useHawcxV6Auth() exposes a normalized React state machine.

Possible state.status values:

  • identifier
  • loading
  • select_method
  • enter_code
  • setup_sms
  • setup_totp
  • enter_totp
  • redirect
  • await_approval
  • completed
  • error

Helpful state fields:

  1. state.step
    • label the current stage such as Primary, MFA, or Device Trust
  2. state.risk
    • show a step-up or risk warning when the backend flags the session
  3. state.traceId
    • capture this in logs or support tickets when it is returned
  4. state.resend
    • contains resend availability and countdown metadata
  5. state.isLoading, state.requiresRedirect, and state.awaitingApproval
    • use these convenience flags for button states and UI transitions

Resend, Polling, and Flow Control

useHawcxV6Auth() exposes:

  • resend()
  • poll()
  • cancel()
  • changeIdentifier()
  • reset()
  • canResend
  • resendAvailability
  • secondsUntilResend

For email OTP and SMS OTP steps, use the resend helpers:

if (v6.canResend) {
  await v6.resend();
}

When the current prompt is await_approval, polling is handled automatically when autoPollApprovals is enabled. You can still call poll() manually if your product needs explicit refresh controls.

Use changeIdentifier() when the user wants to go back and enter a different email or phone number without remounting the whole screen.

Imperative Client and Low-Level Events

If you prefer an imperative integration instead of a React hook, use hawcxV6Client:

import {
  addV6FlowListener,
  hawcxV6Client,
} from '@hawcx/react-native-sdk';

const subscription = addV6FlowListener((event) => {
  console.log(event.type, event.payload);
});

await hawcxV6Client.start({
  flowType: 'signin',
  identifier: '[email protected]',
});

subscription.remove();

This is useful in Redux sagas, state machines, or non-React coordinators.

Handle Redirect Steps

When the current prompt is redirect, open the URL in the browser and forward the return URL back into the SDK.

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

useEffect(() => {
  const subscription = Linking.addEventListener('url', ({ url }) => {
    void v6.handleRedirectUrl(url);
  });

  return () => subscription.remove();
}, [v6.handleRedirectUrl]);

const openRedirect = async () => {
  if (v6.state.prompt?.prompt.type !== 'redirect') {
    return;
  }

  await Linking.openURL(v6.state.prompt.prompt.url);
};

React Native handles the JavaScript side of the callback, but you still need to register your callback scheme natively on iOS and Android so the app receives the return URL.

Platform-specific references:

Handle Completion and Backend Exchange

When the flow completes, the SDK returns:

  1. session
  2. authCode
  3. expiresAt
  4. codeVerifier when PKCE was generated by the SDK
  5. traceId for correlation and support logs, when returned

Use authCode and codeVerifier as the primary inputs for the backend exchange. expiresAt, session, and traceId are useful for telemetry, timing, and support tooling, but they are not the core exchange inputs. Treat traceId as optional; some successful flows can complete without one.

Include codeVerifier whenever the SDK returns it. If your app supplied a custom codeChallenge, send the matching verifier from that flow to your backend as part of the exchange.

Send the completion payload to your backend immediately over HTTPS. Do not perform the code exchange from the React Native app.

Recommended payload shape:

{
  "authCode": "<authCode>",
  "codeVerifier": "<optional-codeVerifier>",
  "identifier": "[email protected]",
  "session": "<optional-session>"
}

Your backend should:

  1. exchange authCode and codeVerifier with the Hawcx backend SDK
  2. verify the returned claims
  3. create your app session or tokens
  4. return the app auth result your React Native app needs

React Native app-to-backend example

import { useEffect } from 'react';
import {
  storeBackendOAuthTokens,
  useHawcxV6Auth,
} from '@hawcx/react-native-sdk';

useEffect(() => {
  if (v6.state.status !== 'completed' || !v6.state.completed) {
    return;
  }

  const run = async () => {
    const response = await fetch('https://your-backend.example.com/api/hawcx/exchange', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        authCode: v6.state.completed.authCode,
        codeVerifier: v6.state.completed.codeVerifier,
        identifier,
        session: v6.state.completed.session,
      }),
    });

    if (!response.ok) {
      throw new Error('Backend exchange failed');
    }

    const result = await response.json();

    if (result.accessToken) {
      await storeBackendOAuthTokens(
        identifier,
        result.accessToken,
        result.refreshToken,
      );
    }
  };

  void run();
}, [identifier, v6.state]);

This example shows the app-to-backend handoff only. Your backend then uses the Hawcx backend SDK to exchange and verify the code before it creates the user session.

Backend implementation guides:

QR Approvals and Web Login Helpers

The React Native package also exposes helpers for QR approval and legacy web-login scans:

  • routeWebLoginScan(raw)
  • approveV6Qr(rawPayload, identifier, options)
  • useHawcxWebLogin()

Use routeWebLoginScan(raw) to decide whether a scanned payload is:

  • a protocol QR payload (protocol_qr)
  • a legacy PIN flow (legacy_pin)
  • invalid input (invalid)

For protocol QR payloads, you can approve directly from React Native:

import { approveV6Qr, routeWebLoginScan } from '@hawcx/react-native-sdk';

const route = routeWebLoginScan(scanValue);
if (route.kind === 'protocol_qr') {
  const result = await approveV6Qr(route.payload.raw, identifier, {
    rememberDevice: true,
  });

  console.log(result.outcome, result.payloadType);
}

Existing V5 and Shared Helpers

The package still includes the older React Native auth surface for apps that already use it, including:

  • authenticate, submitOtp, useHawcxAuth
  • useHawcxWebLogin
  • setPushDeviceToken
  • notifyUserAuthenticated
  • handlePushNotification, approvePushRequest, declinePushRequest

Shared helpers such as initialize and storeBackendOAuthTokens remain available across the current package surface.

That makes incremental migration possible: new V6 flows can live on useHawcxV6Auth while existing utilities remain available in the same package.