Mobile authentication

Introduction

The Onegini Mobile Security Platform offers a mobile authentication mechanism in a user friendly and secure way. You can take advantage of the mobile authentication to add a second factor authentication for your product, that can be used to improve security of selected actions like logging into your website or accepting a transaction payment.

What to set up

As a medium to send push messages for mobile authentication Google Cloud Messaging (GCM) is used so you have to set it up:

  • in a Google Console,
  • on the Token Server,
  • within your Android application.

To set up GCM in the Google Console and the Token Server please follow steps described in google documentation and the Token Server's documentation.

Requirements for mobile authentication

In order to use the mobile authentication the device has to support Google Play Services. It's a responsibility of the app's developer to check if push notifications are supported on the device. Once push notifications are available on the device the app should fetch the registration id, which is used as a push token during the mobile authentication enrollment. Once again please refer to the google documentation on how to obtain this registration id. In order to use mobile fingerprint authentication you need to make sure that device has a fingerprint scanner and fulfills all requirements that are listed here.

Enrollment

After you've set up the GCM, you need to perform an enrollment for authenticated user in order to register him for the mobile authentication. The user can be enrolled on only one application instance at a time. If the user has two mobile devices on which the application is installed, the user can only enroll for mobile authentication on one of these devices. It's your application responsibility to handle all of the GCM communication including fetching the registration id. The reason why this responsibility is in the app is that one app can only have one registration id. This allows the app to support more push messaging features besides the mobile authentication provider by the SDK.

Example code to initialize mobile authentication enrollment for currently authenticated user

final UserClient userClient = OneginiSDK.getOneginiClient(context).getUserClient();
// should be triggered for authentiated user only or an error will be returned
userClient.enrollUserForMobileAuthentication("myRegistrationId", new OneginiMobileAuthenticationEnrollmentHandler() {

  @Override
  void onSuccess() {
    // Method called when mobile authentication was successfully enrolled.
    setMobileAuthenticationEnabled(true);
  }

  @Override
  public void onError(final OneginiMobileAuthenticationEnrollmentError error) {
    @OneginiMobileAuthenticationEnrollmentError.MobileAuthenticationEnrollmentErrorType final int errorType = error.getErrorType();
    // This method is called when a mobile authentication enrollment error occurs, for example when the device is deregistered
    if (errorType == OneginiMobileAuthenticationEnrollmentError.DEVICE_DEREGISTERED) {
      new DeregistrationUtil(GCMInstanceIDListenerService.this).onDeviceDeregistered();
    }
    setMobileAuthenticationEnabled(false);
  }

  private void setMobileAuthenticationEnabled(final boolean isEnabled) {
    UserProfile authenticatedUserProfile = OneginiSDK.getOneginiClient(this).getUserClient().getAuthenticatedUserProfile();
    if (authenticatedUserProfile == null) {
      return;
    }

    final SettingsStorage settingsStorage = new SettingsStorage(GCMInstanceIDListenerService.this);
    settingsStorage.setMobileAuthenticationEnabled(authenticatedUserProfile, isEnabled);
  }
);

If you want to use mobile fingerprint authentication you need to provide an instance of OneginiMobileAuthenticationFingerprintRequestHandler with the OneginiClientBuilder using setMobileAuthenticationFingerprintRequestHandler() method. You also need to register a fingerprint authenticator to be able to use it.

Example code for registering a fingerprint authentication

OneginiAuthenticator fingerprintAuthenticator;
// to get the fingerprint authenticator
final Set<OneginiAuthenticator> notRegisteredAuthenticators = OneginiSDK.getOneginiClient(context).getUserClient().getNotRegisteredAuthenticators(userProfile);
for (final OneginiAuthenticator auth : notRegisteredAuthenticators) {
    if (auth.getType() == OneginiAuthenticator.FINGERPRINT) {
      // the fingerprint authenticator is available for registration
      fingerprintAuthenticator = auth;
    }
}

// fingerprint authentication is not always available
if (fingerprintAuthenticator == null) {
  return;
}

//to register the authenticator
OneginiSDK.getOneginiClient(context).getUserClient().registerAuthenticator(fingerprintAuthenticator, new OneginiAuthenticatorRegistrationHandler() {
    @Override
    public void onSuccess() {
      // successfully registered authenticator
    }

    @Override
    public void onError(final OneginiAuthenticatorRegistrationError oneginiAuthenticatorRegistrationError) {
      // handle different errors, below we mapped two of the possible errors that can occur
      @OneginiAuthenticatorRegistrationError.AuthenticatorRegistrationErrorType int errorType = error.getErrorType();
      if (errorType == OneginiChangePinError.USER_DEREGISTERED) {
        UserProfile authenticatedUserProfile = OneginiSDK.getOneginiClient(SettingsAuthenticatorsActivity.this).getUserClient().getAuthenticatedUserProfile();
        if (authenticatedUserProfile == null) {
          return;
        }
        new DeregistrationUtil(SettingsAuthenticatorsActivity.this).onUserDeregistered(authenticatedUserProfile);
      } else if (errorType == OneginiChangePinError.DEVICE_DEREGISTERED) {
        new DeregistrationUtil(SettingsAuthenticatorsActivity.this).onDeviceDeregistered();
      }
    }
});

Handling push notifications

Push notifications are handled by the application that delegates the handling of the push notification to the SDK. The SDK will determine if it should do something with the push notification or not before handling it. This allows the app to support multiple types of push notifications next to the mobile authentication push notification. You can trigger it with handleMobileAuthenticationRequest() on the UserClient instance.

public void handleMobileAuthenticationRequest(final Bundle pushMessage, final OneginiMobileAuthenticationHandler mobileAuthenticationHandler);

Then the SDK will use OneginiMobileAuthenticationHandler to perform the request and end in one of the two methods of OneginiMobileAuthenticationHandler:

  • Mobile Authentication was successful

    void onSuccess();
    
  • Mobile Authentication failed due to an error

    void onError(OneginiMobileAuthenticationError error);
    

    Please note, that the user has an ability to deny incoming mobile authentication request. In such case the onError method will be called with an ACTION_CANCELED error type.

For more info on error handling see the error handling topic guide.

Example code push notification handling

@Override
protected void onHandleIntent(final Intent intent) {
  final Bundle extras = intent.getExtras();
  if (!extras.isEmpty()) {
    OneginiSDK.getOneginiClient(this).getUserClient().handleMobileAuthenticationRequest(extras, new OneginiMobileAuthenticationHandler() {
      @Override
      public void onSuccess() {
        // Method called when mobile authentication request was successful.
      }

      @Override
      public void onError(final OneginiMobileAuthenticationError oneginiMobileAuthenticationError) {
        Toast.makeText(GCMListenerService.this, oneginiMobileAuthenticationError.getErrorDescription(), Toast.LENGTH_SHORT).show();
        @OneginiMobileAuthenticationError.MobileAuthenticationEnrollmentErrorType final int errorType = oneginiMobileAuthenticationError.getErrorType();
        if (errorType == OneginiMobileAuthenticationError.USER_DEREGISTERED) {
          // the user was deregister, for example he provided a wrong PIN for too many times. You can handle the deregistration here, but since this application
          // supports multiple profiles we handle it when the user tries to login the next time because we don't know which user profile was deregistered at
          // this point.
        } else if (errorType == OneginiMobileAuthenticationError.DEVICE_DEREGISTERED) {
          new DeregistrationUtil(getApplicationContext()).onDeviceDeregistered();
        }
      }
    });
  }
}

As you can see from example above when user got deregistered an error is reported to the app through onError method. To enable mobile authentication for that user he needs to register and enroll once again.

Types of mobile authentication requests

You can configure different mobile authentication types in the Token Server mobile authentication configuration. For every type you can choose between different methods of authentication. These are: Push, Push with PIN, Push with fingerprint and Push with FIDO. The types of for mobile authentication must be configured on the Token Server first before you can use them. You can define multiple types with the same authentication method.

Push

The first type of mobile authentication request, the most simple one, is Push. In this type user have possibility to simply accept or deny request. To support the Push type in your app you need to implement OneginiMobileAuthenticationRequestHandler interface and set its instance during the SDK initialization step by calling setMobileAuthenticationRequestHandler(OneginiMobileAuthenticationRequestHandler handler) method on OneginiClientBuilder. Then you need to handle all interface methods in order to show/hide user interface with (for example) buttons allowing to accept or deny request.

Example how to set OneginiMobileAuthenticationRequestHandler

new OneginiClientBuilder(applicationContext, createPinRequestHandler, pinAuthenticationRequestHandler)
  .setMobileAuthenticationRequestHandler(new MobileAuthenticationRequestHandler(applicationContext))
  .build();

Example how to implement OneginiMobileAuthenticationRequestHandler

public class MobileAuthenticationRequestHandler implements OneginiMobileAuthenticationRequestHandler {

  public static OneginiAcceptDenyCallback CALLBACK;

  private final Context context;

  public MobileAuthenticationRequestHandler(final Context context) {
    this.context = context;
  }

  @Override
  public void startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest,
                                  final OneginiAcceptDenyCallback oneginiAcceptDenyCallback) {
    CALLBACK = oneginiAcceptDenyCallback;
    openActivity(oneginiMobileAuthenticationRequest.getUserProfile().getProfileId(), oneginiMobileAuthenticationRequest.getMessage());
  }

  @Override
  public void finishAuthentication() {
    closeActivity();
  }

  private void openActivity(final String profileId, final String message) {
    final Intent intent = new Intent(context, MobileAuthenticationActivity.class);
    intent.putExtra(MobileAuthenticationActivity.EXTRA_COMMAND, MobileAuthenticationActivity.COMMAND_START);
    intent.putExtra(MobileAuthenticationActivity.EXTRA_PROFILE_ID, profileId);
    intent.putExtra(MobileAuthenticationActivity.EXTRA_MESSAGE, message);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
  }

  private void closeActivity() {
    final Intent intent = new Intent(context, MobileAuthenticationActivity.class);
    intent.putExtra(MobileAuthenticationActivity.EXTRA_COMMAND, MobileAuthenticationActivity.COMMAND_FINISH);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
  }
}

You need to implement both startAuthentication and finishAuthentication methods:

  • startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiAcceptDenyCallback oneginiAcceptDenyCallback) is called when new mobile authentication request is received. First parameter is OneginiMobileAuthenticationRequest object which contains message you should present to the user and UserProfile object that specifies request's receiver. The Second parameter is a callback object that you should use when user accepts or denies the request, by calling acceptAuthenticationRequest or denyAuthenticationRequest respectively.
  • finishAuthentication method is called after end of processing the user response to the request.

Push with PIN

The Second type of mobile authentication request is Push with PIN. In this type the user also have a possibility to accept or deny request. Comparing to simple Push the Push with PIN requires the user to enter his PIN in order to successfully accept the mobile authentication request. To support Push with PIN type you need to implement MobileAuthenticationPinRequestHandler interface and set its instance during SDK initialization step by calling setMobileAuthenticationPinRequestHandler(MobileAuthenticationPinRequestHandler handler) method on OneginiClientBuilder. You also need to handle all interface methods in order to show/hide user interface with a PIN input and buttons allowing to accept or deny request.

Example how to implement OneginiMobileAuthenticationPinRequestHandler


public class MobileAuthenticationPinRequestHandler implements OneginiMobileAuthenticationPinRequestHandler {

  public static OneginiPinCallback CALLBACK;

  private final Context context;

  private String message;
  private String userProfileId;
  private int failedAttemptsCount;
  private int maxAttemptsCount;

  public MobileAuthenticationPinRequestHandler(final Context context) {
    this.context = context;
  }

  @Override
  public void startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter) {
    CALLBACK = oneginiPinCallback;
    message = oneginiMobileAuthenticationRequest.getMessage();
    userProfileId = oneginiMobileAuthenticationRequest.getUserProfile().getProfileId();
    failedAttemptsCount = maxAttemptsCount = 0;
    startActivity();
  }

  @Override
  public void onNextAuthenticationAttempt(final AuthenticationAttemptCounter attemptCounter) {
    this.failedAttemptsCount = attemptCounter.getFailedAttempts();
    this.maxAttemptsCount = attemptCounter.getMaxAttempts();
    startActivity();
  }

  @Override
  public void finishAuthentication() {
    closeActivity();
  }

  private void startActivity() {
    final Intent intent = new Intent(context, MobileAuthenticationPinActivity.class);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_COMMAND, MobileAuthenticationPinActivity.COMMAND_START);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_MESSAGE, message);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_PROFILE_ID, userProfileId);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_FAILED_ATTEMPTS_COUNT, failedAttemptsCount);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_MAX_FAILED_ATTEMPTS, maxAttemptsCount);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);

    context.startActivity(intent);
  }

  private void closeActivity() {
    final Intent intent = new Intent(context, MobileAuthenticationPinActivity.class);
    intent.putExtra(MobileAuthenticationPinActivity.EXTRA_COMMAND, MobileAuthenticationPinActivity.COMMAND_FINISH);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
  }
}

You need to implement startAuthentication, onNextAuthenticationAttempt and finishAuthentication methods:

  • startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter) the app receives new mobile authentication request. First parameter is OneginiMobileAuthenticationRequest object which contains message you should present to the end user, UserProfile object that specifies request's receiver. Second parameter is a callback object you should use when user accepts or denies incoming request by calling: acceptAuthenticationRequest(pinEnteredByUser) or denyAuthenticationRequest respectively. Third parameter is AuthenticationAttemptCounter that holds information about failed, remaining and max PIN attempts.
  • onNextAuthenticationAttempt(final AuthenticationAttemptCounter attemptCounter) is called when user entered wrong pin. The attemptCounter param object gives you the information about failed attempts count via getFailedAttempts() method indicating how many times wrong PIN was entered during current authentication attempt. Same way you can get max attempts with getMaxAttempts() method and remaining attempts with getRemainingAttempts(). You can display those values to the user.
  • finishAuthentication is called after end of processing the user response to the request.

Push with fingerprint

The third type of mobile authentication request is Push with fingerprint. In this type the user also have a possibility to accept or deny request. The Push with fingerprint requires the user to scan his fingerprint in order to successfully accept the mobile authentication request. To support Push with fingerprint type you need to implement OneginiMobileAuthenticationFingerprintRequestHandler interface and set its instance during SDK initialization step by calling setMobileAuthenticationFingerprintRequestHandler(OneginiMobileAuthenticationFingerprintRequestHandler mobileAuthenticationFingerprintRequestHandler) method on OneginiClientBuilder. You also need to handle all interface methods in order to show/hide user interface with a fingerprint input and buttons allowing to accept or deny request as well as fallback to PIN.

Example how to implement OneginiMobileAuthenticationFingerprintRequestHandler

public class MobileAuthenticationFingerprintRequestHandler implements OneginiMobileAuthenticationFingerprintRequestHandler {

  public static OneginiFingerprintCallback fingerprintCallback;
  private final Context context;

  public MobileAuthenticationFingerprintRequestHandler(final Context context) {
    this.context = context;
  }

  @Override
  public void startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiFingerprintCallback oneginiFingerprintCallback) {
    fingerprintCallback = oneginiFingerprintCallback;
    startFingerprintActivity(MSG_EXTRA_ASK_TO_ACCEPT_OR_DENY);
  }

  @Override
  public void onNextAuthenticationAttempt() {
    startFingerprintActivity(MSG_EXTRA_RECEIVED_FINGERPRINT);
  }

  @Override
  public void onFingerprintCaptured() {
    startFingerprintActivity(MSG_EXTRA_SHOW_SCANNING);
  }

  @Override
  public void finishAuthentication() {
    startFingerprintActivity(MSG_EXTRA_CLOSE);
  }

  private void startFingerprintActivity(final String action) {
    final Intent intent = new Intent(context, MobileAuthenticationFingerprintActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    intent.putExtra(FingerprintActivity.MSG_EXTRA_ACTION, action);
    context.startActivity(intent);
  }
}

You need to implement the startAuthentication, onNextAuthenticationAttempt, onFingerprintCaptured and finishAuthentication methods:

  • startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiFingerprintCallback oneginiFingerprintCallback) the app receives new mobile authentication request. First parameter is OneginiMobileAuthenticationRequest object which contains message you should present to the end user, UserProfile object that specifies request's receiver. Second parameter is a callback object you should use when user accepts or denies incoming request by calling: acceptAuthenticationRequest(pinEnteredByUser) or denyAuthenticationRequest respectively. It also contains fallbackToPin() method that should be used when user decides not to scan the fingerprint but use PIN.
  • onNextAuthenticationAttempt() is called when user's fingerprint scan was verified as invalid. Note that scanner will be blocked when used will scan too many his fingerprint too many times without success. This is controlled by Android SDK and the number of attempts might differ depending on device vendor. When the attempts limit will be reached SDK will automatically fallback to PIN authentication.
  • onFingerprintCaptured() is called when fingerprint was scanned but not yet verified. You can show an information to the user that the verification process just started.
  • finishAuthentication() is called after end of processing the user response to the request.

Push with FIDO

The fourth type of mobile authentication request is Push with FIDO. In this type the user also have a possibility to accept or deny request. The Push with FIDO requires the user to authenticate. To support Push with fingerprint type you need to implement OneginiMobileAuthenticationFidoRequestHandler interface and set its instance during SDK initialization step by calling public OneginiClientBuilder setMobileAuthenticationFidoRequestHandler(final OneginiMobileAuthenticationFidoRequestHandler mobileAuthenticationFidoRequestHandler) method on OneginiClientBuilder. You also need to handle all interface methods in order to show/hide user interface with a fingerprint input and buttons allowing to accept or deny request as well as fallback to PIN.

Example how to implement OneginiMobileAuthenticationFidoRequestHandler

public class MobileAuthenticationFidoRequestHandler implements OneginiMobileAuthenticationFidoRequestHandler {

  public static OneginiFidoCallback CALLBACK;

  private String userProfileId;
  private String message;
  private final Context context;

  public MobileAuthenticationFidoRequestHandler(final Context context) {
    this.context = context;
  }

  @Override
  public void startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiFidoCallback oneginiFidoCallback) {
    CALLBACK = oneginiFidoCallback;
    userProfileId = oneginiMobileAuthenticationRequest.getUserProfile().getProfileId();
    message = oneginiMobileAuthenticationRequest.getMessage();
    notifyActivity(COMMAND_START);
  }

  @Override
  public void finishAuthentication() {
    notifyActivity(COMMAND_FINISH);
  }

  private void notifyActivity(final String command) {
    final Intent intent = new Intent(context, MobileAuthenticationFidoActivity.class);
    intent.putExtra(EXTRA_COMMAND, command);
    intent.putExtra(EXTRA_USER_PROFILE_ID, userProfileId);
    intent.putExtra(EXTRA_MESSAGE, message);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);
  }
}

You need to implement the startAuthentication, and finishAuthentication methods:

  • startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiFidoCallback oneginiFidoCallback) the app receives new mobile authentication request. First parameter is OneginiMobileAuthenticationRequest object which contains message you should present to the end user, UserProfile object that specifies request's receiver. Second parameter is a callback object you should use when user accepts or denies incoming request by calling: acceptAuthenticationRequest() or denyAuthenticationRequest() respectively. It also contains fallbackToPin() method that should be used when user decides not to scan the fingerprint but use PIN.
  • finishAuthentication() is called after end of processing the user response to the request.