Privy enables users to login with SMS or WhatsApp. Configure your app following this guide and make sure to read our recipe on enabling SMS or WhatsApp.
Developers can enable either SMS or WhatsApp, but cannot utilize both. Once your account is
enabled for SMS with your chosen provider, it cannot be switched.
React
React Native
Swift
Android
Flutter
Configuring your application
Through your app’s Privy configuration, you can set the default country code for phone numbers. This is useful if your application primarily serves users from a specific country. The default country can be set in your PrivyProvider, like so:<PrivyProvider
appId="your-privy-app-id"
config={{
intl: {
defaultCountry: "US",
},
...insertTheRestOfYourPrivyProviderConfig
}}
>
{children}
</PrivyProvider>
To authenticate your users with a one-time passcode (OTP) sent to their phone number via either SMS or WhatsApp, use the useLoginWithSms hook.To authenticate your users with Privy’s out of the box UIs, check out UI components here. Send Code
sendCode: ({phoneNumber: string, disableSignup?: boolean}) => Promise<void>
Parameters
The phone number of the user to log in. Must follow specific formatting conventions (see below).
Whether to disable the ability to sign up with the phone number.
Returns
A promise that resolves when the code is sent.
The sendCode method requires a phoneNumber string param that must follow these formatting conventions:
- By default, the implicit phone number country code is +1/US.
- Explicitly prepending a
(+)1 to the phone number will still be read as a US phone number.
- For non-US phone numbers, append a
+${countryCode} to the beginning of the input value.
- Non-numerical values in the string are ignored, except for a leading
+ that denotes a custom country code.
Login with Code
loginWithCode: ({ code: string }) => Promise<void>;
Parameters
The one-time passcode sent to the user’s phone number.
Returns
A promise that resolves when the user is logged in.
Usage
import { useState } from "react";
import { useLoginWithSms } from "@privy-io/react-auth";
export default function LoginWithSms() {
const [phoneNumber, setPhoneNumber] = useState("");
const [code, setCode] = useState("");
const { state, sendCode, loginWithCode } = useLoginWithSms();
return (
<div>
{/* Prompt your user to enter their phone number */}
<input onChange={(e) => setPhoneNumber(e.currentTarget.value)} value={phoneNumber} />
{/* Once a phone number has been entered, send the OTP to it on click */}
<button onClick={() => sendCode({ phoneNumber })}>Send Code</button>
{/* Prompt your user to enter the OTP */}
<input onChange={(e) => setCode(e.currentTarget.value)} value={code} />
{/* Once an OTP has been entered, submit it to Privy on click */}
<button onClick={() => loginWithCode({ code })}>Log in</button>
</div>
);
}
Tracking Flow State
Track the state of the OTP flow via the state variable returned by the
useLoginWithSms hook.type OtpFlowState =
| {status: 'initial'}
| {status: 'error'; error: Error | null}
| {status: 'sending-code'}
| {status: 'awaiting-code-input'}
| {status: 'submitting-code'}
| {status: 'done'};
status
'initial' | 'error' | 'sending-code' | 'awaiting-code-input' | 'submitting-code' | 'done'
The current state of the OTP flow.
The error that occurred during the OTP flow.
Callbacks
You can optionally pass callbacks into the useLoginWithSms hook to run custom logic after a successful login or to handle errors that occur during the flow.onComplete
onComplete?: ((params: {
user: User;
isNewUser: boolean;
wasAlreadyAuthenticated: boolean;
loginMethod: LoginMethod | null;
loginAccount: LinkedAccountWithMetadata | null;
}) => void) | undefined
Parameters
The user object corresponding to the authenticated user.
Whether the user is a new user or an existing user.
Whether the user entered the application already authenticated.
The method used by the user to login.
loginAccount
LinkedAccountWithMetadata | null
The account corresponding to the loginMethod used.
onError
onError: (error: Error) => void
Parameters
The error that occurred during the login flow.
Resources
To authenticate your users with a one-time passcode (OTP) sent to their phone number, use the useLoginWithSMS hook.To authenticate your users with Privy’s out of the box UIs, check out UI components here. Send Code
sendCode: ({phone: string}) => Promise<{success: boolean}>
Parameters
The phone number of the user to log in.
Returns
A promise that resolves to an object with a success property indicating if the code was sent successfully.
Login with Code
loginWithCode: ({ code: string, phone?: string, disableSignup?: boolean }) => Promise<{user: PrivyUser; isNewUser: boolean}>
Parameters
The one-time passcode sent to the user’s phone number.
The user’s phone number. Though this parameter is optional, it is highly recommended that you pass the user’s phone number explicitly.
Whether to disable the ability to sign up with the phone number.
Returns
The user object returned by the login process.
Usage
import { useState } from 'react';
import { useLoginWithSMS } from '@privy-io/expo';
export function LoginScreen() {
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const { sendCode, loginWithCode } = useLoginWithSMS();
return (
<View style={styles.container}>
<Text>Login</Text>
<TextInput value={phone} onChangeText={setPhone} placeholder="Phone" inputMode="tel" />
<Button onPress={() => sendCode({ phone })}>Send Code</Button>
<TextInput value={code} onChangeText={setCode} placeholder="Code" inputMode="numeric" />
<Button onPress={() => loginWithCode({ code, phone })}>Login</Button>
</View>
);
}
Tracking login flow state
The state variable returned from useLoginWithSMS will always be one of the following values.type OtpFlowState =
| {status: 'initial'}
| {status: 'error'; error: Error | null}
| {status: 'sending-code'}
| {status: 'awaiting-code-input'}
| {status: 'submitting-code'}
| {status: 'done'};
status
'initial' | 'error' | 'sending-code' | 'awaiting-code-input' | 'submitting-code' | 'done'
The current state of the SMS login flow.
The error that occurred during the SMS login flow.
Callbacks
You can optionally pass callbacks into the useLoginWithSMS hook to run custom logic after an OTP has been sent, after a successful login, or to handle errors that occur during the flow.onSendCodeSuccess
onSendCodeSuccess?: ((phone: string) => void) | undefined
Parameters
The phone number the code was sent to.
onLoginSuccess
onLoginSuccess?: ((user: User, isNewUser: boolean) => void) | undefined
Parameters
The PrivyUser returned by loginWithCode.
Whether the user is a new user or an existing user.
onError
onError?: (error: Error) => void
Parameters
The error that occurred during the login flow.
Resources
To authenticate a user via their phone number, use the Privy client’s sms handler.Send Code
sendCode(to phoneNumber: String) async throws
Parameters
The phone number of the user to log in. Must be in E.164 format (e.g., “+14155552671”).
Returns
Nothing, indicating success.
Throws
An error if sending the code fails.Login with Code
loginWithCode(_ code: String, sentTo phoneNumber: String) async throws -> PrivyUser
Parameters
The one-time passcode sent to the user’s phone number.
Returns
The authenticated Privy user
Throws
An error if logging the user in is unsuccessful.Usage
// Send code to user's phone
do {
try await privy.sms.sendCode(to: "+14155552671")
// successfully sent code to users phone
} catch {
print("error sending code: \(error)")
}
// Log the user in
do {
let user = try await privy.sms.loginWithCode("123456", sentTo: "+14155552671")
// user successfully logged in
} catch {
print("error logging user in: \(error)")
}
To authenticate a user via their phone number, use the Privy client’s sms handler.Send Code
sendCode(phoneNumber: String): Result<Unit>
Parameters
The phone number of the user to log in. Must be in E.164 format (e.g., “+14155552671”).
Returns
A Result object that indicates whether the operation was successful. Returns Result.success if the code was sent successfully, or Result.failure if there was an error.
Login with Code
loginWithCode(code: String, phoneNumber: String?): Result<PrivyUser>
Parameters
The one-time passcode sent to the user’s phone number.
(Optional) The user’s phone number. Though this parameter is optional, it is highly recommended that you pass the user’s phone number explicitly. If phone number is omitted, the phone number from sendCode will be used.
Returns
A Result object containing the PrivyUser if successful, or an error if the operation failed.
Usage
// Send code to user's phone number
val result: Result<Unit> = privy.sms.sendCode(phoneNumber = "+14155552671")
result.fold(
onSuccess = {
// OTP was successfully sent
},
onFailure = {
println("Error sending OTP: ${it.message}")
}
)
// Authenticate with the OTP code
val result: Result<PrivyUser> = privy.sms.loginWithCode(code = "123456", phoneNumber = "+14155552671")
result.fold(
onSuccess = { user ->
// User logged in
},
onFailure = {
println("Error logging in user: ${it.message}")
}
)
To authenticate a user via their phone number, use the Privy client’s sms handler.Send Code
sendCode(String phoneNumber): Future<Result<void>>
Parameters
The phone number of the user to log in. Must be in E.164 format (e.g., “+14155552671”).
Returns
A Result object that indicates whether the operation was successful. Returns Result.success if the code was sent successfully, or Result.failure if there was an error.
Login with Code
loginWithCode({required String code, String? phoneNumber}): Future<Result<PrivyUser>>
Parameters
The one-time passcode sent to the user’s phone number.
(Optional) The user’s phone number. Though this parameter is optional, it is highly recommended that you pass the user’s phone number explicitly. If phone number is omitted, the phone number from sendCode will be used.
Returns
Result<PrivyUser>
Future<Result<PrivyUser>>
A Result object containing the PrivyUser if successful, or an error if the operation failed.
Usage
// Send code to user's phone number
final Result<void> result = await privy.sms.sendCode("+14155552671");
result.fold(
onSuccess: (_) {
// OTP was sent successfully
},
onFailure: (error) {
// Handle error sending OTP
print(error.message);
},
);
// Authenticate with the OTP code
final Result<PrivyUser> result = await privy.sms.loginWithCode(
code: code,
phoneNumber: phoneNumber,
);
result.fold(
onSuccess: (user) {
// User authenticated successfully
},
onFailure: (error) {
// Handle authentication error
},
);
Resources