authenticateWithEmailOrPhone() and authenticateWithOAuth().
Available in v2.13.0+
While these methods manage the flow end-to-end, you are responsible for opening the portal URLs that Para generates during authentication (for verification, passkey creation, password entry, etc.). Use
para.onStatePhaseChange() to listen for these URLs and open them using the in-app browser. See Handling Portal URLs below.Prerequisites
Before implementing custom authentication, ensure you have completed the basic Para setup for your React Native or Expo application.Method Reference
This method must be paired with
para.onStatePhaseChange() to handle portal URLs that appear during authentication (verification, passkey, password, PIN). See the guide for your platform for the full state listener pattern.This method must be paired with
para.onStatePhaseChange() to handle portal URLs that appear after OAuth completes (e.g. passkey or password setup for returning users). See the guide for your platform for the full state listener pattern.Handling Portal URLs
During authentication, Para’s state machine emits portal URLs that the user must interact with (e.g. entering a verification code, creating a passkey, or entering a password). Since you’re building a custom UI, you need to subscribe to state changes and open these URLs using the in-app browser. Usepara.onStatePhaseChange() to receive a StateSnapshot containing authStateInfo — a flat object with all the URLs and flags you need:
- React Native
- Expo
authStateInfo Fields
| Field | Type | Description |
|---|---|---|
userId | string | null | The user’s ID, if available at the current stage. |
verificationUrl | string | null | Portal URL for basic login users to complete auth in the hosted portal. Only set during awaiting_session_start for basic login flows. Open in the in-app browser. |
passkeyUrl | string | null | Portal URL for passkey login or creation. Open in the in-app browser. |
passkeyKnownDeviceUrl | string | null | Portal URL for authorizing from a known device with passkey. Open in the in-app browser when present instead of passkeyUrl. |
passwordUrl | string | null | Portal URL for password login or creation. Open in the in-app browser. |
pinUrl | string | null | Portal URL for PIN login or creation. Open in the in-app browser. |
isPasskeySupported | boolean | Whether the user’s device supports passkeys/WebAuthn. |
hasPasskey | boolean | Whether the user has a passkey configured. |
hasPassword | boolean | Whether the user has a password configured. |
hasPin | boolean | Whether the user has a PIN configured. |
isNewUser | boolean | Whether this is a new signup flow. |
passkeyHints | BiometricLocationHint[] | null | Hints for known device detection. |
When
authPhase is 'awaiting_account_verification', the user is a non-basic-login new signup who has been sent an OTP code via email or SMS. There is no URL to open — you must show a code input field and call para.verifyNewAccount({ verificationCode }). If the user needs a new code, call para.resendVerificationCode({ type: 'SIGNUP' }). The simplified method is waiting for this step to complete before it proceeds. See the full example below.State Phase Reference
TheStateSnapshot returned by para.onStatePhaseChange() contains three phase fields that tell you exactly where in the flow the user is. Use these to drive your UI.
corePhase — Top-Level Lifecycle
| Phase | Description |
|---|---|
setting_up | Para is initializing. Show a loading indicator. |
auth_flow | The user is in the authentication flow. Refer to authPhase for details. |
wallet_flow | Authentication succeeded; wallets are being set up. Refer to walletPhase for details. |
authenticated | The user is fully logged in with wallets ready. |
guest_mode | The user is in guest mode (no full authentication). |
logging_out | A logout is in progress. |
error | An unrecoverable error occurred. Check snapshot.error. |
authPhase — Authentication Flow Detail
| Phase | Description | What to show |
|---|---|---|
unauthenticated | No auth in progress. | Login / signup form. |
authenticating_email_phone | Email/phone auth initiated, routing in progress. | Loading indicator. |
authenticating_oauth | Standard OAuth flow in progress. | Loading indicator or “Waiting for provider…”. |
authenticating_telegram | Telegram auth in progress. | Telegram popup/iframe. |
authenticating_farcaster | Farcaster auth in progress. | Farcaster QR code. |
processing_authentication | Server is processing the auth request. | Loading indicator. |
verifying_new_account | Verification code is being validated. | Loading indicator. |
awaiting_account_verification | New user (non-basic-login) needs to enter an OTP verification code sent to their email or phone. No URL to open — authStateInfo will not have a verificationUrl. | Show a code input field. Call para.verifyNewAccount({ verificationCode }) when submitted. |
awaiting_session_start | Portal URLs are ready. For basic login users, authStateInfo.verificationUrl is set (user completes auth in the portal). For passkey/password/PIN users, passkeyUrl, passwordUrl, and/or pinUrl are set. | Open the appropriate URL. Passkey URLs must use a popup on web. |
waiting_for_session | Session polling is active — waiting for the user to complete in the portal. | ”Completing setup…” loading state. |
authenticated | Auth flow is complete. (Core may still be in wallet_flow.) | Transition to authenticated UI, or show wallet loading indicator. |
guest_mode | The user is in guest mode (no full authentication). | Guest UI. |
error | Auth flow failed. Check snapshot.error. | Error message with retry option. |
Basic login vs passkey/password/PIN: Basic login users complete their entire authentication through a portal URL — the
verificationUrl handles OTP entry, passkey creation, etc. in a single hosted flow. Passkey, password, and PIN users go through a two-step process where OTP verification happens in your app (via awaiting_account_verification) and biometric setup happens in the portal (via awaiting_session_start).walletPhase — Wallet Setup Detail
| Phase | Description |
|---|---|
checking_wallet_state | Checking if wallets need to be created. |
needs_wallets | Wallets need to be created for this user. |
claiming_wallets | Claiming pregenerated wallets that were created for this user before signup. |
creating_wallets | Wallets are being created. |
waiting_for_wallets | Waiting for wallet creation or claiming to complete. |
setting_up_after_login | Performing post-login wallet setup (e.g. refreshing session, syncing wallet state). |
wallets_ready | Wallets are created and ready to use. |
no_wallets_needed | User already has wallets; nothing to create. |
error | Wallet creation failed. Check snapshot.error. |
Email / Phone Authentication
Usepara.authenticateWithEmailOrPhone() to authenticate a user by email or phone. The method handles the complete flow: determining whether the user is new or returning, session polling, and wallet creation. Combine it with the state listener above to open portal URLs, and handle OTP input when authPhase is 'awaiting_account_verification'.
{ phone: '+1234567890' } instead of { email }:
OAuth Authentication
Usepara.authenticateWithOAuth() to authenticate a user via a third-party OAuth provider. The method manages the OAuth redirect, polls for completion, waits for session establishment, and creates wallets for new signups.
Standard OAuth (Google, Apple, Discord, X, Facebook)
For standard OAuth providers, use theonOAuthUrl callback to open the OAuth URL in an in-app browser. The state listener from above handles any biometric URLs that appear after OAuth completes.
- React Native
- Expo
Telegram & Farcaster
Telegram and Farcaster authentication require a different approach than standard OAuth. These providers authenticate through Para’s hosted portal, which needs to send events back to the SDK when authentication completes. On mobile, this requires opening the portal in a WebView (not an in-app browser) so the portal can communicate viawindow.ReactNativeWebView.postMessage().
You must forward messages from the WebView to the SDK using para.handleWebViewMessage().
Install
react-native-webview if you haven’t already:Cancelling Authentication
Both methods accept polling callbacks with anisCanceled function. Return true from isCanceled to stop the polling loop — for example, when the user dismisses the in-app browser or navigates away. The cancellation is clean: no error is thrown, and the optional onCancel callback is fired.
para.logout() also cancels all active polling and resets the state phases back to unauthenticated. This is useful for implementing a “Cancel” button that fully resets the auth flow:
Handling Results
Both methods return anAuthenticateResponse object with the same shape:
| Field | Description |
|---|---|
authInfo | Contains the user’s primary authentication information such as email, phone, userId, and any auth extras. |
hasCreatedWallets | true if the user is new and wallets were auto-created during signup. false for returning users. |
recoverySecret | Present only for non-basic-login when new wallets are created. Basic login users do not receive a recovery secret. This should be displayed to the user or stored securely — it cannot be retrieved again. |