Mobile authentication with Push
Two factor authentication can be implemented using GCM push notifications. It's a very secure and user friendly way of authentication. You can use this feature to allow users to confirm their transactions. Confirmation can be done using a simple Accept button, PIN, Fingerprint or a FIDO authenticator. All transaction information is encrypted and no sensitive information is sent through GCM.
Configuration
In order to use mobile authentication with push notifications the device has to support Google Play Services. It's your responsibility 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 with push enrollment. Please refer to the Google documentation on how to obtain this registration id. In order to use push mobile authentication with additional fingerprint verification you need to make sure that device has a fingerprint scanner and it fulfills all fingerprint requirements.
You also need to set up GCM in the Google Console and the Token Server as described in the Google documentation and Token Server documentation.
Enrollment
After you've set up GCM and enrolled the used for mobile authentication, you need to perform one additional enrollment step to allow your users to perform mobile authentication with push notifications. The user can be enrolled on only a single device (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 with push on one of these devices. All of the GCM communication must be handled by your application. This includes 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 mobile authentication provided the SDK.
To check if a particular user profile has already enrolled for mobile authentication with push the SDK exposes the method:
UserClient#isUserEnrolledForMobileAuthWithPush(UserProfile userProfile)
. Please note, that this method checks if the user has enrolled for push
enrollment in the past and that the user still has valid PGP keys stored on the device. Hoverer, the method doesn't have an ability to check if the
registration ID
used for enrollment with GCM is still valid or not.
Example code to initialize mobile authentication with push enrollment for the currently authenticated user
final UserClient userClient = OneginiSDK.getOneginiClient(context).getUserClient();
userClient.enrollUserForMobileAuthWithPush("myRegistrationId", new OneginiMobileAuthWithPushEnrollmentHandler() {
@Override
public void onSuccess() {
showToast("Mobile authentication enabled");
}
@Override
public void onError(final OneginiMobileAuthWithPushEnrollmentError error) {
@OneginiMobileAuthWithPushEnrollmentError.MobileAuthWithPushEnrollmentErrorType final int errorType = error.getErrorType();
if (errorType == OneginiMobileAuthWithPushEnrollmentError.DEVICE_DEREGISTERED) {
new DeregistrationUtil(SettingsActivity.this).onDeviceDeregistered();
}
showToast("Mobile authentication error - " + error.getMessage());
}
);
Request handling
Once a user is enrolled, he/she should be able to receive push mobile authentication requests. To verify if the user is already enrolled for push mobile
authentication, you should use the UserClient#isUserEnrolledForMobileAuthWithPush(UserProfile userProfile)
method.
The entire push mobile authentication process from can be described as follows. In this flow we call the initiator of the mobile authentication request 'portal':
1. Portal -> Token Server: Initialize mobile authentication.
2. Token Server -> GCM: Send the push notification.
3. GCM -> Token Server: Push notification delivery report.
4. Token Server -> Portal: Identifier of the initialized mobile authentication transaction.
5. GCN -> APP: push notification.
6. APP -> SDK: Delegate handling of the push notification to the SDK.
7. SDK -> Token Server: Fetch PGP encrypted message from server.
8. Token Server -> SDK: Base64 encoded PGP encrypted authentication data.
9. SDK -> APP: The push message is decrypted and the SDK triggers a request handler so the app can show
a dialogue to the end-user.
10. APP -> SDK: Application responds with the authentication result.
11. SDK -> Token Server: The authentication result is send encrypted to the Token Server.
12. Token Server -> SDK: If the used push type requires feedback on the result e.g. pin usage
the result is communicated back to the SDK which will optionally perform a retry.
13. Token Server -> Portal: A callback is sent to inform the portal about the mobile
authentication result.
As you can see from the diagram above, the application has the following responsibilities:
- Passing push notification received from GCM to the SDK
- Responding to authentication requests using the
*RequestHandler
interfaces- Displaying a dialog to the end-user when the authentication challenge is received
- Sending the users' response back to the SDK
- Handling the completion of the mobile authentication request
The Following paragraphs explain those steps and show how the responsibilities mentioned above can be implemented.
Passing a push notification (containing a mobile authentication request) to the SDK
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 handleMobileAuthWithPushRequest(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 anACTION_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().handleMobileAuthWithPushRequest(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.getMessage(), 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 with push
You can configure different types of mobile authentication with push 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. Each type of authentication need a proper handler implementation to be provided in the SDK builder, as described below.
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 OneginiMobileAuthWithPushRequestHandler
interface and set its instance during the SDK
initialization step by calling setMobileAuthWithPushRequestHandler(OneginiMobileAuthWithPushRequestHandler 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 OneginiMobileAuthWithPushRequestHandler
new OneginiClientBuilder(applicationContext, createPinRequestHandler, pinAuthenticationRequestHandler)
.setMobileAuthWithPushRequestHandler(new MobileAuthenticationRequestHandler(applicationContext))
.build();
Example how to implement OneginiMobileAuthWithPushRequestHandler
public class MobileAuthenticationRequestHandler implements OneginiMobileAuthWithPushRequestHandler {
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 isOneginiMobileAuthenticationRequest
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 callingacceptAuthenticationRequest
ordenyAuthenticationRequest
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 the OneginiMobileAuthWithPushPinRequestHandler
interface and set its instance during SDK initialization step by
calling the setMobileAuthWithPushPinRequestHandler(OneginiMobileAuthWithPushPinRequestHandler handler)
method on the 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 OneginiMobileAuthWithPushPinRequestHandler
public class MobileAuthWithPushPinRequestHandler implements OneginiMobileAuthWithPushPinRequestHandler {
public static OneginiPinCallback CALLBACK;
private final Context context;
private String message;
private String userProfileId;
private int failedAttemptsCount;
private int maxAttemptsCount;
public MobileAuthWithPushPinRequestHandler(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 the startAuthentication
, onNextAuthenticationAttempt
and finishAuthentication
methods:
startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter)
the app receives new mobile authentication request. First parameter isOneginiMobileAuthenticationRequest
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)
ordenyAuthenticationRequest
respectively. Third parameter isAuthenticationAttemptCounter
that holds information about failed, remaining and max PIN attempts.onNextAuthenticationAttempt(final AuthenticationAttemptCounter attemptCounter)
is called when user entered wrong pin. TheattemptCounter
param object gives you the information about failed attempts count viagetFailedAttempts()
method indicating how many times wrong PIN was entered during current authentication attempt. Same way you can get max attempts withgetMaxAttempts()
method and remaining attempts withgetRemainingAttempts()
. 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 the OneginiMobileAuthWithPushFingerprintRequestHandler
interface and set its instance during SDK initialization step by calling
the setMobileAuthWithPushFingerprintRequestHandler(OneginiMobileAuthWithPushFingerprintRequestHandler mobileAuthenticationFingerprintRequestHandler)
method on
the OneginiClientBuilder
. You also need to handle all interface methods in order to show/hide user interface with a fingerprint input and buttons allowing the
end-user to accept or deny the mobile authentication request as well as perform a fallback to PIN.
Example how to implement OneginiMobileAuthWithPushFingerprintRequestHandler
public class MobileAuthWithPushFingerprintRequestHandler implements OneginiMobileAuthWithPushFingerprintRequestHandler {
public static OneginiFingerprintCallback fingerprintCallback;
private final Context context;
public MobileAuthWithPushFingerprintRequestHandler(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 isOneginiMobileAuthenticationRequest
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)
ordenyAuthenticationRequest
respectively. It also containsfallbackToPin()
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 the OneginiMobileAuthenticationFidoRequestHandler
interface
and set its instance during SDK initialization step by calling the
public OneginiClientBuilder setMobileAuthWithPushFidoRequestHandler(final OneginiMobileAuthWithPushPinRequestHandler mobileAuthenticationFidoRequestHandler)
method on the OneginiClientBuilder
. You also need to handle all interface methods in order to show/hide buttons allowing the end-user to accept or deny the
mobile authentication request or perform a fallback to PIN.
Example how to implement OneginiMobileAuthWithPushPinRequestHandler
public class MobileAuthWithPushPinRequestHandler implements OneginiMobileAuthWithPushPinRequestHandler {
public static OneginiFidoCallback CALLBACK;
private String userProfileId;
private String message;
private final Context context;
public MobileAuthWithPushPinRequestHandler(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 isOneginiMobileAuthenticationRequest
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()
ordenyAuthenticationRequest()
respectively. It also containsfallbackToPin()
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.