User interaction typically consists of two steps: the session confirmation and the input of the second factor. Note that, depending on the configuration, the current state of the app and specifics for a given session, the session confirmation or second factor step might be skipped.
After starting a session with
startUserInteraction(), the Mobile SDK will either trigger a ConfirmLogin or ConfirmEnrol callback.
Upon receiving ConfirmLogin, the app should display a dialog to the user, asking for confirmation. When multiple accounts exist for a server (and no account was pre-selected at the server side), the user should also choose which account it wants to login with.
The server can specify
announceInfo for the login, which can contain a logo and an arbitrary set of strings, each identified by a unique key. The app can access this data, by requesting the session's
After confirming and selecting an account,
setAccount() should be called with the selected account. Optionally, a
userInput string can be provided, which can contain arbitrary data to be sent back to the nextAuth server. If
userInput was sent, it will be available through the nextAuth Server SDK.
NextAuth.getSessionManager().setAccount(session, account, userInput);
NextAuth.default.setAccount(account, for: session, with: userInput)
account in the
setAccount(session, account) (Android) or
setAccount(account, for: session) (iOS) functions, should be one of the accounts from the list you get back in the ConfirmLogin callback. Do not use the account inside the session (that you also get back in the ConfirmLogin callback), since there will be no account linked to that session prior to this call.
Upon receiving ConfirmEnrol, the app should display a dialog to the user, asking for confirmation that the user wants to create a new account. When one or more accounts already exist for that server, one can optionally display additional information to the user, warning about existing accounts. The ConfirmEnrol contains a list of all accounts that already exist inside the mobile SDK for that server. Next to this, the session contains
enrolInfo (starting from Android SDK 1.6.0 and iOS SDK 1.10.0, in combination with nextAuth server 2.4.0 or higher) which provides additional information about this enrolment from the nextAuth server: the display name for this account to be enrolled and the accounts that already exist on the server for the user that is now enrolling.
The ConfirmEnrol callback also contains a visual hash of the server parameters (including the public key), which can be displayed to the user for visual confirmation of the server identity. This can be used as an alternative to public key pinning, when the public key is not known in advance.
When you do not intend to display the visual hash of the server parameters, disabling the visual hash in the configuration will result in a speedup of 100-300 ms.
After the user confirms the creation of a new account,
setAccount() should be called.
NextAuth.default.setAccount(nil, for: session)
Do not use the
setAccount(session, account) (Android) or
setAccount(account, for: session) (iOS) functions, even if you get a list of accounts back from the Mobile SDK. The list of accounts that you get back in the ConfirmEnrol callback are the accounts that already exist at that server. As you are about to create a new account, you should never set an existing account for this session!
The user is requested to enter a second factor (PIN or biometric). A SecondFactor callback can be received multiple times, for example, if the user fails to input the correct PIN code. There are specific scenarios depending on the already registered second factor.
You can send the PIN, biometrics or both at the same time to the Mobile SDK by invoking one of the following methods.
NextAuth.getSecondFactorManager().sfInput(pin); NextAuth.getSecondFactorManager().sfInput(cryptoObject); NextAuth.getSecondFactorManager().sfInput(pin, cryptoObject);
NextAuth.default.sfInput(pin) NextAuth.default.sfInput(info) NextAuth.default.sfInput(pin, and: info)
After sending a PIN, biometric or both to the Mobile SDK for verification, you will receive another SecondFactor callback where the
secondFactorInfo contains the
result and whether or not retrying is allowed.
stopped = true, the nextAuth Mobile SDK has stopped the current user interaction flow, and will no longer listen for
sfCancel(). The app is therefore expected to dismiss the second factor input interface.
Second Factor Info⚓︎
the main information that you will need to build the second factor input UI is represented by the following properties of the
context property of the secondFactorInfo object indicates the action which triggered the second factor interaction:
VERIFY: the user is asked to verify theirself by entering the second factor;
FIRST_SET: the user is asked to set their second factor;
BIOMETRICS_ADD: if the user wants to enable biometrics as a second factor, see Second Factor Management for more details;
BIOMETRICS_REMOVE: if the user wants to disable biometrics as a second factor, see Second Factor Management for more details.
PIN_CHANGE_VERIFY: if the user wants to change their PIN to verify the current PIN or biometric, see Second Factor Management for more details;
PIN_CHANGE_SET: if the user wants to change their PIN to set a new PIN, see Second Factor Management for more details.
FIRST_SET or a
PIN_CHANGE_SET, you should ask the user to enter their chosen PIN twice and verify that both match before sending the entered code to the Mobile SDK.
When adding a biometric, the user will be first asked to confirm their biometric through a biometric prompt.
You need to show this biometric prompt on receiving a SecondFactor callback with a
BIOMETRICS_ADD context (and
showBiometrics = true).
This is handled from the nextAuth mobile SDK (depending on the OS version, the user will be shown a biometric prompt or not).
After confirming their biometric, you will receive (another) SecondFactor callback with a
BIOMETRICS_ADD context (and
showBiometrics = false), to have the user authenticate their intent of adding biometrics as a second factor.
Result and Retry⚓︎
After sending a PIN or biometric to the Mobile SDK for verification, you will receive an updated
result property will contain the result of the second factor verification:
- it's default state is
SUCCESS: the second factor was verified successfully with the backend;
ERROR: something went wrong, while verifying with the backend.
Next to the result, there is a boolean
retry that indicates whether or not the user is allowed to retry. When
false, you will no longer be able to pass a second factor input to the mobile SDK;
As long as you do not send a PIN and/or biometric as input to a second factor flow, the
result contained in the SecondFactor callback will be
null. When this is the case, the value of the boolean
retry should not be interpreted, the user has still to perform their first try.
Blocked, Max Attempts, Failed Attempts and Penalised For⚓︎
The secondFactorInfo contains the following information towards the user inputting their PIN: whether they are blocked from entering a PIN (
blocked), the total number of tries the user has for entering a correct PIN (
maxAttempts) and the number of times it tried already (
failedAttempts). In case the user used up one or more tries, they should be notified in order to reduce the risk of users blocking themselves accidentally.
When a PIN policy is implemented at the SFS, the integer
penalisedFor will tell you how many seconds the user needs to wait before they can try to enter a PIN again (starting from Android SDK 1.7.0 and iOS SDK 2.0.0-beta1, in combination with nextAuth server 2.5.0 or higher). Note that our Mobile SDK will not block you from calling
sfInput(pin) within the penalty period, instead you will receive another SecondFactor callback with an updated
When the user successfully authenticates using a biometric, the number of tries (the blocked status and penalty, if any) for the PIN will be reset, and the user will no longer be blocked from entering their PIN.
Maximally avoid attackers from learning the user's PIN:
- implement your own PIN pad (do not use the standard numeric keyboard);
- disable screenshots or touch event logging while the user enters their PIN;
- use the
PINclass provided by the Mobile SDK for your platform (i.e.,
PinContaineron Android, and
PINon iOS). These classes have been designed to prevent that (partial) copies of the PIN remain in memory any longer than strictly necessarily. Additionally, they also implement utility functionality to check if the PIN is easy to guess.
secondFactorInfo also informs you whether or not the user should be requested to present a biometric feature:
showBiometrics. The Mobile SDK determines that biometrics should be shown, if all of the following are true:
- the device has support for biometrics that are considered sufficiently strong by the OS1;
- the user has enrolled at least one biometric feature;
- if a biometricsDisabled is configured, it is set to
- the session is not an enrolment (for creating a new account, the user's PIN is always required);
- when maxNumberSecondFactorWithoutPIN and/or maxTimeSecondFactorWithoutPIN have not been reached, if they are configured.
Avoid that users forget their PIN by asking them to input their PIN every once in a while. For example, the app could be configured to ask the user for their PIN after 30 subsequent biometric verifications or after 30 days since the last time the user entered their PIN.
Always present a biometric prompt to the user whenever biometrics are requested and allow user to authenticate using PIN as a secondary option.
If set, the included
session object can be used for displaying additional information, e.g.
enrolInfo, to the user.
Only second factor flows that are linked to a session (e.g. while logging in or enrolling) contain the
session object. Second factor flows that are not linked to a session (see Second Factor Management), i.e., enabling/disabling biometrics and changing the PIN, do not contain a
Android device vendors sometimes provide biometrics that, even though these can be used to unlock the phone, are not considered to be sufficiently strong with respect to the standards that are imposed by the Android OS. This is most notably the case for many face and iris recognition implementations. ↩