How to implement
To integrate Prove authentication, you must use the client-side SDK.
Determine Type of Flow
Web SDK
Android SDK
iOS SDK
Determine if the customer is on a mobile or desktop browser using this example. If the isMobile is true, set mobile as the flowType for the Start() function on the server, otherwise you can set desktop:// Check if the customer is on a mobile or desktop browser.
const authCheck = new proveAuth.AuthenticatorBuilder().build();
let isMobile = authCheck.isMobileWeb()
When using the Android SDK, set mobile as the flowType for the Start() function on the server.
When using the iOS SDK, set mobile as the flowType for the Start() function on the server.
Initialize the Flow
No Mobile Auth
Mobile Auth
Send a request to your back end server with the phone number and possession type to start the flow.Additional parameters:
-
finalTargetURL: required when flowType=desktop. This should be a URL you maintain. Once the customer clicks the Instant Link, they will be redirected to this URL. It should instruct the customer to continue the workflow. Maximum length is 128 characters.
-
checkReputation: if true, TrustScore verification will be performed.
-
clientHumanId: a client-generated unique ID to identify a specific customer across business lines.
-
clientRequestId: a client-generated unique ID for a specific session. This can be used to identify specific requests.
-
smsMessage: optional field to customize the message body sent in the Instant Link or OTP SMS message. Otherwise, you can use Prove defaults.
-
clientCustomerId: a client-generated unique ID for a specific customer. You can link calls related to the same customer, across different requests or sessions. The client defines the format of this ID.
-
deviceId: the unique identifier for the Prove Key on the device.
-
emailAddress: the email address of the customer.
-
ipAddress: the IP address of the customer.
-
proveId: a unique ID to identify a specific customer obtained from a previous successful authentication.
For OTP retries, make sure to implement client SDK changes in the next 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)
}
Send a request to your back end server with the possession type to start the flow.Additional parameters:
-
finalTargetURL: required when flowType=desktop. This should be a URL you maintain. Once the customer clicks the Instant Link, they will be redirected to this URL. It should instruct the customer to continue the workflow. Maximum length is 128 characters.
-
smsMessage: optional field to customize the message body sent in the Instant Link or OTP SMS message. Otherwise, you can use Prove defaults.
-
clientCustomerId: a client-generated unique ID for a specific customer. You can link calls related to the same customer, across different requests or sessions. The client defines the format of this ID.
-
clientRequestId: a client-generated unique ID for a specific session. You can identify specific requests using this field. You determine the format of this ID.
-
allowOTPRetry: set to true to allow the customer to re-enter the OTP up to three times. Defaults to false.
For OTP retries, make sure to implement client SDK changes in the next step.
// Send the Unify request.
rspUnify, err := client.V3.V3UnifyRequest(ctx, &components.V3UnifyRequest{
PossessionType: "mobile",
ClientRequestID: provesdkservergo.String("client-abc-123"),
AllowOTPRetry: true,
})
if err != nil {
t.Fatal(err)
}
The function returns the following fields:
-
authToken: send this to your client-side code to pass into the Authenticate() function - it’s a short lived JSON Web Token (JWT) tied to the current flow and used for the possession checks.
-
correlationId: save this in your current session, then pass it in to the UnifyStatus() function call of the same flow. The correlation ID ties together different system calls for the same Prove flow. It also aids in troubleshooting. The session expires in 15 minutes from when the correlation ID returns from the Unify() call.
-
success: will return pending for this initial call.
Return the authToken in a response to the front end.Authenticate
Once you have the authToken, build the authenticator for both the mobile and desktop flows. Web SDK
Android SDK
iOS SDK
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))
.withMobileAuthImplementation("fetch")
.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);
}
In the desktop flow, a WebSocket opens for three minutes on the desktop browser while waiting for the customer to select the link in the text message. Once clicked, the WebSocket closes and the AuthFinishStep function finishes.// 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 cellular 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 (without a cellular data connection available). 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 application thread. The application employs an executor service with a minimum of two threads to manage threads due to the SDK’s 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());
}
}
// 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()
In the event a cellular data connection is unavailable during testing, use the Builder class. It permits simulated successful session results while connected to a Wi-Fi network. Testing using a Wi-Fi connection is useful in the Sandbox environment.proveAuthSdk = ProveAuth.builder(authFinish: finishStep)
.withMobileAuthTestMode() // Test mode flag
.build()
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 application thread. The application employs an executor service with a minimum of two threads to manage threads due to the SDK’s ability to process concurrent blocking requests.// 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)
}
}
Verify Mobile Number
In the AuthFinishStep, you’ll 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)
}
The function returns the following fields:
-
success: either true if the mobile number validation was successful, or false if it failed.
-
phoneNumber: the phone number associated with the possession check.
-
clientHumanId: if provided in the request.
-
clientRequestId: if provided in the request.
-
deviceId: the unique identifier for the Prove Key on the device.
-
evaluation: the evaluation result for the Global Fraud Policy.
-
proveId: a unique ID to identify a specific customer obtained from a successful possession check.
You can then respond to the front end with the results of the authentication. Only mobile channels are supported for this flow.
Initialize the Flow
You need to send a request to your back end server with the phone number and possessionType=none to start the flow.Additional parameters:
-
checkReputation: if true, TrustScore verification will be performed.
-
clientHumanId: a client-generated unique ID to identify a specific customer across business lines.
-
clientCustomerId: a client-generated unique ID for a specific customer. You can link calls related to the same customer, across different requests or sessions. The client defines the format of this ID.
-
deviceId: the unique identifier for the Prove Key on the device.
-
emailAddress: the email address of the customer.
-
ipAddress: the IP address of the customer.
-
proveId: a unique ID to identify a specific customer obtained from a previous successful authentication.
-
rebind: if true, rebinds the Prove Key with the newly verified phone number.
// Send the Unify request.
rspUnify, err := client.V3.V3UnifyRequest(ctx, &components.V3UnifyRequest{
PhoneNumber: "2001004014",
PossessionType: "mobile",
})
if err != nil {
t.Fatal(err)
}
The function returns the following fields:
-
authToken: send this to your client-side code to pass into the Authenticate() function - it’s a short lived JSON Web Token (JWT) tied to the current flow and used for the possession checks.
-
correlationId: save this in your current session, then pass it in to the UnifyStatus() function call of the same flow. The correlation ID ties together different system calls for the same Prove flow. It also aids in troubleshooting. The session expires in 15 minutes from when the correlation ID returns from the Unify() call.
-
success: will return pending for this initial call.
Return the authToken in a response to the front end. Authenticate
Initialize the client-side SDK to place a Prove key on the device or to check if a Prove key is bound.async function authenticate(isMobileWeb, authToken) {
// Set up the authenticator for the mobile flow.
let builder = new proveAuth.AuthenticatorBuilder();
builder = builder
.withAuthFinishStep((input) => verify(input.authId));
const authenticator = builder.build();
// Authenticate with the authToken.
return authenticator.authenticate(authToken);
}
Verify Mobile Number
In the AuthFinishStep of the client SDK, you’ll need to have the function make a call to an endpoint on your back end server. Your backend server should then call 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)
}
The function returns the following fields:
-
success: either possession_required if the reputation check was successful, or false if it failed.
-
phoneNumber: the phone number associated with the possession check.
-
clientHumanId: if provided in the request.
-
clientRequestId: if provided in the request.
-
deviceId: the unique identifier for the Prove Key on the device.
-
evaluation: the evaluation result for the Global Fraud Policy.
-
proveId: a unique ID to identify a specific customer obtained from a successful possession check.
You can then respond to the front end with the results of the authentication. Perform Possession Check
If possession is required, your application needs to perform a customer-supplied possession check such as SMS OTP. ONLY proceed to the next step if the posession check passes.
Call the Bind Endpoint
Call UnifyBind() after UnifyStatus() returns success=possession_required. Call this endpoint if and only if the possession check has passed. This binds the phone number to the Prove Key for future authentications.This function takes these required parameters:
-
correlationId: the ID returned by the Unify() function.
-
phoneNumber: the phone number to bind to the Prove Key.
-
clientRequestId: a client-generated unique ID for a specific session. This can be used to identify specific requests.
rspUnifyBind, err := client.V3.V3UnifyBindRequest(context.TODO(), &components.V3UnifyBindRequest{
CorrelationID: rspUnify.V3UnifyResponse.CorrelationID,
PhoneNumber: "2001004018",
})
if err != nil {
return fmt.Errorf("error on UnifyBind(): %w", err)
}
The function returns the following fields:
-
success: true if the binding succeeded, false if it failed.
-
phoneNumber: the phone number that was bound to the Prove Key.
-
clientHumanId: if provided in the request.
-
clientRequestId: if provided in the request.
-
deviceId: the unique identifier for the Prove Key on the device.
-
evaluation: the evaluation result for the Global Fraud Policy.
Prove Key BehaviorThe following are things to be aware of when using the Prove Key: Prove Possession - Desktop
Prove Possession - Mobile
Customer-Supplied Possession
The Prove Key is ignored and Instant Link is performed.
If you send a different phone number to /unify than the one registered to the Prove key, you will receive success=false 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 will force a full possession check and then, if valid, will rebind the Prove Key to the new phone number.
If you send a different phone number to /unify than the one registered to the Prove key, the customer will receive possession_required on the /unify-status call. You will need to call /unify-bind to rebind the Prove Key to the new number. Once it’s rebound, the previous number will ask for possession_required. The Prove key only supports one phone number.