Skip to main content
Possession requires both a client-side SDK (Web, iOS, or Android) 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 Web, Android, or iOS SDK for possession checks and the Prove Key. For the browser flow, see Identity Web SDK.

How to implement

You must integrate the client-side SDK for possession checks and the Prove Key.
This section only applies to mobile channels.
1

Determine Type of Flow

Decide 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()
2

Initialize the Flow

Send a request to your back end server with possessionType=none to start the flow. See POST /v3/unify for optional identifiers such as clientCustomerId and clientRequestId and the full response.
// Send the Unify request.
rspUnify, err := client.V3.V3UnifyRequest(ctx, &components.V3UnifyRequest{
  PossessionType: "none",
  ClientRequestID: provesdkservergo.String("client-abc-123"),
})
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.
3

Authenticate

Once you have the authToken, build the authenticator for both the mobile and desktop flows.
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);
}
If you’re using Content Security Policy headers, ensure you allow connect-src for wss://*.prove-auth.proveapis.com.
4

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)
}
Use success (including possession_required when a possession check is still required) to drive your UI and next server calls. See POST /v3/unify-status for the full response schema. Interpret evaluation using the Global Fraud Policy.
5

Perform Possession Check

If success returned possession_required, your app must perform a customer-supplied possession check such as SMS OTP. Only proceed to the next step if the possession check passes.
6

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 requires correlationId and phoneNumber. See the /v3/unify-bind reference for the full schema. You can also send optional fields such as clientRequestId for session tracking.
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)
}
Return the binding outcome to the client. See POST /v3/unify-bind for all response fields. Interpret evaluation using the Global Fraud Policy.
If you send a different phone number to /unify than the one registered to the Prove key, the customer receives possession_required on the /unify-status call. Call /unify-bind to rebind the Prove Key to the new number. Once it’s rebound, the first number responds with possession_required. The Prove key only supports one 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

CodeDescription
9101Phone number doesn’t match Prove Key. Rebind required.

Error code 9101 - Phone number doesn’t match Prove Key

For Prove Possession - Mobile and Customer-Supplied Possession, 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:
  1. Prove initiates a new authentication check.
  2. If the authentication is successful, the Prove Key is rebound to the new phone number passed into the request.
  3. 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 or success=possession_required, depending on the flow.