Prerequisites
- Server integration — Your backend must support the flows this SDK expects—for example issuing an
authTokenfrom your server SDK’s start/initialize call, plus HTTP endpoints your app invokes from finish steps (such as validate, verify, or complete). Use your product’s implementation guide together with this page. - Prove credentials — Sandbox or Production access from the Prove Portal, configured for your application.
- Phone-based flow — A customer phone number where your flow requires it. For Mobile Auth, end users may need to disable VPN or Private Relay (see per-platform warnings below).
- Android
- iOS
- Web
Installation
The Android SDK is a set of lightweight libraries. The libraries deliver as Android Archive Repository,.aar, files. The minimum supported version of Android is v7, level 24.Prove manages a maven repository with Android binaries to enable integration with Gradle.Update the dependencies object in the build.gradle file:dependencies {
// Existing dependencies are here.
// Add the Prove Link dependencies:
implementation 'com.prove.sdk:proveauth:6.10.7'
}
settings.gradle file with the Maven repository:dependencyResolutionManagement {
// Existing repository settings are here.
repositories {
// Existing repositories are here.
// Add the Prove Link Maven repository:
maven {
url = "https://prove.jfrog.io/artifactory/libs-public-maven/"
}
}
}
build.gradle file to also download dependency libraries:dependencies {
implementation fileTree('libs')
}
application@fullBackupContent value, you can resolve it by adding this line of code to your application AndroidManifest.xml file inside the <application>...</application> node. Add it as an attribute to the opening application tag:<application
...
tools:replace="android:fullBackupContent"
...>
</application>
Permissions
The Prove Auth SDK and its children SDKs merge the following permissions into the main app:<!-- Required to perform authentication -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required to access information about networks -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Required for ConnectivityManager.requestNetwork -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
Send the type of flow: mobile
Unlike the Web SDK, when using the Android SDK, use the mobile flow. Passmobile to the Start() function on the server. In a mobile flow, Mobile Auth℠ executes first and if that fails, performs OTP validation on the mobile phone.In the mobile flow, once either Mobile Auth or the OTP validation is complete, the AuthFinishStep function executes.Mobile AuthMobile Auth requires the customer to disable any VPN.
Authenticate()
The SDK requires anauthToken as a parameter for the Authenticate() function. This token returns from the Start() call of the server SDK. The token is session specific, limiting it to a single flow. It also expires after 15 minutes.Retrieve authToken
Send a request to your backend server with the phone number, flow type, and an optional challenge of either the date of birth or social security number.String initialize(String phoneNumber, String ssn, String flowType) {
YourBackendClient backend = new YourBackendClient(); // Backend API client
// TODO: Build your InitializeRequest object
InitializeRequest initializeRequest = new InitializeRequest(phoneNumber, ssn, flowType);
// Send an initialize request to your backend server to get authToken
InitializeResponse response = backend.initialize(initializeRequest);
// TODO: define your own InitializeResponse object to parse authToken string
return response.getAuthToken();
}
Setup authenticator
Once you have theauthToken, build the authenticator for the mobile flow.// Object implementing AuthFinishStep interface
AuthFinishStep authFinishStep = new AuthFinishStep() {
...
};
// Objects implementing OtpStartStep/OtpFinishStep interfaces
OtpStartStep otpStartStep = new OtpStartStep() {
...
};
OtpFinishStep otpFinishStep = new OtpFinishStep() {
...
};
ProveAuth proveAuth = ProveAuth.builder()
.withAuthFinishStep(authId -> verify(authId)) // verify(authId) call defined in #Validate the Mobile Phone section
.withOtpFallback(otpStartStep, otpFinishStep)
.withContext(this)
.build();
Builder class offers a withTestMode(boolean testMode) method, which permits simulated successful session results while connected to a Wi-Fi network only. Testing using a Wi-Fi connection is useful in the Sandbox environment.ProveAuth proveAuth = ProveAuth.builder()
.withAuthFinishStep(authId -> verify(authId))
.withOtpFallback(otpStartStep, otpFinishStep)
.withContext(this)
.withTestMode(true) // Test mode flag
.build();
Performing the authentication
TheProveAuth object is thread safe. You can use it as a singleton. Most Prove Auth methods are blocking and therefore can’t execute in the main app thread. The app employs an executor service with a minimum of two threads to manage threads due to the ability to process concurrent blocking requests.public class MyAuthenticator {
private final MyBackendClient backend = new MyBackendClient(); // Backend API client
private ExecutorService executor = Executors.newCachedThreadPool();
private final AuthFinishStep authFinishStep = new AuthFinishStep() {
@Override
void execute(String authId) {
try {
AuthFinishResponse response = backend.authFinish("My App", authId);
... // Check the authentication status returned in the response
} catch (IOException e) {
String failureCause = e.getCause() != null ? e.getCause().getMessage() : "Failed to request authentication results";
// Authentication failed due to request failure
}
}
};
private ProveAuth proveAuth;
public MyAuthenticator(Context context) {
proveAuth = ProveAuth.builder()
.withAuthFinishStep(authFinishStep)
.withOtpFallback(otpStartStep, otpFinishStep)
.withContext(context)
.build();
}
public void authenticate() throws IOException, ProveAuthException {
AuthStartResponse response = backend.authStart("My Prove Auth App");
proveAuth.authenticate(response.getAuthToken());
}
}
public void authenticate() throws IOException, ProveAuthException {
// NOTE: blocking method proveAuth.authenticate() should be run in background thread
executor.submit(() -> {
AuthStartResponse response = backend.authStart("My Prove Auth App");
proveAuth.authenticate(response.getAuthToken());
}
}
Validate the mobile phone
In the AuthFinishStep, specify a function to call once the possession checks are complete on the mobile phone. This endpoint on your back end server calls theValidate() function to check phone number validation. If it was successful, the server returns the results from the Challenge() function, including customer information.// Send a verify request to get return customer information.
void verify(String: authId) {
YourBackendClient backend = new YourBackendClient(); // Backend API client
// Build your VerifyRequest object
VerifyRequest verifyRequest = new VerifyRequest(authId, ...);
// Send a verify request to your backend server to get return customer information.
VerifyResponse response = backend.verify(verifyRequest);
// TODO: define your VerifyResponse object to parse customer information from the response
String firstName = response.getFirstName();
String lastName = response.getLastName();
// Pre-fill customer information to your Android App UI, for example:
firstNameEditText.setText(firstName);
lastNameEditText.setText(lastName);
}
Configure OTP
Use this section to configure SMS OTP fallback on Android: wireOtpStartStep and OtpFinishStep so the SDK can send the code, collect the PIN, and optionally support resend, OTP retry, or phone-number change.Prerequisites
- After Prove sends the SMS, the customer has about two minutes to enter the OTP before the session times out.
- When building the authenticator, call
withOtpFallback(otpStart: OtpStartStep, otpFinish: OtpFinishStep)and implement bothOtpStartStepandOtpFinishStep. The Prove client SDK orchestrates when each step runs—implement the interfaces, but do not duplicate that orchestration in app code (see Invalid OTP and step orchestration below).
Invalid OTP and step orchestrationWhen the customer enters an invalid OTP, the Prove client SDK detects it. Typical signs include a validation or reported error such as code
10001 with a message that the PIN doesn’t match. You may also see flow activity related to AuthFinishStep, such as ProveAuth.builder().withAuthFinishStep(…), that looks like an unexpected redirect.This behavior is expected. Do not add your own follow-up call to OtpFinishStep.execute to pass the error in otpException. The SDK invokes OtpFinishStep.execute when a retry or error UI is needed.Your app should implement the required step interfaces, but the Prove client SDK orchestrates when each step runs. Don’t duplicate that orchestration in application code.Configure the client
Implement start and finish for your use case
Open the tab that matches how the phone number is collected and which OTP options you need.Allow a new OTP SMS to the same number (up to three send attempts including the first).Implement the start step:Then implement the finish step so the customer can request another SMS, for example:Allow the customer to re-enter the OTP after a wrong PIN (up to three attempts). On the server, pass Implement the finish step—no extra client changes. If the OTP is invalid, the finish step drives retry UI until attempts are exhausted, then Allow the customer to re-enter the phone number (up to three entries/send attempts).Implement the start step:Then implement the finish step so the customer can supply a new number, for example:
- Default
- Prompt for Phone Number
- Resend
- Retry OTP
- Phone Number Change
Use this path when the server already has the phone number (for example from Call
POST /v3/start or your server Start wrapper) and the client must not prompt for it again.Call OtpStartStepCallback.onSuccess(OtpStartInput) with an OtpStartInput instance that reflects “number already known”—typically an empty string or null for the phone value, as in the snippet—so the SDK knows the customer agreed to receive the SMS.Java
import com.prove.sdk.proveauth.OtpStartInput;
import com.prove.sdk.proveauth.OtpStartStep;
import com.prove.sdk.proveauth.OtpStartStepCallback;
import com.prove.sdk.proveauth.ProveAuthException;
public class NoPromptStart implements OtpStartStep {
@Override
public void execute(boolean phoneNumberNeeded, ProveAuthException otpException,
OtpStartStepCallback callback) {
// No phone number needed, no need to ask end user for input.
callback.onSuccess(new OtpStartInput(""));
}
}
OtpStartStepCallback.onError() if something prevents sending the SMS (for example the customer cancels or leaves the flow with the back button).In the finish step, call OtpFinishStepCallback.onSuccess(OtpFinishInput) with the OTP the customer entered, wrapped in OtpFinishInput.Java
import com.prove.sdk.proveauth.OtpFinishInput;
import com.prove.sdk.proveauth.OtpFinishStep;
import com.prove.sdk.proveauth.OtpFinishStepCallback;
import com.prove.sdk.proveauth.OtpValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class NoPromptFinish implements OtpFinishStep {
@Override
public void execute(@Nullable ProveAuthException otpException,
OtpFinishStepCallback otpFinishStepCallback) {
// If error message is found, handle it.
if (otpException instanceof OtpValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for OTP delivered by SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForOtpCode which gives us the OTP code.
String otpCode = promptForOtpCode();
otpFinishStepCallback.onSuccess(new OtpFinishInput(otpCode));
} catch (Exception e) {
// if any issue with the OTP collection from the end user or the user wants to cancel
// then call onError to exit the flow. In this example we simplify it as catching
// an exception.
otpFinishStepCallback.onError();
}
}
}
Use this path when the Android app collects the phone number in the client and you do not need SMS resend, dedicated OTP retry handling beyond defaults, or phone-number change. For those capabilities, use Resend, Retry OTP, or Phone Number Change.In the start step, call Implement the finish step:
OtpStartStepCallback.onSuccess(OtpStartInput) with the collected number.Java
import com.prove.sdk.proveauth.OtpStartInput;
import com.prove.sdk.proveauth.OtpStartStep;
import com.prove.sdk.proveauth.OtpStartStepCallback;
import com.prove.sdk.proveauth.PhoneNumberValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class PromptStart implements OtpStartStep {
@Override
public void execute(boolean phoneNumberNeeded, @Nullable ProveAuthException otpException,
OtpStartStepCallback callback) {
// If phone number is needed, need to ask the end user for phone number input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (otpException instanceof PhoneNumberValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for phone number to receive OTP SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForPhoneNumber which gives us the collected
// phone number.
String phoneNumber = promptForPhoneNumber();
callback.onSuccess(new OtpStartInput(phoneNumber));
} catch (Exception e) {
// if any issue with the phone number collection from the end user or the user
// wants to cancel then call onError to exit the flow.
// In this example we simplify it as catching an exception.
callback.onError();
}
} else {
// No phone number needed, no need to ask end user for input.
callback.onSuccess(new OtpStartInput(""));
}
}
}
Java
import com.prove.sdk.proveauth.OtpFinishInput;
import com.prove.sdk.proveauth.OtpFinishStep;
import com.prove.sdk.proveauth.OtpFinishStepCallback;
import com.prove.sdk.proveauth.OtpValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class NoPromptFinish implements OtpFinishStep {
@Override
public void execute(@Nullable ProveAuthException otpException,
OtpFinishStepCallback otpFinishStepCallback) {
// If error message is found, handle it.
if (otpException instanceof OtpValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for OTP delivered by SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForOtpCode which gives us the OTP code.
String otpCode = promptForOtpCode();
otpFinishStepCallback.onSuccess(new OtpFinishInput(otpCode));
} catch (Exception e) {
// if any issue with the OTP collection from the end user or the user wants to cancel
// then call onError to exit the flow. In this example we simplify it as catching
// an exception.
otpFinishStepCallback.onError();
}
}
}
To use the Resend/Retry/Phone Change features, install the Android SDK version 6.5.0 or later.
Java
import com.prove.sdk.proveauth.OtpStartInput;
import com.prove.sdk.proveauth.OtpStartStep;
import com.prove.sdk.proveauth.OtpStartStepCallback;
import com.prove.sdk.proveauth.PhoneNumberValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class PromptStart implements OtpStartStep {
@Override
public void execute(boolean phoneNumberNeeded, @Nullable ProveAuthException otpException,
OtpStartStepCallback callback) {
// If phone number is needed, need to ask the end user for phone number input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (otpException instanceof PhoneNumberValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for phone number to receive OTP SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForPhoneNumber which gives us the collected
// phone number.
String phoneNumber = promptForPhoneNumber();
callback.onSuccess(new OtpStartInput(phoneNumber));
} catch (Exception e) {
// if any issue with the phone number collection from the end user or the user
// wants to cancel then call onError to exit the flow.
// In this example we simplify it as catching an exception.
callback.onError();
}
} else {
// No phone number needed, no need to ask end user for input.
callback.onSuccess(new OtpStartInput(""));
}
}
}
Java
import com.prove.sdk.proveauth.OtpFinishInput;
import com.prove.sdk.proveauth.OtpFinishStep;
import com.prove.sdk.proveauth.OtpFinishStepCallback;
import com.prove.sdk.proveauth.OtpValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class MultipleResendFinish implements OtpFinishStep {
@Override
public void execute(@Nullable ProveAuthException otpException,
OtpFinishStepCallback otpFinishStepCallback) {
// If error message is found, handle it.
if (otpException instanceof OtpValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
// Prompt the user for whether they received the SMS.
if (promptForResend("Didn't receive the SMS OTP? Click resend button for a new one!")) {
// If the end user wants to send again to the same phone number call onOtpResend().
otpFinishStepCallback.onOtpResend();
return;
}
try {
// Prompt the user for OTP delivered by SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForOtpCode which gives us the OTP code.
String otpCode = promptForOtpCode();
otpFinishStepCallback.onSuccess(new OtpFinishInput(otpCode));
} catch (Exception e) {
// if any issue with the OTP collection from the end user or the user wants to cancel
// then call onError to exit the flow. In this example we simplify it as catching
// an exception.
otpFinishStepCallback.onError();
}
}
}
To use the Resend/Retry/Phone Change features, install the Android SDK version 6.5.0 or later.
allowOTPRetry=true on POST /v3/start (or your equivalent Start request).Implement the start step—no extra client changes beyond your normal prompt path:Java
import com.prove.sdk.proveauth.OtpStartInput;
import com.prove.sdk.proveauth.OtpStartStep;
import com.prove.sdk.proveauth.OtpStartStepCallback;
import com.prove.sdk.proveauth.PhoneNumberValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class PromptStart implements OtpStartStep {
@Override
public void execute(boolean phoneNumberNeeded, @Nullable ProveAuthException otpException,
OtpStartStepCallback callback) {
// If phone number is needed, need to ask the end user for phone number input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (otpException instanceof PhoneNumberValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for phone number to receive OTP SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForPhoneNumber which gives us the collected
// phone number.
String phoneNumber = promptForPhoneNumber();
callback.onSuccess(new OtpStartInput(phoneNumber));
} catch (Exception e) {
// if any issue with the phone number collection from the end user or the user
// wants to cancel then call onError to exit the flow.
// In this example we simplify it as catching an exception.
callback.onError();
}
} else {
// No phone number needed, no need to ask end user for input.
callback.onSuccess(new OtpStartInput(""));
}
}
}
AuthFinish runs.Java
import com.prove.sdk.proveauth.OtpFinishInput;
import com.prove.sdk.proveauth.OtpFinishStep;
import com.prove.sdk.proveauth.OtpFinishStepCallback;
import com.prove.sdk.proveauth.OtpValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class NoPromptFinish implements OtpFinishStep {
@Override
public void execute(@Nullable ProveAuthException otpException,
OtpFinishStepCallback otpFinishStepCallback) {
// If error message is found, handle it.
if (otpException instanceof OtpValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for OTP delivered by SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForOtpCode which gives us the OTP code.
String otpCode = promptForOtpCode();
otpFinishStepCallback.onSuccess(new OtpFinishInput(otpCode));
} catch (Exception e) {
// if any issue with the OTP collection from the end user or the user wants to cancel
// then call onError to exit the flow. In this example we simplify it as catching
// an exception.
otpFinishStepCallback.onError();
}
}
}
To use the Resend/Retry/Phone Change features, install the Android SDK version 6.5.0 or later.
Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
Java
import com.prove.sdk.proveauth.OtpStartInput;
import com.prove.sdk.proveauth.OtpStartStep;
import com.prove.sdk.proveauth.OtpStartStepCallback;
import com.prove.sdk.proveauth.PhoneNumberValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class PromptStart implements OtpStartStep {
@Override
public void execute(boolean phoneNumberNeeded, @Nullable ProveAuthException otpException,
OtpStartStepCallback callback) {
// If phone number is needed, need to ask the end user for phone number input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (otpException instanceof PhoneNumberValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
try {
// Prompt the user for phone number to receive OTP SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForPhoneNumber which gives us the collected
// phone number.
String phoneNumber = promptForPhoneNumber();
callback.onSuccess(new OtpStartInput(phoneNumber));
} catch (Exception e) {
// if any issue with the phone number collection from the end user or the user
// wants to cancel then call onError to exit the flow.
// In this example we simplify it as catching an exception.
callback.onError();
}
} else {
// No phone number needed, no need to ask end user for input.
callback.onSuccess(new OtpStartInput(""));
}
}
}
Java
import com.prove.sdk.proveauth.OtpFinishInput;
import com.prove.sdk.proveauth.OtpFinishStep;
import com.prove.sdk.proveauth.OtpFinishStepCallback;
import com.prove.sdk.proveauth.OtpValidationException;
import com.prove.sdk.proveauth.ProveAuthException;
public class PhoneChangeFinish implements OtpFinishStep {
@Override
public void execute(@Nullable ProveAuthException otpException,
OtpFinishStepCallback otpFinishStepCallback) {
// If error message is found, handle it.
if (otpException instanceof OtpValidationException) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
String errorMsg = otpException.getMessage();
}
// Prompt the user for whether they received the SMS.
if (promptForPhoneNumberChange("Didn't receive the SMS OTP? Try a different phone number.")) {
// If the end user wants to correct the phone number already in use, or changing to a
// different phone number to receive the future SMS OTP, call onMobileNumberChange(), and
// the otpStartStep will re-prompt for phone number input from the end user.
otpFinishStepCallback.onMobileNumberChange();
return;
}
try {
// Prompt the user for OTP delivered by SMS. You can build UI to provide
// best UX based on your application and business logic, here we simplify to a
// generic function named promptForOtpCode which gives us the OTP code.
String otpCode = promptForOtpCode();
otpFinishStepCallback.onSuccess(new OtpFinishInput(otpCode));
} catch (Exception e) {
// if any issue with the OTP collection from the end user or the user wants to cancel
// then call onError to exit the flow. In this example we simplify it as catching
// an exception.
otpFinishStepCallback.onError();
}
}
}
Verify the integration
In Sandbox, walk each shipped path (default, prompt, resend, retry if enabled, phone change if enabled). Confirm SMS delivery, OTP entry within the timeout, and that you never manually chain
OtpFinishStep.execute on top of the SDK’s invalid-OTP handling—the SDK should remain the single orchestrator for retries and errors.Verify the Customer Information
Once the customer has made any edits to their pre-fill information, you should submit that information to the backend server so theComplete() call can then verify the customer information.// Send request to the backend to verify customer information.
SendInfoResponse sendInfo(String firstName, String lastName) {
YourBackendClient backend = new YourBackendClient(); // Backend API client
// TODO: Build your SendInfoRequest object
SendInfoRequest sendInfoRequest = new SendInfoRequest(firstName, lastName, ...);
// Send a sendInfoRequest to your backend server to get return customer information.
SendInfoResponse response = backend.verify(sendInfoRequest);
// TODO: define your own SendInfoResponse object to parse the response
return response;
}
Installation
Prove provides the iOS SDK in Swift. It has a download size of 2.5 MB and an install size of 1.5 MB for the minimum required components. It relies on iOS native APIs. The iOS SDK supports the earlier three major versions. Prove has seen successful transactions with iOS v11.Xcode RequirementTo integrate with our iOS SDKs, build apps with Xcode 16.0 or later.
Prerequisites
- Xcode — Use the Xcode version required for your Prove iOS SDK (see the installation warning on this page if present).
- CocoaPods path — Ruby and the
cocoapods-artplugin; ability to edit the Podfile and runpod install. - SPM path — Swift toolchain with
swift package-registryavailable in your environment (terminal).
Add the dependency
- CocoaPods
- Swift Package Manager
Configure the Prove CocoaPods source, declare ProveAuth, then install pods.
# Install the cocoapods-art plugin (Artifactory)
gem install cocoapods-art
# Add the Prove pod repository
pod repo-art add prove.jfrog.io https://prove.jfrog.io/artifactory/api/pods/libs-public-cocoapods
# In your Podfile, add the Prove pod repository as a source
plugin 'cocoapods-art', :sources => [
'prove.jfrog.io'
]
# In your Podfile, add the SDK pod (pin the version you were onboarded to)
pod 'ProveAuth', '6.10.4'
# Install pods
pod install
Upgrading the SDK (CocoaPods)When you upgrade, set a single
ProveAuth version in your Podfile. Don’t add separate versions for ProveBase, ProveDeviceAuth, or ProveMobileAuth. CocoaPods resolves those as dependencies of ProveAuth, which keeps the components aligned and helps avoid version conflicts.Configure the Swift package registry
Register and log in to the Prove Swift registry (needed for both Xcode’s package UI andPackage.swift):swift package-registry set --global "https://prove.jfrog.io/artifactory/api/swift/libs-public-swift"
swift package-registry login "https://prove.jfrog.io/artifactory/api/swift/libs-public-swift"
# Press Enter when prompted for access token
Public registryThis registry is publicly accessible; you do not need a password or access token. Press Enter when prompted for an access token.
swift.proveauth from Xcode or from Package.swift.Add the package
Option A — Xcode- In Xcode, choose File → Add Package Dependencies.
-
Search for
swift.proveauthand add it to your target. -
For production builds, pin an exact package version (for example 6.10.4) so CI and local builds stay consistent.

swift.proveauth in the package search field if it does not appear automatically.Option B — Package.swiftAdd the dependency and product to Package.swift:Swift
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "YourApp",
platforms: [.iOS(.v12)],
dependencies: [
.package(id: "swift.proveauth", from: "6.10.4"),
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "ProveAuth", package: "swift.proveauth"),
]
)
]
)
swift package resolve
Upgrading the SDK (Swift Package Manager)When you upgrade, change only the
swift.proveauth package version. Dependent libraries resolve with that package.Verify
- Open the workspace (
.xcworkspacefor CocoaPods, or your SPM project) and build the app target (⌘B). The project should compile without missing ProveAuth symbols. - If resolution fails, confirm registry / Pod repo commands completed without errors and that you are on a network that can reach
prove.jfrog.io.
Send the type of flow: mobile
Unlike the Web SDK, when using the iOS SDK, use the mobile flow. Pass mobile to theStart() function on the server. In a mobile flow, Mobile Auth executes first and if that fails, performs one-time password (OTP) validation on the mobile phone.In the mobile flow, once either Mobile Auth or the OTP validation is complete, the AuthFinishStep function executes.Mobile AuthIn order for Mobile Auth to succeed, the customer needs to disable the VPN and Private Relay on iOS.
Authenticate()
The SDK requires an authToken as a parameter for the Authenticate() function. This token returns from the Start() call of the server-side SDK. The token is session specific so it’s used for a single flow. It also expires after 15 minutes.Retrieve authToken
To start the flow, send a request to your back end server with the phone number, flow type, and an optional challenge. Use either the date of birth, YYYY-MM-DD, or the last four digits of the social security number.Swift
// The below example uses native iOS URLSession, but any other
// alternative networking approaches should also work
func initialize(phoneNumber: String, ssn: String, flowType: String, completion: @escaping (Result<String, Error>) -> Void) {
guard let url = URL(string: "\(backendUrl)/initialize") else {
completion(.failure(URLError(.badURL)))
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Set up the request body
let body: [String: Any] = [
"phoneNumber": phoneNumber,
"flowType": flowType,
"ssn": ssn
]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
} catch {
completion(.failure(error))
return
}
// Perform the request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// Handle network or connection errors
if let error = error {
completion(.failure(error))
return
}
// Check HTTP response status code
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode != 200 {
let statusError = NSError(domain: "", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP error with status code: \(httpResponse.statusCode)"])
completion(.failure(statusError))
return
}
guard let data = data else {
let noDataError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"])
completion(.failure(noDataError))
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let authToken = json["authToken"] as? String {
completion(.success(authToken))
} else {
let parsingError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to parse JSON or authToken is missing"])
completion(.failure(parsingError))
}
} catch {
completion(.failure(error))
}
}
// Start the network call
task.resume()
}
Setup authenticator
Once you have theauthToken, build the authenticator for the mobile flow.Swift
// Object implementing ProveAuthFinishStep protocols
let finishStep = FinishAuthStep()
// Objects implementing OtpStartStep/OtpFinishStep protocols
let otpStartStep = MobileOtpStartStep()
let otpFinishStep = MobileOtpFinishStep()
let proveAuthSdk: ProveAuth
proveAuthSdk = ProveAuth.builder(authFinish: finishStep)
.withOtpFallback(otpStart: otpStartStep, otpFinish: otpFinishStep)
.build()
Swift
proveAuthSdk = ProveAuth.builder(authFinish: finishStep)
.withMobileAuthTestMode() // Test mode flag
.build()
Performing the authentication
The Prove Auth object is thread safe and used as a singleton. Most Prove Auth methods are blocking and therefore can’t execute in the main app thread. The app employs an executor service with a minimum of two threads to manage threads due to the ability to process concurrent blocking requests.Swift
// authToken retrieved from your server via StartAuthRequest
proveAuthSdk.authenticate(authToken) { error in
DispatchQueue.main.async {
self.messages.finalResultMessage = "ProveAuth.authenticate returned error: \(error.localizedDescription)"
print(self.messages.finalResultMessage)
}
}
Validate the mobile phone
In theAuthFinishStep, specify a function to call once the possession checks complete on the mobile phone. In the following code, notice an endpoint called /verify. This endpoint on your back end server calls the Validate() function to validate the phone number. If unsuccessful, the server calls the Challenge() function and then returns the results, including customer information. Refer to the following example fields that return and then prefill on a form for the customer to verify.Swift
// Send a verify request to get return customer information.
// The below example uses native iOS URLSession, but any other
// alternative networking approaches should also work
func verify(authId: String, completion: @escaping (Error?) -> Void) {
guard let url = URL(string: "\(backendUrl)/verify") else {
completion(URLError(.badURL))
return
}
// Create the request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Set up the request body (empty JSON object) - authId is not needed so you can ignore.
let body: [String: Any] = ["authId": authId]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
} catch {
completion(error)
return
}
// Perform the request
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(error)
return
}
// Check HTTP response status code
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode != 200 {
let statusError = NSError(domain: "", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP error with status code: \(httpResponse.statusCode)"])
completion(statusError)
return
}
guard let data = data else {
let noDataError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"])
completion(noDataError)
return
}
// Parse the response
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let firstName = json["firstName"] as? String,
let lastName = json["lastName"] as? String {
// Update the UI on the main thread
DispatchQueue.main.async {
// Update your UI with the pre-fill customer information
// For example, assuming you have IBOutlet references for your text fields
// self.firstNameInput.text = firstName
// self.lastNameInput.text = lastName
}
completion(nil)
} else {
let parsingError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to parse JSON"])
completion(parsingError)
}
} catch {
completion(error)
}
}
// Start the network call
task.resume()
}
Configure OTP
Use this section to configure SMS OTP fallback on iOS: wireOtpStartStep and OtpFinishStep so the SDK can send the code, collect the PIN, and optionally support resend, OTP retry, or phone-number change.Prerequisites
- After Prove sends the SMS, the customer has about two minutes to enter the OTP before the session times out.
- When building the authenticator, call
withOtpFallback(otpStart: otpStartStep, otpFinish: otpFinishStep)and implement bothOtpStartStepandOtpFinishStep. The Prove client SDK orchestrates when each step runs—implement the protocols, but do not duplicate that orchestration in app code (see Invalid OTP and step orchestration below).
Invalid OTP and step orchestrationWhen the customer enters an invalid OTP, the Prove client SDK detects it (for example, a reported error such as code
10001 with a message that the PIN does not match). You may also see flow activity related to AuthFinishStep (the handler you pass to ProveAuth.builder(authFinish:)) that looks like an unexpected redirect.This behavior is expected. Do not add your own follow-up call to OtpFinishStep to pass the validation error in otpError. The SDK invokes OtpFinishStep.execute(otpError:callback:) when a retry or error UI is needed.Your app should implement the required step protocols, but the Prove client SDK orchestrates when each step runs. Do not duplicate that orchestration in application code.Configure the client
Implement start and finish for your use case
Open the tab that matches how the phone number is collected and which OTP options you need.Allow a new OTP SMS to the same number (up to three send attempts including the first).Implement the start step:Then implement the finish step so the customer can request another SMS, for example:Allow the customer to re-enter the OTP after a wrong PIN (up to three attempts). On the server, pass Implement the finish step—no extra client changes. If the OTP is invalid, the finish step drives retry UI until attempts are exhausted, then Allow the customer to re-enter the phone number (up to three entries/send attempts).Implement the start step:Then implement the finish step so the customer can supply a new number, for example:
- Default
- Prompt for Phone Number
- Resend
- Retry OTP
- Phone Number Change
Use this path when the server already has the phone number (for example from Call
POST /v3/start or your server Start wrapper) and the client must not prompt for it again.Call callback.onSuccess(input: nil) so the SDK knows the customer agreed to receive the SMS.Swift
class OtpStartStepNoPrompt: OtpStartStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpStartStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to handle phone number collection for SMS OTP,
// or to obtain user confirmation for initiating an SMS message.
func execute(
phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
) {
self.callback = callback
// Since no phone number is needed, don't prompt the user.
callback.onSuccess(input: nil)
}
}
callback.onError() if something prevents sending the SMS (for example the customer cancels or leaves the flow with the back button).In the finish step, return the OTP the customer entered through your OtpFinishStep implementation, as in the snippet below.Swift
class OtpFinishStepNoPrompt: OtpFinishStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpFinishStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to collect the OTP value delivered via SMS.
func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
self.callback = callback
// Handle the OTP validation error if present.
// Signal to UI components to display OtpFinishView
DispatchQueue.main.async {
if case .otpValidationError = otpError {
print("found otpError: \(String(describing: otpError?.localizedDescription))")
// Signal to your UI components that the last provided OTP is invalid
self.sheetObservable.isOtpValidationError = true
} else {
self.sheetObservable.isOtpValidationError = false
}
self.sheetObservable.isOtpFinishActive = true
}
}
// Provide the collected OTP value to the SDK for validation.
func handleOtp(_ otp: String) {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
let otpFinishInput = OtpFinishInput(otp: otp)
callback.onSuccess(input: otpFinishInput)
}
// Notify the SDK of any issues encountered while obtaining the OTP value or if the user cancels the OTP flow.
func handleOtpFinishError() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
callback.onError()
}
}
Use this path when the iOS app collects the phone number in the client and you do not need SMS resend, dedicated OTP retry handling beyond defaults, or phone-number change. For those capabilities, use Resend, Retry OTP, or Phone Number Change.In the start step, call Implement the finish step:
callback.onSuccess(input: otpStartInput) with the collected number.Swift
class OtpStartStepWithPrompt: OtpStartStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpStartStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
func execute(
phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
) {
self.callback = callback
if !phoneNumberNeeded {
// If no phone number is needed, then don't prompt the user.
callback.onSuccess(input: nil)
} else {
DispatchQueue.main.async {
// If a phone number validation error is detected, ensure it is handled to provide feedback to the user.
if case .phoneNumberValidationError = phoneValidationError {
print(
"found phoneValidationError: \(String(describing: phoneValidationError?.localizedDescription))"
)
// Update UI components to display OtpStartView with the phone number validation error.
self.sheetObservable.isPhoneValidationError = true
} else {
self.sheetObservable.isPhoneValidationError = false
}
// Update UI components to display OtpStartView if a phone number is needed.
self.sheetObservable.isOtpStartActive = true
}
}
}
// Return collected phone number to the SDK
func handlePhoneNumber(phoneNumber: String) {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
let otpStartInput = OtpStartInput(phoneNumber: phoneNumber)
// This is how you pass collected phone number to SDK
callback.onSuccess(input: otpStartInput)
}
// Communicate any issues encountered while trying to obtain the phone number to the SDK.
// Error should be reported if the customer explicitly cancels the SMS OTP transaction
// or presses the back button to exit out the SMS OTP start step screen.
func handleOtpStartError() {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
callback.onError()
}
}
Swift
class OtpFinishStepNoPrompt: OtpFinishStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpFinishStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to collect the OTP value delivered via SMS.
func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
self.callback = callback
// Handle the OTP validation error if present.
// Signal to UI components to display OtpFinishView
DispatchQueue.main.async {
if case .otpValidationError = otpError {
print("found otpError: \(String(describing: otpError?.localizedDescription))")
// Signal to your UI components that the last provided OTP is invalid
self.sheetObservable.isOtpValidationError = true
} else {
self.sheetObservable.isOtpValidationError = false
}
self.sheetObservable.isOtpFinishActive = true
}
}
// Provide the collected OTP value to the SDK for validation.
func handleOtp(_ otp: String) {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
let otpFinishInput = OtpFinishInput(otp: otp)
callback.onSuccess(input: otpFinishInput)
}
// Notify the SDK of any issues encountered while obtaining the OTP value or if the user cancels the OTP flow.
func handleOtpFinishError() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
callback.onError()
}
}
To use the Resend/Retry/Phone Change features, install the iOS SDK version 6.5.1 or later.
Swift
class OtpStartStepWithPrompt: OtpStartStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpStartStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
func execute(
phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
) {
self.callback = callback
if !phoneNumberNeeded {
// If no phone number is needed, then don't prompt the user.
callback.onSuccess(input: nil)
} else {
DispatchQueue.main.async {
// If a phone number validation error is detected, ensure it is handled to provide feedback to the user.
if case .phoneNumberValidationError = phoneValidationError {
print(
"found phoneValidationError: \(String(describing: phoneValidationError?.localizedDescription))"
)
// Update UI components to display OtpStartView with the phone number validation error.
self.sheetObservable.isPhoneValidationError = true
} else {
self.sheetObservable.isPhoneValidationError = false
}
// Update UI components to display OtpStartView if a phone number is needed.
self.sheetObservable.isOtpStartActive = true
}
}
}
// Return collected phone number to the SDK
func handlePhoneNumber(phoneNumber: String) {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
let otpStartInput = OtpStartInput(phoneNumber: phoneNumber)
// This is how you pass collected phone number to SDK
callback.onSuccess(input: otpStartInput)
}
// Communicate any issues encountered while trying to obtain the phone number to the SDK.
// Error should be reported if the customer explicitly cancels the SMS OTP transaction
// or presses the back button to exit out the SMS OTP start step screen.
func handleOtpStartError() {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
callback.onError()
}
}
Swift
class OtpFinishStepMultipleResend: OtpFinishStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpFinishStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to collect the OTP value delivered in the SMS message.
func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
self.callback = callback
// Handle the OTP validation error if present.
// Update your UI to display the OtpFinishView
DispatchQueue.main.async {
if case .otpValidationError = otpError {
print("found otpError: \(String(describing: otpError?.localizedDescription))")
// Update your UI to indicate that the provided OTP is invalid
self.sheetObservable.isOtpValidationError = true
} else {
self.sheetObservable.isOtpValidationError = false
}
self.sheetObservable.isOtpFinishActive = true
}
}
// Return the OTP value to the SDK
func handleOtp(_ otp: String) {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
let otpFinishInput = OtpFinishInput(otp: otp)
callback.onSuccess(input: otpFinishInput)
}
// Communicate to the SDK any issues when host app trys to obtain the OTP value
// or when users cancel the OTP flow
func handleOtpFinishError() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
callback.onError()
}
// Call this method to request a new OTP code for the same mobile number.
func sendNewOtp() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
callback.onOtpResend()
}
}
To use the Resend/Retry/Phone Change features, install the iOS SDK version 6.5.1 or later.
allowOTPRetry=true on POST /v3/start (or your equivalent Start request).Implement the start step—no extra client changes beyond your normal prompt path:Swift
class OtpStartStepWithPrompt: OtpStartStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpStartStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
func execute(
phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
) {
self.callback = callback
if !phoneNumberNeeded {
// If no phone number is needed, then don't prompt the user.
callback.onSuccess(input: nil)
} else {
DispatchQueue.main.async {
// If a phone number validation error is detected, ensure it is handled to provide feedback to the user.
if case .phoneNumberValidationError = phoneValidationError {
print(
"found phoneValidationError: \(String(describing: phoneValidationError?.localizedDescription))"
)
// Update UI components to display OtpStartView with the phone number validation error.
self.sheetObservable.isPhoneValidationError = true
} else {
self.sheetObservable.isPhoneValidationError = false
}
// Update UI components to display OtpStartView if a phone number is needed.
self.sheetObservable.isOtpStartActive = true
}
}
}
// Return collected phone number to the SDK
func handlePhoneNumber(phoneNumber: String) {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
let otpStartInput = OtpStartInput(phoneNumber: phoneNumber)
// This is how you pass collected phone number to SDK
callback.onSuccess(input: otpStartInput)
}
// Communicate any issues encountered while trying to obtain the phone number to the SDK.
// Error should be reported if the customer explicitly cancels the SMS OTP transaction
// or presses the back button to exit out the SMS OTP start step screen.
func handleOtpStartError() {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
callback.onError()
}
}
AuthFinish runs.Swift
class OtpFinishStepNoPrompt: OtpFinishStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpFinishStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to collect the OTP value delivered via SMS.
func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
self.callback = callback
// Handle the OTP validation error if present.
// Signal to UI components to display OtpFinishView
DispatchQueue.main.async {
if case .otpValidationError = otpError {
print("found otpError: \(String(describing: otpError?.localizedDescription))")
// Signal to your UI components that the last provided OTP is invalid
self.sheetObservable.isOtpValidationError = true
} else {
self.sheetObservable.isOtpValidationError = false
}
self.sheetObservable.isOtpFinishActive = true
}
}
// Provide the collected OTP value to the SDK for validation.
func handleOtp(_ otp: String) {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
let otpFinishInput = OtpFinishInput(otp: otp)
callback.onSuccess(input: otpFinishInput)
}
// Notify the SDK of any issues encountered while obtaining the OTP value or if the user cancels the OTP flow.
func handleOtpFinishError() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
callback.onError()
}
}
To use the Resend/Retry/Phone Change features, install the iOS SDK version 6.5.1 or later.
Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
Swift
class OtpStartStepWithPrompt: OtpStartStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpStartStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
func execute(
phoneNumberNeeded: Bool, phoneValidationError: ProveAuthError?, callback: OtpStartStepCallback
) {
self.callback = callback
if !phoneNumberNeeded {
// If no phone number is needed, then don't prompt the user.
callback.onSuccess(input: nil)
} else {
DispatchQueue.main.async {
// If a phone number validation error is detected, ensure it is handled to provide feedback to the user.
if case .phoneNumberValidationError = phoneValidationError {
print(
"found phoneValidationError: \(String(describing: phoneValidationError?.localizedDescription))"
)
// Update UI components to display OtpStartView with the phone number validation error.
self.sheetObservable.isPhoneValidationError = true
} else {
self.sheetObservable.isPhoneValidationError = false
}
// Update UI components to display OtpStartView if a phone number is needed.
self.sheetObservable.isOtpStartActive = true
}
}
}
// Return collected phone number to the SDK
func handlePhoneNumber(phoneNumber: String) {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
let otpStartInput = OtpStartInput(phoneNumber: phoneNumber)
// This is how you pass collected phone number to SDK
callback.onSuccess(input: otpStartInput)
}
// Communicate any issues encountered while trying to obtain the phone number to the SDK.
// Error should be reported if the customer explicitly cancels the SMS OTP transaction
// or presses the back button to exit out the SMS OTP start step screen.
func handleOtpStartError() {
guard let callback = self.callback else {
print("Error: OtpStartStepCallback is not set ")
return
}
callback.onError()
}
}
Swift
class OtpFinishStepWithPhoneChange: OtpFinishStep {
@ObservedObject var sheetObservable: SheetObservable
var callback: OtpFinishStepCallback?
init(sheetObservable: SheetObservable) {
self.sheetObservable = sheetObservable
}
// Implement this method to collect the OTP value delivered via SMS.
func execute(otpError: ProveAuthError?, callback: OtpFinishStepCallback) {
self.callback = callback
// Handle the OTP validation error if present.
// Update your UI to display the OTP finish view.
DispatchQueue.main.async {
if case .otpValidationError = otpError {
print("found otpError: \(String(describing: otpError?.localizedDescription))")
// Update your UI to indicate that the provided OTP is invalid.
self.sheetObservable.isOtpValidationError = true
} else {
self.sheetObservable.isOtpValidationError = false
}
self.sheetObservable.isOtpFinishActive = true
}
}
// Return the collected OTP value to the SDK.
func handleOtp(_ otp: String) {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set ")
return
}
let otpFinishInput = OtpFinishInput(otp: otp)
callback.onSuccess(input: otpFinishInput)
}
// When callback.onMobileNumberChange() is evoked, OtpStartStep will be re-initiated
// so that end-users can enter a different phone number via OtpStartStep.
func handleMobileNumberChange() {
guard let callback = self.callback else {
print("Error: OtpFinishStepCallback is not set")
return
}
callback.onMobileNumberChange()
}
}
Verify the integration
In Sandbox, walk each shipped path (default, prompt, resend, retry if enabled, phone change if enabled). Confirm SMS delivery, OTP entry within the timeout, and that you never manually chain
OtpFinishStep on top of the SDK’s invalid-OTP handling—the SDK should remain the single orchestrator for retries and errors.Verify the Customer Information
Once the customer has made any edits to their prefill information, submit that information to the back end server so theComplete() call can then verify the customer information.Swift
// Send request to the backend to verify customer information.
// The below example uses native iOS URLSession, but any other
// alternative networking approaches should also work
func sendInfo(firstName: String, lastName: String, completion: @escaping (Result<[String: Any], Error>) -> Void) {
guard let url = URL(string: "\(backendUrl)/finish") else {
completion(.failure(URLError(.badURL)))
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Set up the request body
let body: [String: Any] = [
"firstName": firstName,
"lastName": lastName
]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: body, options: [])
} catch {
completion(.failure(error))
return
}
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// Handle network or connection errors
if let error = error {
completion(.failure(error))
return
}
// Check HTTP response status code
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode != 200 {
let statusError = NSError(domain: "", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP error with status code: \(httpResponse.statusCode)"])
completion(.failure(statusError))
return
}
guard let data = data else {
let noDataError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"])
completion(.failure(noDataError))
return
}
do {
if let results = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
completion(.success(results))
} else {
let parsingError = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to parse JSON"])
completion(.failure(parsingError))
}
} catch {
completion(.failure(error))
}
}
// Start the network call
task.resume()
}
Supported languages
Prove provides client web SDKs in the following languages: TypeScript and JavaScript.Installation
The Prove Platform Web SDK has an unpacked size of 171 KB, and a single dependency:@prove-identity/mobile-auth. Install the client-side SDK of your choice by running a command in your terminal, or by using a dependency management tool specific to your project.# Run this command to install the package (ensure you have the latest version).
npm install @prove-identity/prove-auth@3.4.2
If you’re loading the file from jsDelivr and using Content Security Policy headers, ensure you add
https://cdn.jsdelivr.net/npm/@prove-identity/ to script-src,
plus corresponding sha256-... or nonce-....Angular TypeScript Compilation ErrorSome Angular projects may experience TypeScript compilation errors when building with the Prove Web SDK. This is a known issue that affects various Angular versions.To correct this, add or update the This setting tells TypeScript to skip type checking of declaration files,
skipLibCheck setting in your tsconfig.json file:tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true,
// ... other options
}
}
.d.ts files, from external libraries, which resolves the compilation issues while maintaining type safety for your own code.Affected Versions:- Angular 17.x, confirmed with Angular 17.3.3
Find the type of flow: mobile or desktop
You can find if the customer is on a mobile or desktop browser using this example. If theisMobile is true, pass mobile to the Start() function on the server, otherwise you can pass desktop:// Check if the customer is on a mobile or desktop browser.
const authCheck = new proveAuth.AuthenticatorBuilder().build();
let isMobile = authCheck.isMobileWeb()
AuthFinishStep function finishes.Mobile AuthIn order for Mobile Auth to succeed:
- Disable VPN.
- Disable Private Relay on iOS.
ERR_TUNNEL_CONNECTION_FAILED - this is due to the VPN, but the SDK fallbacks to OTP.AuthFinishStep function finishes.Authenticate()
The SDK requires an authToken as a parameter for the Authenticate() function. This token returns from the Start() call of the server-side SDK. The token is session specific, limiting it to a single flow. It also expires after 15 minutes.Retrieve authToken
Send a request to your back end server with the phone number, flow type, and an optional challenge to start the flow. This can either be the date of birth or last four digits of the social security number.async function initialize(phoneNumber, ssn, flowType) {
const response = await fetch(backendUrl + "/initialize", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
phoneNumber: phoneNumber,
flowType: flowType,
ssn: ssn,
}),
});
const rsp = await response.json();
const authToken = rsp.authToken;
return authToken;
}
Setup authenticator
Once you have theauthToken, build the authenticator for both the mobile and desktop flows.Mobile Auth Implementations OnlyIf your app uses Content Security Policy headers, you must configure them to allow WebSocket connections to Prove’s authentication services:Sandbox Environment
https://device.uat.proveapis.com:4443https://device.uat.proveapis.comhttp://device.uat.proveapis.com:4443http://device.uat.proveapis.com
https://device.proveapis.com:4443https://device.proveapis.comhttp://device.proveapis.com:4443http://device.proveapis.comhttps://auth.svcs.verizon.com:22790
async function authenticate(isMobileWeb, authToken) {
// Set up the authenticator for either mobile or desktop flow.
let builder = new proveAuth.AuthenticatorBuilder();
if (isMobileWeb) {
// Set up Mobile Auth and OTP.
builder = builder
.withAuthFinishStep((input) => verify(input.authId))
.withOtpFallback(otpStart, otpFinish);
} else {
// Set up Instant Link.
builder = builder
.withAuthFinishStep((input) => verify(input.authId))
.withInstantLinkFallback(instantLink)
.withRole("secondary");
}
const authenticator = builder.build();
// Authenticate with the authToken.
return authenticator.authenticate(authToken);
}
Validate the mobile phone
In theAuthFinishStep, specify a function to call once the possession checks complete on the mobile phone. This endpoint on your back end server calls the Validate() function to validate the phone number. If it was successful, the server returns the results from the Challenge() function including customer information. Refer to the following example fields that return and then prefill on a form for the customer to verify. The AuthFinishStep then completes. If a user cancels, the server makes a call to the Validate() function and returns success=false.// Send a verify request to get return customer information.
async function verify() {
const response = await fetch(backendUrl + "/verify", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const results = await response.json();
const firstName = document.getElementById("firstNameInput");
const lastName = document.getElementById("lastNameInput");
firstName.value = rsp.firstName;
lastName.value = rsp.lastName;
return null;
}
Configure OTP
Use this section to configure SMS OTP fallback in the Web SDK: wireOtpStartStep and OtpFinishStep so the SDK can send the code, collect the PIN, and optionally support resend, OTP retry, or phone-number change.Prerequisites
- After Prove sends the SMS, the customer has about two minutes to enter the OTP before the session times out.
- When building the authenticator, call
withOtpFallback(startStep: OtpStartStep | OtpStartStepFn, finishStep: OtpFinishStep | OtpFinishStepFn)and implement bothOtpStartStepandOtpFinishStep. Return the phone number from your start step as an object with aphoneNumberfield passed toresolve(...); usereject('message')when collection fails. The Prove client SDK orchestrates when each step runs—do not duplicate that orchestration in app code (see Invalid OTP and step orchestration below).
Invalid OTP and step orchestrationWhen the customer enters an invalid OTP, the Prove client SDK detects it and may surface activity related to
AuthFinishStep that looks unexpected. This behavior is expected.Do not add your own extra invocation of the OTP finish step to pass the error in otpError. The SDK runs your OtpFinishStep when retry or error UI is needed.Implement the required step functions, but let the Prove client SDK orchestrate when each step runs.Configure the client
Implement start and finish for your use case
Open the tab that matches how the phone number is collected and which OTP options you need.Allow a new OTP SMS to the same number (up to three send attempts including the first).Implement the start step:Then implement the finish step so the customer can request another SMS, for example:Allow the customer to re-enter the OTP after a wrong PIN (up to three attempts). On the server, pass Implement the finish step—no extra client changes. If the OTP is invalid, the finish step drives retry UI until attempts are exhausted, then Allow the customer to re-enter the phone number (up to three entries/send attempts).Implement the start step:Then implement the finish step so the customer can supply a new number, for example:
- Default
- Prompt for Phone Number
- Resend
- Retry OTP
- Phone Number Change
Use this path when the server already has the phone number (for example from Call
POST /v3/start or your server Start wrapper) and the browser must not prompt for it again.Call resolve(null) (or the equivalent in your snippet) so the SDK knows the customer agreed to receive the SMS. Follow the sample for the exact resolve shape your SDK version expects.function otpStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// Since no phone number is needed, don't prompt the user.
resolve(null);
});
}
reject('some error message') if something prevents sending the SMS or completing the flow (for example the customer cancels or leaves the UI with the back button).In the finish step, return the OTP through resolve(result: OtpFinishResult) with the OnSuccess result type and the value wrapped in OtpFinishInput, as in the snippet below.function otpFinishStep(otpError) {
return new Promise((resolve, reject) => {
// If error message is found, handle it.
if (otpError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = otpError.message;
}
// Prompt the user for whether they received the SMS.
// Typically, this is a page that shows the OTP already. We are simplifying
// it by requiring an input.
var input = confirm('Did you receive a text message?');
if (!input) {
// Close the modal if a text message was not received.
return;
}
// Prompt the user for the OTP.
var otp = prompt('Enter OTP code:');
if (otp) {
// If the input is valid and the user clicked `OK`, return the OTP.
resolve({
input: { otp }, // OTP value
resultType: 0, // OnSuccess enum type = 0
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
Use this path when the page collects the phone number in the browser and you do not need SMS resend, dedicated OTP retry handling beyond defaults, or phone-number change. For those capabilities, use Resend, Retry OTP, or Phone Number Change.In the start step, call Implement the finish step:
resolve(input) with an object that includes the collected phoneNumber.function otpStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
function otpFinishStep(otpError) {
return new Promise((resolve, reject) => {
// If error message is found, handle it.
if (otpError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = otpError.message;
}
// Prompt the user for whether they received the SMS.
// Typically, this is a page that shows the OTP already. We are simplifying
// it by requiring an input.
var input = confirm('Did you receive a text message?');
if (!input) {
// Close the modal if a text message was not received.
return;
}
// Prompt the user for the OTP.
var otp = prompt('Enter OTP code:');
if (otp) {
// If the input is valid and the user clicked `OK`, return the OTP.
resolve({
input: { otp }, // OTP value
resultType: 0, // OnSuccess enum type = 0
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
To use the Resend/Retry/Phone Change features, install the Web SDK version 2.15.1 or later.
function otpStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
function otpFinishStep(otpError) {
return new Promise((resolve, reject) => {
// If error message is found, handle it.
if (otpError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = otpError.message;
}
// Prompt the user for whether they received the SMS.
// Typically, this is a page that shows the OTP already. We are simplifying
// it by requiring an input.
var input = confirm('Did you receive a text message?');
if (!input) {
// If `Cancel`, then resend to the same phone number.
resolve({
resultType: 1, // OnResendOtp enum type = 1
});
return;
}
// Prompt the user for the OTP.
var otp = prompt('Enter OTP code:');
if (otp) {
// If the input is valid and the user clicked `OK`, return the OTP.
resolve({
input: { otp }, // OTP value
resultType: 0, // OnSuccess enum type = 0
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
To use the Resend/Retry/Phone Change features, install the Web SDK version 2.15.1 or later.
allowOTPRetry=true on POST /v3/start (or your equivalent Start request).Implement the start step—no extra client changes beyond your normal prompt path:function otpStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
AuthFinish runs.function otpFinishStep(otpError) {
return new Promise((resolve, reject) => {
// If error message is found, handle it.
if (otpError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = otpError.message;
}
// Prompt the user for whether they received the SMS.
// Typically, this is a page that shows the OTP already. We are simplifying
// it by requiring an input.
var input = confirm('Did you receive a text message?');
if (!input) {
// Close the modal if a text message was not received.
return;
}
// Prompt the user for the OTP.
var otp = prompt('Enter OTP code:');
if (otp) {
// If the input is valid and the user clicked `OK`, return the OTP.
resolve({
input: { otp }, // OTP value
resultType: 0, // OnSuccess enum type = 0
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
To use the Resend/Retry/Phone Change features, install the Web SDK version 2.15.1 or later.
Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
function otpStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
function otpFinishStep(otpError) {
return new Promise((resolve, reject) => {
// If error message is found, handle it.
if (otpError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = otpError.message;
}
// Prompt the user for whether they received the SMS.
// Typically, this is a page that shows the OTP already. We are simplifying
// it by requiring an input.
var input = confirm('Did you receive a text message?');
if (!input) {
// If `Cancel`, then trigger the otpStartStep to re-prompt for
// phone number.
resolve({
resultType: 2, // OnMobileNumberChange enum type = 2
});
return;
}
// Prompt the user for the OTP.
var otp = prompt('Enter OTP code:');
if (otp) {
// If the input is valid and the user clicked `OK`, return the OTP.
resolve({
input: { otp }, // OTP value
resultType: 0, // OnSuccess enum type = 0
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
Verify the integration
In Sandbox, walk each shipped path (default, prompt, resend, retry if enabled, phone change if enabled). Confirm SMS delivery, OTP entry within the timeout, and that you never stack extra
OtpFinishStep invocations on top of the SDK’s invalid-OTP handling—the SDK should remain the single orchestrator for retries and errors.Configure Instant Link
Use this section to configure the Web SDK so Instant Link SMS can be sent from the browser flow and optional resend or phone-number change behaves as expected.Instant Link for mobile web isn’t supported.
Custom or vanity Instant Links aren’t supported. You can’t substitute a custom link for the default Instant Link.
Prerequisites
- Integrate on desktop (or other supported) web; see the callout above for mobile web.
withInstantLinkFallback(startStep: InstantLinkStartStep | InstantLinkStartStepFn, retryStep?: InstantLinkRetryStep | InstantLinkRetryStepFn). Implement InstantLinkStartStep in every flow. Add InstantLinkRetryStep only if you support Resend or Phone Number Change (see those tabs). Return the phone number from your step as an object with a phoneNumber field passed to resolve(...); use reject('message') when collection fails.Configure the client
Implement the Instant Link start and optional retry
Open the tab that matches how the phone number is collected and sent to Prove.Allow a new SMS to the same number (up to three send attempts including the first).Implement the start step:Then implement Allow the customer to re-enter the phone number (up to three entries/send attempts).Implement the start step:Then implement
- Default
- Prompt for Phone Number
- Resend
- Phone Number Change
Use this path when the server already has the phone number (for example from
POST /v3/start or your server Start wrapper) and the browser must not prompt again.Call resolve(null) (or the equivalent in your snippet) so the SDK knows the customer agreed to receive the SMS. Follow the sample for the exact resolve shape your SDK version expects.function instantLinkStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// Since no phone number is needed, don't prompt the user.
resolve(null);
});
}
Use this path when the page collects the number in the browser and you do not need resend or phone-number change. For resend or change, use the other tabs.Call
resolve(input) with an object that includes the collected phoneNumber. Call reject('some error message') if collection fails, the customer cancels, or they leave the Instant Link start UI (for example with the back button).function instantLinkStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
To use the Resend/Retry/Phone Number Change features, install the Web SDK version 2.15.1 or later.
function instantLinkStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
InstantLinkRetryStep so the customer can request another SMS, for example:function instantLinkRetryStep() {
return new Promise((resolve, reject) => {
// There are multiple return options:
// - resolve(0): request resend to the same phone number
// - reject('user clicked cancel'): error out of the possession flow
// Prompt the user for the phone number.
// Typically, this is a page that is automatically closed or redirected
// once the `AuthFinish` function is called. We are simplifying it by
// requiring an input.
var input = confirm('Did you receive a text message?');
if (input) {
// If `OK`, close the modal.
return;
}
// Else `Cancel`, then resend to the same phone number.
resolve(0);
});
}
To use the Resend/Retry/Phone Number Change features, install the Web SDK version 2.15.1 or later.
Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
function instantLinkStartStep(phoneNumberNeeded, phoneValidationError) {
return new Promise((resolve, reject) => {
// If no phone number is needed, then don't prompt the user.
if (!phoneNumberNeeded) {
resolve(null);
return;
}
// If error message is found around phone number, handle it.
// The `phoneValidationError` is ONLY available when `phoneNumberNeeded`
// has a value.
if (phoneValidationError) {
// Set to a variable and display it in a field.
// In this example, we don't do anything with the error.
var someErrorMessage = phoneValidationError.message;
}
// Prompt the user for the phone number.
var input = prompt('Enter phone number:');
if (input) {
// If the input is valid and the user clicked `OK`, return the phone
// number.
resolve({
phoneNumber: input,
});
} else {
// Else, exit the flow.
reject('phone invalid or user cancelled');
}
});
}
InstantLinkRetryStep to collect a new number, for example:function instantLinkRetryStep() {
return new Promise((resolve, reject) => {
// There are multiple return options:
// - resolve(0): request resend to the same phone number
// - resolve(1): request phone number change/re-prompt
// - reject('user clicked cancel'): error out of the possession flow
// Prompt the user for the phone number.
// Typically, this is a page that is automatically closed or redirected
// once the `AuthFinish` function is called. We are simplifying it by
// requiring an input.
var input = confirm('Did you receive a text message?');
if (input) {
// If `OK`, close the modal.
return;
}
// Else `Cancel`, then trigger the instantLinkStartStep to re-prompt for
// phone number.
resolve(1);
});
}
Verify the Customer Information
Once the customer has made any edits to their prefill information, submit that information to the back end server so theComplete() call can then verify the customer information.// Send request to the backend to verify customer information.
async function sendInfo(firstName, lastName) {
const response = await fetch(backendUrl + "/finish", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
firstName: firstName,
lastName: lastName,
}),
});
const results = await response.json();
const rsp = JSON.stringify(results);
return rsp;
}

