Mobile authentication with PUSH
Introduction
Two factor authentication can be implemented using FCM 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, Biometrics or a custom authenticator. All transaction information is encrypted and no sensitive information is sent through FCM.
Configuration
First you need to set up FCM in the Firebase Console and the Token Server as described in the Token Server documentation.
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 Firebase instance token, 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 Firebase instance token. In order to use push mobile authentication with additional biometric verification you need to make sure that device has a biometric scanner and it fulfills all requirements.
Enrollment
After you've set up FCM 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 FCM communication must be handled by your application. This includes fetching the Firebase instance token. The reason why this responsibility is in the app is that one app can only have one Firebase instance token. This allows the app to support more push messaging features besides mobile authentication provided the SDK.
Push token enrollment
As mentioned on FCM docs the FCM SDK generates a registration token used
for communication with backend. The token has to be passed to the SDK via the OneginiClient#enrollUserForMobileAuthWithPush
method.
Example code to initialize mobile authentication with push enrollment for the currently authenticated user
Push token update
The FCM token could be rotated after initial startup. When this happens it's the app responsibility to notify the SDK that the token has
changed. To do so, call the DeviceClient#refreshMobileAuthPushToken
method with the new refresh token.
Example code to update the push token for all users
Please note, that:
- the method updates the token for all users of the app that have been enrolled for push and it doesn't require any user authentication
- the method will return an error if no user is registered for mobile authentication with push
Check if user is enrolled
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. However, the method doesn't have an ability to check if the
Firebase instance token used for enrollment with FCM is still valid or not.
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 can be described as follows. In this flow we call the initiator of the mobile authentication request 'portal':
As you can see from the diagram above, the application has the following responsibilities:
- Parsing and passing the data message received from FCM 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 the mobile authentication request to the SDK
The Token Server uses a data message
type of FCM notifications. This means that the app itself is responsible for handling the notification received from
FCM. To read more about FCM message types, please follow the FCM documentation.
When the app is capable to receive incoming FCM messages it should parse the incoming push
object (RemoteMessage
) in order to decide if it contains the Mobile Authentication Request. This allows the app to support multiple types of push
notifications next to the mobile authentication request. The request is a JSON object which contains the following properties:
og_transaction_id
an unique identifier for each mobile authentication requestog_message
the message specified by the portal when initializing the requestog_profile_id
the ID of the user profile for which the request was sent
Example body of the Mobile Authentication Request
The valid request should be parsed to the OneginiMobileAuthWithPushRequest
class, which has corresponding mandatory properties (transaction id, message,
user profile id) with two optional properties (timestamp and ttl) that are not used in this case.
The OneginiMobileAuthWithPushRequest
As you can see, the class also defines the mapping between the JSON properties and the class using @SerializedName
annotations. The recommended way to parse
the incoming message into the OneginiMobileAuthWithPushRequest is to use the Google Gson
parser and let it simply use the annotations to properly deserialize
the JSON.
Example code RemoteMessage parsing
When the mobile authentication request was successfully parsed, you can pass it to the SDK in order to handle it using handleMobileAuthWithPushRequest()
method on the UserClient
instance:
Then the SDK will use OneginiMobileAuthenticationHandler
to perform the request and end in one of the two methods of OneginiMobileAuthenticationHandler
:
- Mobile Authentication was successful
- Mobile Authentication failed due to an
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
Please note that CustomInfo is an optional param that can be non null only when custom authenticator was used.
Pending mobile authentication request
Mobile authentication requests that were send from the server using FCM, but were not delivered to the SDK are considered as "pending" requests. This can happen
when the message send through FCM was not delivered yet. Another scenario is when the app has received the message but didn't pass it to the
handleMobileAuthWithPushRequest()
method yet - i.e. the app has created a notification to the user instead, but he didn't answer it yet.
In both cases you can call the SDK for a list of pending mobile authentication requests. You can check the list on the app startup and start the pending requests handling regardless if user has ignored notifications on the notification bar or FCM has failed to deliver the message on time.
The pending mobile authentication request is also represented by the OneginiMobileAuthWithPushRequest
class. The class has number of methods to return the
following info:
public String getTransactionId()
returns an unique identifier for each mobile authentication requestpublic String getMessage()
returns the message specified by the portal when initializing the mobile authentication requestpublic String getUserProfileId
returns the ID of the user profile for which the mobile authentication request was sentpublic long getTimestamp()
returns an epoch/unix timestamp representing the date when the request was createdpublic int getTimeToLiveSeconds()
returns the maximum lifespan of a message (Time To Live) in seconds
Example code for fetching pending mobile authentication requests
In case when there are no pending mobile authentication requests for the device, the returned set will be empty.
Denying selected mobile authentication request
You can show a pending mobile authentication request list in your app using getPendingMobileAuthWithPushRequests()
. In that case, you might want to give users
ability to deny and delete selected request from the list without invoking request handling process through handleMobileAuthWithPushRequest()
method.
Example code for denying pending mobile authentication request
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 biometrics and Push with custom authenticator. 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 _
Example how to implement OneginiMobileAuthWithPushRequestHandler
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
You need to implement the startAuthentication
, onNextAuthenticationAttempt
and finishAuthentication
methods:
startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter, final OneginiMobileAuthenticationError oneginiMobileAuthenticationError)
the app receives new mobile authentication request. First parameter isOneginiMobileAuthenticationRequest
object which contains message you should present to the end user and- 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 is anAuthenticationAttemptCounter
that holds information about failed, remaining and max PIN attempts. The last parameter is an optional error, that will be non-null in case when fallback to PIN was triggered by an error. 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 biometrics
The third type of mobile authentication request is Push with biometrics. In this type the user also have a possibility to accept or deny request. The Push with
biometrics requires the user to scan his fingerprint in order to successfully accept the mobile authentication request. To support Push with biometrics type
you need to implement the OneginiMobileAuthWithPushBiometricRequestHandler
interface and set its instance during SDK initialization step by calling
the setMobileAuthWithPushBiometricRequestHandler(OneginiMobileAuthWithPushBiometricRequestHandler mobileAuthenticationBiometricRequestHandler)
method on
the OneginiClientBuilder
. You also need to handle all interface methods in order to show/hide biometric prompt 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 OneginiMobileAuthWithPushBiometricRequestHandler
You need to implement the startAuthentication
and finishAuthentication
methods:
startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, BiometricPrompt.CryptoObject cryptoObject, final OneginiBiometricCallback callback)
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 isBiometricPrompt.CryptoObject
which has to be used when showing the prompt to the user to properly authenticate on backend. Third parameter is a callback object you should use to handle authentication process.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.startAuthentication(final UserProfile userProfile, BiometricPrompt.CryptoObject cryptoObject, final OneginiBiometricCallback callback)
triggered when a new fingerprint authentication request is made, providing anUserProfile
object, biometric crypto object that has to be authenticated and a biometric callback;
Push with custom authenticator
The fourth type of mobile authentication request is Push with custom authenticator. In this type the user also have a possibility to accept or deny request.
In order to authenticate the push message, user has to perform authentication using custom authenticator. To support push with custom authenticator you first
need to implement custom authentication as described in Authenticate user with custom authenticator topic guide. After that you need to
implement
the OneginiMobileAuthWithPushCustomRequestHandler
interface and set its instance during SDK initialization step by calling the
public OneginiClientBuilder setMobileAuthWithPushCustomRequestHandler(final OneginiMobileAuthWithPushCustomRequestHandler mobileAuthenticationCustomRequestHandler)
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 OneginiMobileAuthWithPushCustomRequestHandler
startAuthentication(final OneginiMobileAuthenticationRequest oneginiMobileAuthenticationRequest, final OneginiCustomCallback oneginiCustomCallback)
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 use the custom authenticator but use PIN instead.finishAuthentication()
is called after end of processing the user response to the request.
Error scenarios
When push with a specific authenticator is received, the SDK tries to use the exact authenticator that was selected. However, during the authentication
attempt some errors can occur. Those errors can be authenticator specific or not. For example if mobile authentication with fingerprint was requested, but user
fails to provide the correct fingerprint several times, such authenticator error will trigger a fallback to pin authenticator. In such case the SDK
will start new mobile authentication with PIN, like described above, while the last param of the startAuthentication()
method will contain
a non-null error object containing the error details. That's the opposite to a regular PIN authentication request (a request that was started by incoming
mobile authentication with PIN) when the error object is null.
In case of errors that are not authenticator-specific, the SDK will not trigger PIN authentication, but it will return the error immediately on the
OneginiMobileAuthenticationHandler#onError()
. For example, if mobile authentication with custom authenticator was received, but user has lost his internet
connection, the SDK will return this error to the app without triggering the fallback to pin.