The Android SDK is a set of lightweight libraries (267 kB total compressed size for the minimum required components without external dependencies). The libraries deliver as Android Archive Repository (.aar) files. The minimum supported version of Android is v7 (level 24). Prove’s Android SDK recommends the previous three major Android versions, but successful transactions occur with Android v8 in production.
Prove manages a maven repository with Android binaries to enable simple 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.3.0'}
You’ll also need to point to the repository by updating your 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/"}}}
The following needs added to the build.gradle file to also download dependency libraries:
dependencies { implementation fileTree('libs')}
If you receive an error message on the 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):
Since permissions are automatically merged from the library’s manifest (SDKs) into the main application, the Prove Auth SDK (and its children SDKs) merges the following non-dangerous permissions:
<!-- Required to perform authentication --><uses-permissionandroid:name="android.permission.INTERNET"/><!-- Required to access information about networks --><uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/><!-- Required for ConnectivityManager.requestNetwork --><uses-permissionandroid:name="android.permission.CHANGE_NETWORK_STATE"/>
Unlike the Web SDK, when using the Android SDK, use the mobile flow. Pass mobile to the Start() function on the server. In a mobile flow, Mobile Auth℠ executes first and if that fails, it will perform 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 Auth
Mobile Auth requires the end user to disable any VPN.
The SDK requires an authToken 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.
To start the flow, you’ll need to send a request to your backend server with the phone number, flow type, and an optional challenge of either the date of birth (YYYY-MM-DD) or social security number (last four digits).
Stringinitialize(String phoneNumber,String ssn,String flowType){YourBackendClient backend =newYourBackendClient();// Backend API client// TODO: Build your InitializeRequest objectInitializeRequest initializeRequest =newInitializeRequest(phoneNumber, ssn, flowType);// Send an initialize request to your backend server to get authTokenInitializeResponse response = backend.initialize(initializeRequest);// TODO: define your own InitializeResponse object to parse authToken stringreturn response.getAuthToken();}
Once you have the authToken, build the authenticator for the mobile flow.
// Object implementing AuthFinishStep interfaceAuthFinishStep authFinishStep =newAuthFinishStep(){...};// Objects implementing OtpStartStep/OtpFinishStep interfacesOtpStartStep otpStartStep =newOtpStartStep(){...};OtpFinishStep otpFinishStep =newOtpFinishStep(){...};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). This feature is particularly useful for testing on Emulator. This can only be used in Sandbox mode.
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 cannot execute in the main application thread. The application manages threads, for instance, with an executor service, which provides at least two threads since SDK may have more then one simultaneous blocking requests.
publicclassMyAuthenticator{privatefinalMyBackendClient backend =newMyBackendClient();// Backend API clientprivatefinalAuthFinishStep authFinishStep =newAuthFinishStep(){@Overridevoidexecute(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}}};privateProveAuth proveAuth;publicMyAuthenticator(Context context){ proveAuth =ProveAuth.builder().withAuthFinishStep(authFinishStep).withOtpFallback(otpStartStep, otpFinishStep).withContext(context).build();}publicvoidauthenticate()throwsIOException,ProveAuthException{AuthStartResponse response = backend.authStart("My Prove Auth App"); proveAuth.authenticate(response.getAuthToken());}}
In the AuthFinishStep, you’ll specify a function to call once the possession checks are complete on the mobile phone. This endpoint on your backend server will then call the Validate() function to check phone number validation. If it was successful, the server returns the results from the Challenge() function that will include user information.
// Send a verify request to get return user information.voidverify(String: authId){YourBackendClient backend =newYourBackendClient();// Backend API client// Build your VerifyRequest objectVerifyRequest verifyRequest =newVerifyRequest(authId,...);// Send a verify request to your backend server to get return user information.VerifyResponse response = backend.verify(verifyRequest);// TODO: define your VerifyResponse object to parse user information from the responseString firstName = response.getFirstName();String lastName = response.getLastName();// Pre-fill user information to your Android App UI, for example: firstNameEditText.setText(firstName); lastNameEditText.setText(lastName);}
There are two functions to implement for the OTP handling - a start and a finish step.
Retry functionality is disabled in OTP.
In order to set the OTP handlers, implement OtpStartStep and OtpFinishStep. The Java snippet has an example.
OtpStartStep example:
packagecom.prove.android_sample_app;importandroid.app.Activity;importandroid.content.Context;importandroid.content.Intent;importandroidx.activity.result.ActivityResultLauncher;importandroidx.activity.result.ActivityResultRegistry;importandroidx.activity.result.contract.ActivityResultContracts;importandroidx.annotation.NonNull;importandroidx.annotation.Nullable;importandroidx.lifecycle.DefaultLifecycleObserver;importandroidx.lifecycle.LifecycleOwner;importcom.prove.sdk.proveauth.OtpStartInput;importcom.prove.sdk.proveauth.OtpStartStepCallback;importcom.prove.sdk.proveauth.PhoneNumberValidationException;importcom.prove.sdk.proveauth.ProveAuthException;importjava.util.concurrent.atomic.AtomicReference;publicclassOtpStartStepimplementscom.prove.sdk.proveauth.OtpStartStep,DefaultLifecycleObserver{publicstaticfinalStringOTP_START_STEP_KEY="OtpStartStep";privatefinalActivityResultRegistry registry;privateActivityResultLauncher<Intent> launcher;privatefinalContext context;privatefinalAtomicReference<OtpStartStepCallback> otpStartStepCallbackRef =newAtomicReference<>();publicOtpStartStep(Context context,@NonNullActivityResultRegistry registry){this.context = context;this.registry = registry;}@Overridepublicvoidexecute(boolean phoneNumberNeeded,@NullableProveAuthException exception,OtpStartStepCallback otpStartStepCallback){if(exception instanceofPhoneNumberValidationException){ otpStartStepCallbackRef.set(otpStartStepCallback);Intent intent =newIntent(context,OtpPhoneNumberInputActivity.class); intent.putExtra(IntentExtraFields.OTP_ERROR,"Phone number is invalid, please try again."); launcher.launch(intent);return;}if(phoneNumberNeeded){// We need to store callback object, it will be needed to complete this activity// by calling it with the results. otpStartStepCallbackRef.set(otpStartStepCallback);Intent intent =newIntent(context,OtpPhoneNumberInputActivity.class); launcher.launch(intent);}else{ otpStartStepCallback.onSuccess(newOtpStartInput(""));}}publicvoidonCreate(@NonNullLifecycleOwner owner){ launcher = registry.register(OTP_START_STEP_KEY, owner,newActivityResultContracts.StartActivityForResult(), result ->{OtpStartStepCallback callback = otpStartStepCallbackRef.get();if(callback ==null){return;}if(result.getResultCode()==Activity.RESULT_OK){Intent intent = result.getData();if(intent !=null){String phoneNumber = intent.getStringExtra(IntentExtraFields.OTP_PHONE_NUMBER_FIELD); callback.onSuccess(newOtpStartInput(phoneNumber));}else{ callback.onError();}}else{ callback.onError();}});}}
OtpFinishStep example:
importandroid.app.Activity;importandroid.content.Context;importandroid.content.Intent;importandroid.util.Log;importandroidx.activity.result.ActivityResultLauncher;importandroidx.activity.result.ActivityResultRegistry;importandroidx.activity.result.contract.ActivityResultContracts;importandroidx.annotation.NonNull;importandroidx.annotation.Nullable;importandroidx.lifecycle.DefaultLifecycleObserver;importandroidx.lifecycle.LifecycleOwner;importcom.prove.sdk.proveauth.OtpFinishInput;importcom.prove.sdk.proveauth.OtpFinishStepCallback;importcom.prove.sdk.proveauth.OtpValidationException;importcom.prove.sdk.proveauth.ProveAuthException;importjava.util.concurrent.atomic.AtomicReference;publicclassOtpFinishStepimplementscom.prove.sdk.proveauth.OtpFinishStep,DefaultLifecycleObserver{publicstaticfinalStringOTP_FINISH_STEP_KEY="OtpFinishStep";privatefinalActivityResultRegistry registry;privateActivityResultLauncher<Intent> launcher;privatefinalContext context;privatefinalAtomicReference<OtpFinishStepCallback> otpFinishStepCallbackRef =newAtomicReference<>();publicOtpFinishStep(Context context,@NonNullActivityResultRegistry registry){this.context = context;this.registry = registry;}@Overridepublicvoidexecute(@NullableProveAuthException otpException,OtpFinishStepCallback otpFinishStepCallback){// We need to store callback object, it will be needed to complete this activity// by calling it with the results. otpFinishStepCallbackRef.set(otpFinishStepCallback);if(otpException instanceofOtpValidationException){Log.e("OtpFinishStep","found otp errors", e);return;}Intent intent =newIntent(context,OtpCodeInputActivity.class); launcher.launch(intent);}publicvoidonCreate(@NonNullLifecycleOwner owner){ launcher = registry.register(OTP_FINISH_STEP_KEY, owner,newActivityResultContracts.StartActivityForResult(), result ->{OtpFinishStepCallback callback = otpFinishStepCallbackRef.get();if(callback ==null){return;}if(result.getResultCode()==Activity.RESULT_OK){Intent intent = result.getData();if(intent !=null){String code = intent.getStringExtra(IntentExtraFields.OTP_CODE_FIELD); callback.onSuccess(newOtpFinishInput(code));return;}} callback.onError();});}}
Once the user has made any edits to their pre-fill information, you should submit that information to the backend server so the Complete() call can then verify the user information.
// Send request to the backend to verify user information.SendInfoResponsesendInfo(String firstName,String lastName){YourBackendClient backend =newYourBackendClient();// Backend API client// TODO: Build your SendInfoRequest objectSendInfoRequest sendInfoRequest =newSendInfoRequest(firstName, lastName,...);// Send a sendInfoRequest to your backend server to get return user information.SendInfoResponse response = backend.verify(sendInfoRequest);// TODO: define your own SendInfoResponse object to parse the responsereturn response;}