Possession requires both a client-side SDK and these platform APIs:
Prerequisites
- Access token — Obtain a bearer token using Prove OAuth (Authentication). Use the same token for
POST /v3/unify, POST /v3/unify-status, and POST /v3/unify-bind.
- Server — Use a Prove server SDK or call those endpoints directly. Use the reference pages for full request bodies, optional fields, and responses.
- Client SDK — Install the Android SDK for possession checks and the Prove Key.
How to implement
You must integrate the client-side SDK for possession checks and the Prove Key.
Determine Type of Flow
When using the Android SDK, set mobile as the flowType for the Start() function on the server.
Initialize the Flow
Mobile Auth in the United States doesn’t require a phone number, but all other countries do.
Send a request to your back end server with the phone number and possession type to start the flow. See POST /v3/unify for all request fields, optional parameters (for example finalTargetURL, deviceId, allowOTPRetry), and the full response.For OTP retries (allowOTPRetry), implement the client SDK behavior in the Authenticate step.
// Send the Unify request.
rspUnify, err := client.V3.V3UnifyRequest(ctx, &components.V3UnifyRequest{
PhoneNumber: "2001004014",
PossessionType: "mobile",
ClientRequestID: provesdkservergo.String("client-abc-123"),
AllowOTPRetry: true,
})
if err != nil {
t.Fatal(err)
}
Return the authToken to the client for Authenticate(). Persist correlationId for UnifyStatus(). See POST /v3/unify for all response fields, JWT behavior, and session timing.Authenticate
Once you have the authToken, build the authenticator for both the mobile and desktop flows.Implement OTP or Instant Link - not both.
// 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();
The mobile data connection can sometimes be unavailable during testing. The 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();
The ProveAuth 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 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());
}
}
Use this section to configure SMS OTP fallback on Android: wire OtpStartStep 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 both OtpStartStep and OtpFinishStep. 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.
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. 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 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.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(""));
}
}
Call 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.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 OtpStartStepCallback.onSuccess(OtpStartInput) with the collected number.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(""));
}
}
}
Implement the finish step: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.
Allow a new OTP SMS to the same number (up to three send attempts including the first).Implement the start step: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(""));
}
}
}
Then implement the finish step so the customer can request another SMS, for example: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.
Allow the customer to re-enter the OTP after a wrong PIN (up to three attempts). On the server, pass allowOTPRetry=true on POST /v3/start (or your equivalent Start request).Implement the start step—no extra client changes beyond your normal prompt path: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(""));
}
}
}
Implement the finish step—no extra client changes. If the OTP is invalid, the finish step drives retry UI until attempts are exhausted, then AuthFinish runs.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.
Allow the customer to re-enter the phone number (up to three entries/send attempts).Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
Implement the start step: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(""));
}
}
}
Then implement the finish step so the customer can supply a new number, for example: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.
Instant Link for Android is an add-on feature. To enable, contact your Prove representative.
Use this section to configure the Android client so an Instant Link SMS opens your app and you pass the returned redirect URL to the SDK to resume the session.Prerequisites
- Instant Link is enabled for your project (contact your Prove representative if needed).
- Verified App Links (recommended) so the SMS redirect opens your app with the full URL string. See App Links in the Android documentation.
When building the authenticator, use withInstantLinkFallback(InstantLinkStartStep startStep, @Nullable InstantLinkRetryStep retryStep). Implement InstantLinkStartStep in every flow. Add InstantLinkRetryStep only if you support Resend or Phone Number Change (see those tabs). When you have a mobile number, pass it in InstantLinkStartInput (for example the mobileNumber field) to callback.onSuccess(...).Implement the Instant Link start step
Open the tab that matches how the phone number is collected and sent to Prove. Default
Prompt for Phone Number
Resend
Phone Number Change
Use this path when the server already has the phone number (for example from your initial Start call) and the client must not prompt again.Call callback.onSuccess(InstantLinkStartInput input) so the SDK knows the customer agreed to receive the SMS.InstantLinkStartStep noPromptStartStep = (phoneNumberNeeded, instantLinkError, callback) -> {
// No phone number needed, no need to ask end user for input.
if (!phoneNumberNeeded) {
callback.onSuccess(new InstantLinkStartInput(""));
}
};
Use this path when the client collects the number and you do not need resend or phone-number change.Call callback.onSuccess(InstantLinkStartInput input) with the collected number. Call callback.onError() if collection fails, the customer cancels, or they leave the Instant Link start UI (for example with the back button).InstantLinkStartStep promptStartStep = (phoneNumberNeeded, instantLinkError, callback) -> {
// No phone number needed, no need to ask end user for input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (instantLinkError 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 = instantLinkError.getMessage();
}
try {
// Prompt the user for phone number to receive InstantLink 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 InstantLinkStartInput(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 InstantLinkStartInput(""));
}
};
To use the Resend/Phone Number Change features, install the Android SDK version 6.10.3 or later.
Allow a new SMS to the same number (up to three send attempts including the first).Implement the start step:InstantLinkStartStep promptStartStep = (phoneNumberNeeded, instantLinkError, callback) -> {
// No phone number needed, no need to ask end user for input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (instantLinkError 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 = instantLinkError.getMessage();
}
try {
// Prompt the user for phone number to receive InstantLink 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 InstantLinkStartInput(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 InstantLinkStartInput(""));
}
};
Then implement InstantLinkRetryStep so the customer can request another SMS, for example:InstantLinkRetryStep promptMultiResendRetryStep = callback -> {
// Prompt the user for whether they received the SMS.
if (promptForResend(
"Didn't receive the InstantLink SMS? Click resend button for a new one!")) {
// If the end user wants to send again to the same phone number call onResend().
callback.onResend();
}
};
To use the Resend/Phone Number Change features, install the Android SDK version 6.10.3 or later.
Allow the customer to re-enter the phone number (up to three entries/send attempts).Manual Request RequiredTo enable phone number change capabilities on your credentials, contact your Prove representative.
Implement the start step:InstantLinkStartStep promptStartStep = (phoneNumberNeeded, instantLinkError, callback) -> {
// No phone number needed, no need to ask end user for input.
if (phoneNumberNeeded) {
// If error message is found around phone number, handle it.
// The `PhoneNumberValidationException` is ONLY available when `phoneNumberNeeded`
// has a value.
if (instantLinkError 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 = instantLinkError.getMessage();
}
try {
// Prompt the user for phone number to receive InstantLink 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 InstantLinkStartInput(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 InstantLinkStartInput(""));
}
};
Then implement InstantLinkRetryStep to collect a new number, for example:InstantLinkRetryStep promptPhoneNumChangeRetryStep = callback -> {
// Prompt the user for whether they received the SMS.
if (promptForChangePhoneNumber(
"Didn't receive the InstantLink SMS? Try a different phone number.")) {
// If the end user wants to send again to the same phone number call onResend().
callback.onResend();
}
};
Handle the redirect and resume the session
After the user finishes the web step outside your app, Prove redirects to the finalTargetUrl from your server Start call. Your App Link (or equivalent) must deliver that URL into your app so you can pass the full string—including query parameters—into finishInstantLink(String redirectUrl).Call finishInstantLink from the code path that handles the incoming deep link (for example onCreate() in your App Link activity).Register an activity similar to this (replace the host with yours):<activity android:name=".MyAppLinkHandlerActivity"
android:exported="true"
android:launchMode="singleTask"
android:excludeFromRecents="true"
android:taskAffinity="">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="my.applink.com" />
</intent-filter>
</activity>
The redirect URL is your original finalTargetUrl plus parameters the SDK needs. Example: if Start used https://yourDeepLinkUrl.com, the link might look like https://yourDeepLinkUrl.com?asc=true&authId=some-uuid-string.| Parameter | Meaning |
|---|
asc | "true" or "false": whether the server considers the auth session complete. |
authId | UUID for the session; the SDK uses it to match the redirect to the in-progress client session. |
If required parameters are missing or invalid, the SDK throws ProveAuthException and does not continue the flow.Verify: In Sandbox, complete a flow where the SMS opens your handler activity; finishInstantLink should run with the full URL and the session should resume without ProveAuthException from a well-formed redirect. Verify Mobile Number
In the AuthFinishStep, specify a function to call once the possession checks complete on the mobile phone. This endpoint on your back end server calls the UnifyStatus() function to validate the phone number. The AuthFinishStep then completes.rspUnifyStatus, err := client.V3.V3UnifyStatusRequest(context.TODO(), &components.V3UnifyStatusRequest{
CorrelationID: rspUnify.V3UnifyResponse.CorrelationID,
})
if err != nil {
return fmt.Errorf("error on UnifyStatus(): %w", err)
}
See POST /v3/unify-status for the full response schema. Interpret evaluation using the Global Fraud Policy.If you’re using
Content Security Policy headers, ensure you allow
connect-src for
wss://*.prove-auth.proveapis.com,
wss://device.uat.proveapis.com, and
wss://device.proveapis.com as required for your environment.
Prove Key validity: The key does not expire on a fixed schedule, but Prove deactivates it on our servers after 12 months of inactivity (each successful authentication resets that period). On iOS, the Prove Key remains on the device after uninstall and reinstall. Device Context is available for the Web SDK only—not the native Android SDK. Pass rebind on /v3/unify to force a full possession check when needed. See Prove Key validity and expiration. If you send a different phone number to /unify than the one registered to the Prove key, success=false returns when calling /unify-status. This is because the Prove Key is bound to a different number. Run the flow again with rebind=true in the /unify endpoint. This forces a full possession check and then, if valid, rebinds the Prove Key to the new phone number.
Error handling
If authentication was not successful, the evaluation field returned by UnifyStatus() has failure reasons explaining what went wrong according to the Global Fraud Policy.
Possession failure reasons
| Code | Description |
|---|
| 9100 | Phone possession incomplete. |
| 9101 | Phone number doesn’t match Prove Key. Rebind required. |
Error code 9100 - Phone possession incomplete
Error code 9100 indicates that authentication was not successful.
Error code 9101 - Phone number doesn’t match Prove Key
Error code 9101 indicates that someone attempted authentication using a phone number that doesn’t match the phone number registered to the Prove Key.
How to resolve:
To rebind the Prove Key to the new phone number, restart the flow with rebind=true in the call to /unify:
rspUnify, err := client.V3.V3UnifyRequest(ctx, &components.V3UnifyRequest{
PhoneNumber: "2001004014",
PossessionType: "mobile",
Rebind: provesdkservergo.Bool(true),
})
When you set rebind=true:
- Prove initiates a new authentication check.
- If the authentication is successful, the Prove Key is rebound to the new phone number passed into the request.
- Future authentications with this phone number succeed without requiring rebind.
/v3/unify accepts a correctly formatted phone number as valid regardless of landline or mobile. If SMS or Mobile Auth cannot finish the possession step, expect the issue to surface on /v3/unify-status as success=false, depending on the flow.