Authenticate user with PIN
Determine if we can login user
The UserClient
contains the getUserProfiles
method which returns set of all registered UserProfile value objects. You can check size of that set to
determine if there is need to register new user or if there is possibility to login.
If the method isRegisteredAtLeastOneUser
from the example below will return false
you can assume that no user is authenticated on the device. In that case
user have to register before logging in.
Example code
| private boolean isRegisteredAtLeastOneUser() {
final Set<UserProfile> userProfiles = OneginiSDK.getOneginiClient(this).getUserClient().getUserProfiles();
return userProfiles.size() > 0;
}
|
You can also use UserClient#isUserRegistered()
method in order to check if specified user is registered on the device.
Login registered user
When at least one user has already registered there is possibility to log in that user using authenticateUser
method from the UserClient
. This method
requires two arguments:
UserProfile
the UserProfile ValueObject that we want to authenticate,
OneginiAuthenticationHandler
the authentication handler to return the authentication result to.
The result of authentication is an access token with optionally a refresh token. The authentication handler contains:
- an
onSuccess
method which lets you know that authentication was finished successfully - at this point, you can request data on behalf of the user,
- an
onError
method which is called in every other case.
You can find more information about authentication in user authentication section.
PIN request handlers
At this point the app would crash with a NullPointerException
due to the lack of PIN request handlers for creating and verifying a PIN. To prevent that you
need to provide your own authentication request handlers using the OneginiClientBuilder
methods. The OneginiCreatePinRequestHandler
and
OneginiPinAuthenticationRequestHandler
are required in order to perform authentication flows with a PIN.
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | public static OneginiClient getOneginiClient(final Context context) {
OneginiClient oneginiClient = OneginiClient.getInstance();
if (oneginiClient == null) {
final Context applicationContext = context.getApplicationContext();
final RegistrationRequestHandler registrationRequestHandler = new RegistrationRequestHandler(applicationContext);
final CreatePinRequestHandler createPinRequestHandler = new CreatePinRequestHandler(applicationContext);
final PinAuthenticationRequestHandler pinAuthenticationRequestHandler = new PinAuthenticationRequestHandler(applicationContext);
// will throw OneginiConfigNotFoundException if OneginiConfigModel class can't be found
oneginiClient = new OneginiClientBuilder(applicationContext, createPinRequestHandler, pinAuthenticationRequestHandler)
// add a browser registration handler
.setBrowserRegistrationRequestHandler(registrationRequestHandler)
.build();
}
return oneginiClient;
}
|
Create PIN request handler
The OneginiCreatePinRequestHandler
interface is responsible for handling the PIN creation process. Create a class that implements this interface and overrides
the following methods:
startPinCreation(final UserProfile userProfile, final OneginiPinCallback oneginiPinCallback, final int pinLength)
- this method will be invoked by the SDK whenever
there will be a need to create a new PIN (during registration, or during the change pin action). You have to call
OneginiPinCallback#acceptAuthenticationRequest(pinEnteredByUser)
in order to successfully finish PIN creation process. The pinLength
parameter determines
the required pin length. In order to cancel the PIN creation process you can call the OneginiPinCallback#denyAuthenticationRequest()
method.
onNextPinCreationAttempt(final OneginiPinValidationError oneginiPinValidationError)
- this method will be called when the PIN provided by user hasn't met
the PIN policy. You can check the exact error by comparing the OneginiPinValidationError#getErrorType()
value with the OneginiPinValidationError
's static
fields. Please note that there are also the OneginiPinValidationError#getMessage()
and OneginiPinValidationError#getCause()
methods which can
return more information about the error.
finishPinCreation()
is called whenever correct PIN was created or the action was canceled by the user.
Below is sample code for creating a custom PIN request handler. In this example we created an additional PinWithConfirmationHandler
to support the PIN
verification step. You can feel free to skip this step if it's not relevant for your use-case. You can check the full solution in our Example App which is
available on GitHub.
Example code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 | public class CreatePinRequestHandler implements OneginiCreatePinRequestHandler {
public static PinWithConfirmationHandler oneginiPinCallback;
private final Context context;
public CreatePinRequestHandler(final Context context) {
this.context = context;
}
@Override
public void startPinCreation(final UserProfile userProfile, final OneginiPinCallback oneginiPinCallback, final int pinLength) {
PinActivity.setIsCreatePinFlow(true);
notifyActivity(context.getString(R.string.pin_title_choose_pin), "");
CreatePinRequestHandler.oneginiPinCallback = new PinWithConfirmationHandler(oneginiPinCallback);
}
@Override
public void onNextPinCreationAttempt(final OneginiPinValidationError oneginiPinValidationError) {
handlePinValidationError(oneginiPinValidationError);
}
@Override
public void finishPinCreation() {
Toast.makeText(context, "CreatePinRequestHandler#finishPinCreation", Toast.LENGTH_LONG).show();
}
/**
* Extended pin handler, used to create PIN verification step
*/
public class PinWithConfirmationHandler {
private final OneginiPinCallback originalHandler;
private char[] pin;
public PinWithConfirmationHandler(final OneginiPinCallback originalHandler) {
this.originalHandler = originalHandler;
}
public void onPinProvided(final char[] pin) {
if (isPinSet()) {
secondPinProvided(pin);
} else {
firstPinProvided(pin);
}
}
private void firstPinProvided(final char[] pin) {
OneginiSDK.getOneginiClient(context).getUserClient().validatePinWithPolicy(pin, new OneginiPinValidationHandler() {
@Override
public void onSuccess() {
PinWithConfirmationHandler.this.pin = pin;
notifyActivity(context.getString(R.string.pin_title_verify_pin), "");
}
@Override
public void onError(final OneginiPinValidationError oneginiPinValidationError) {
handlePinValidationError(oneginiPinValidationError);
}
});
}
public void secondPinProvided(final char[] pin) {
final boolean pinsEqual = Arrays.equals(this.pin, pin);
nullifyPinArray();
if (pinsEqual) {
originalHandler.acceptAuthenticationRequest(pin);
} else {
notifyActivity(context.getString(R.string.pin_title_choose_pin), context.getString(R.string.pin_error_not_equal));
}
}
public void pinCancelled(){
nullifyPinArray();
originalHandler.denyAuthenticationRequest();
}
private boolean isPinSet() {
return pin != null;
}
private void nullifyPinArray() {
if (isPinSet()) {
final int arraySize = pin.length;
for (int i = 0; i < arraySize; i++) {
pin[i] = '\0';
}
pin = null;
}
}
}
private void handlePinValidationError(final OneginiPinValidationError oneginiPinValidationError) {
@OneginiPinValidationError.PinValidationErrorType int errorType = oneginiPinValidationError.getErrorType();
switch (errorType) {
case OneginiPinValidationError.WRONG_PIN_LENGTH:
notifyActivity(context.getString(R.string.pin_title_choose_pin), context.getString(R.string.pin_error_invalid_length));
break;
case OneginiPinValidationError.PIN_BLACKLISTED:
notifyActivity(context.getString(R.string.pin_title_choose_pin), context.getString(R.string.pin_error_blacklisted));
break;
case OneginiPinValidationError.PIN_IS_A_SEQUENCE:
notifyActivity(context.getString(R.string.pin_title_choose_pin), context.getString(R.string.pin_error_sequence));
break;
case OneginiPinValidationError.PIN_USES_SIMILAR_DIGITS:
notifyActivity(context.getString(R.string.pin_title_choose_pin), context.getString(R.string.pin_error_similar));
break;
case OneginiPinValidationError.DEVICE_DEREGISTERED:
new DeregistrationUtil(context).onDeviceDeregistered();
case OneginiPinValidationError.GENERAL_ERROR:
default:
notifyActivity(context.getString(R.string.pin_title_choose_pin), oneginiPinValidationError.getMessage());
break;
}
}
private void notifyActivity(final String title, final String message) {
final Intent intent = new Intent(context, PinActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(PinActivity.EXTRA_TITLE, title);
intent.putExtra(PinActivity.EXTRA_MESSAGE, message);
context.startActivity(intent);
}
}
|
For more info on error handling see the error handling topic guide.
PIN authentication request handler
Create a class that implements the OneginiPinAuthenticationRequestHandler
interface, which is responsible for handling PIN authentication requests, and
override the following methods:
startAuthentication(final UserProfile userProfile, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter)
- this
method will be invoked by the SDK whenever there will be a to need authenticate the user. You have to call
OneginiPinCallback#acceptAuthenticationRequest(pinEnteredByUser)
in order to successfully finish the PIN creation process or OneginiPinCallback#denyAuthenticationRequest()
to cancel it.
onNextAuthenticationAttempt(final AuthenticationAttemptCounter attemptCounter)
- this method is called when the PIN that is provided by the user is incorrect,
but his failed attempts limit hasn't been reached yet. The method's parameter is AuthenticationAttemptCounter
object providing information about number of
failed attempts, remaining attempts and maximum attempts counters.
finishAuthentication()
- this method is called whenever the authentication process is finished regardless whether it was successful or failed.
Example code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | public class PinAuthenticationRequestHandler implements OneginiPinAuthenticationRequestHandler {
public static OneginiPinCallback oneginiPinCallback;
private static UserProfile userProfile;
private final Context context;
private final UserStorage userStorage;
public PinAuthenticationRequestHandler(final Context context) {
this.context = context;
userStorage = new UserStorage(context);
}
@Override
public void startAuthentication(final UserProfile userProfile, final OneginiPinCallback oneginiPinCallback, final AuthenticationAttemptCounter attemptCounter) {
PinAuthenticationRequestHandler.userProfile = userProfile;
PinAuthenticationRequestHandler.oneginiPinCallback = oneginiPinCallback;
PinActivity.setIsCreatePinFlow(false);
startPinActivity(userProfile);
}
@Override
public void onNextAuthenticationAttempt(final AuthenticationAttemptCounter attemptCounter) {
PinActivity.setRemainingFailedAttempts(attemptCounter.getRemainingAttempts());
startPinActivity(userProfile);
}
@Override
public void finishAuthentication() {
PinActivity.setRemainingFailedAttempts(0);
}
private void startPinActivity(final UserProfile userProfile) {
final Intent intent = new Intent(context, PinActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(PinActivity.EXTRA_TITLE, context.getString(R.string.pin_title_enter_pin));
final User user = userStorage.loadUser(userProfile);
intent.putExtra(PinActivity.EXTRA_USER_NAME, user.getName());
context.startActivity(intent);
}
}
|
For more info on error handling see the error handling topic guide.