Skip to content

Commit

Permalink
enable MFA support for oidc conformant clients
Browse files Browse the repository at this point in the history
  • Loading branch information
lbalmaceda committed Feb 20, 2018
1 parent 75bad67 commit 1603fbd
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 13 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ allprojects {
group = 'com.auth0.android'

repositories {
mavenLocal()
jcenter()
}
}
2 changes: 1 addition & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ dependencies {
compile 'com.android.support:design:25.3.1'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.squareup:otto:1.3.8'
compile 'com.auth0.android:auth0:1.11.0'
compile 'com.auth0.android:auth0:1.12.1-SNAPSHOT'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.robolectric:robolectric:3.1.2'
Expand Down
30 changes: 23 additions & 7 deletions lib/src/main/java/com/auth0/android/lock/LockActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public class LockActivity extends AppCompatActivity implements ActivityCompat.On
private static final int WEB_AUTH_REQUEST_CODE = 200;
private static final int CUSTOM_AUTH_REQUEST_CODE = 201;
private static final int PERMISSION_REQUEST_CODE = 202;
public static final String KEY_MFA_TOKEN = "mfa_token";

private ApplicationFetcher applicationFetcher;
private Configuration configuration;
Expand Down Expand Up @@ -377,13 +378,20 @@ public void onDatabaseAuthenticationRequest(DatabaseLoginEvent event) {
lockView.showProgress(true);
lastDatabaseLogin = event;
AuthenticationAPIClient apiClient = options.getAuthenticationAPIClient();
final HashMap<String, Object> parameters = new HashMap<>(options.getAuthenticationParameters());
if (event.getVerificationCode() != null) {
parameters.put(KEY_VERIFICATION_CODE, event.getVerificationCode());
AuthenticationRequest request;
HashMap<String, Object> parameters = new HashMap<>(options.getAuthenticationParameters());
if (TextUtils.isEmpty(event.getMFAToken())) {
String connection = configuration.getDatabaseConnection().getName();
request = apiClient.login(event.getUsernameOrEmail(), event.getPassword(), connection);
if (!TextUtils.isEmpty(event.getVerificationCode())) {
parameters.put(KEY_VERIFICATION_CODE, event.getVerificationCode());
}
} else {
//noinspection ConstantConditions / OTP code is present along with MFA token
request = apiClient.loginWithOTP(event.getMFAToken(), event.getVerificationCode());
}
final String connection = configuration.getDatabaseConnection().getName();
AuthenticationRequest request = apiClient.login(event.getUsernameOrEmail(), event.getPassword(), connection)
.addAuthenticationParameters(parameters);

request.addAuthenticationParameters(parameters);
if (options.getScope() != null) {
request.setScope(options.getScope());
}
Expand Down Expand Up @@ -512,12 +520,20 @@ public void run() {
lockView.showProgress(false);

final AuthenticationError authError = loginErrorBuilder.buildFrom(error);
if (error.isMultifactorRequired() || error.isMultifactorEnrollRequired()) {
if (error.isMultifactorRequired()) {
String mfaToken = (String) error.getValue(KEY_MFA_TOKEN);
if (!TextUtils.isEmpty(mfaToken)) {
lastDatabaseLogin.setMFAToken(mfaToken);
}
lockView.showMFACodeForm(lastDatabaseLogin);
return;
}
String message = authError.getMessage(LockActivity.this);
showErrorMessage(message);
if (error.isMultifactorTokenExpired()) {
//The MFA Token has expired. The user needs to log in again. Show the username/password form
onBackPressed();
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class LoginErrorMessageBuilder implements ErrorMessageBuilder<Authenticat
private static final int userExistsResource = R.string.com_auth0_lock_db_signup_user_already_exists_error_message;
private static final int unauthorizedResource = R.string.com_auth0_lock_db_login_error_unauthorized_message;
private static final int invalidMFACodeResource = R.string.com_auth0_lock_db_login_error_invalid_mfa_code_message;
private static final int mfaEnrollRequiredResource = R.string.com_auth0_lock_db_login_error_mfa_enroll_required;
private static final int tooManyAttemptsResource = R.string.com_auth0_lock_db_too_many_attempts_error_message;

private int invalidCredentialsResource;
Expand All @@ -63,8 +64,10 @@ public AuthenticationError buildFrom(AuthenticationException exception) {

if (exception.isInvalidCredentials()) {
messageRes = invalidCredentialsResource;
} else if (exception.isMultifactorCodeInvalid()) {
} else if (exception.isMultifactorCodeInvalid() || exception.isMultifactorTokenExpired()) {
messageRes = invalidMFACodeResource;
} else if (exception.isMultifactorEnrollRequired()) {
messageRes = mfaEnrollRequiredResource;
} else if (USER_EXISTS_ERROR.equals(exception.getCode()) || USERNAME_EXISTS_ERROR.equals(exception.getCode())) {
messageRes = userExistsResource;
} else if (exception.isRuleError()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class DatabaseLoginEvent extends DatabaseEvent {

private String password;
private String verificationCode;
private String mfaToken;

public DatabaseLoginEvent(@NonNull String usernameOrEmail, @NonNull String password) {
super(usernameOrEmail);
Expand All @@ -56,4 +57,13 @@ public void setVerificationCode(@NonNull String code) {
public String getVerificationCode() {
return verificationCode;
}

public void setMFAToken(@NonNull String mfaToken) {
this.mfaToken = mfaToken;
}

@Nullable
public String getMFAToken() {
return mfaToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ public void showCustomFieldsForm(DatabaseSignUpEvent event) {
}

public void showMFACodeForm(DatabaseLoginEvent event) {
MFACodeFormView form = new MFACodeFormView(this, event.getUsernameOrEmail(), event.getPassword());
MFACodeFormView form = new MFACodeFormView(this, event.getUsernameOrEmail(), event.getPassword(), event.getMFAToken());
updateHeaderTitle(R.string.com_auth0_lock_title_mfa_input_code);
addSubForm(form);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

package com.auth0.android.lock.views;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;

Expand All @@ -38,16 +38,18 @@ public class MFACodeFormView extends FormView implements TextView.OnEditorAction

private final String usernameOrEmail;
private final String password;
private final String mfaToken;

private LockWidget lockWidget;
private ValidatedInputView codeInput;


public MFACodeFormView(LockWidget lockWidget, String usernameOrEmail, String password) {
public MFACodeFormView(@NonNull LockWidget lockWidget, @Nullable String usernameOrEmail, @Nullable String password, @Nullable String mfaToken) {
super(lockWidget.getContext());
this.lockWidget = lockWidget;
this.usernameOrEmail = usernameOrEmail;
this.password = password;
this.mfaToken = mfaToken;
init();
}

Expand All @@ -62,6 +64,7 @@ private void init() {
public Object getActionEvent() {
DatabaseLoginEvent event = new DatabaseLoginEvent(usernameOrEmail, password);
event.setVerificationCode(getInputText());
event.setMFAToken(mfaToken);
return event;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
<string name="com_auth0_lock_enterprise_no_connection_message">THERE WAS NO CONNECTION CONFIGURED FOR THE DOMAIN</string>
<string name="com_auth0_lock_db_login_error_message">THERE WAS AN ERROR PROCESSING THE SIGN IN</string>
<string name="com_auth0_lock_db_login_error_invalid_credentials_message">WRONG EMAIL OR PASSWORD</string>
<string name="com_auth0_lock_db_login_error_mfa_enroll_required">THE USER HAS NOT ENROLLED MULTIFACTOR AUTHENTICATION</string>
<string name="com_auth0_lock_db_login_error_invalid_mfa_code_message">THE CODE IS INVALID OR HAS EXPIRED</string>
<string name="com_auth0_lock_db_login_error_unauthorized_message">USER IS BLOCKED</string>
<string name="com_auth0_lock_db_sign_up_error_message">THERE WAS AN ERROR PROCESSING THE SIGN UP</string>
<string name="com_auth0_lock_db_change_password_message_success">WE\'VE JUST SENT YOU AN EMAIL TO RESET YOUR PASSWORD</string>
Expand All @@ -142,7 +144,6 @@
<string name="com_auth0_lock_passwordless_code_request_error_message">THERE WAS AN ERROR SENDING THE CODE</string>
<string name="com_auth0_lock_passwordless_link_request_error_message">THERE WAS AN ERROR SENDING THE LINK</string>
<string name="com_auth0_lock_passwordless_login_error_invalid_credentials_message">WRONG IDENTITY OR PASSCODE</string>
<string name="com_auth0_lock_db_login_error_invalid_mfa_code_message">THE CODE IS INVALID OR HAS EXPIRED</string>
<string name="com_auth0_lock_title_mfa_input_code">Two Step Verification</string>
<string name="com_auth0_lock_description_mfa_input_code">Please enter a verification code from\n your code generator application.</string>

Expand Down
36 changes: 36 additions & 0 deletions lib/src/test/java/com/auth0/android/lock/LockActivityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,42 @@ public void shouldCallLegacyDatabaseLoginWithVerificationCode() throws Exception
assertThat(reqParams, is(notNullValue()));
assertThat(reqParams, hasEntry("extra", "value"));
assertThat(reqParams, hasEntry("mfa_code", "123456"));
assertThat(reqParams, not(hasKey("mfa_token")));
assertThat(reqParams, not(hasKey("otp")));
}

@Test
public void shouldCallOIDCDatabaseLoginWithOTPCodeAndMFAToken() throws Exception {
Auth0 account = new Auth0("cliendId", "domain");
account.setOIDCConformant(true);
Options options = mock(Options.class);
when(options.getAccount()).thenReturn(account);
when(options.getAuthenticationAPIClient()).thenReturn(client);
when(options.getScope()).thenReturn("openid user photos");
when(options.getAudience()).thenReturn("aud");
when(options.getAuthenticationParameters()).thenReturn(basicParameters);
LockActivity activity = new LockActivity(configuration, options, lockView, webProvider);

DatabaseLoginEvent event = new DatabaseLoginEvent("username", "password");
event.setVerificationCode("123456");
event.setMFAToken("mfaToken");
activity.onDatabaseAuthenticationRequest(event);

verify(lockView).showProgress(true);
verify(options).getAuthenticationAPIClient();
verify(client).login("username", "password", "connection");
verify(authRequest).addAuthenticationParameters(mapCaptor.capture());
verify(authRequest).start(any(BaseCallback.class));
verify(authRequest).setScope("openid user photos");
verify(authRequest, never()).setAudience("aud");
verify(configuration, atLeastOnce()).getDatabaseConnection();

Map<String, String> reqParams = mapCaptor.getValue();
assertThat(reqParams, is(notNullValue()));
assertThat(reqParams, hasEntry("extra", "value"));
assertThat(reqParams, hasEntry("otp", "123456"));
assertThat(reqParams, hasEntry("mfa_token", "mfaToken"));
assertThat(reqParams, not(hasKey("mfa_code")));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,15 @@ public void shouldSetVerificationCode() throws Exception {
event.setVerificationCode("code");
assertThat(event.getVerificationCode(), is("code"));
}

@Test
public void shouldNotHaveMFAToken() throws Exception {
assertThat(event.getMFAToken(), is(nullValue()));
}

@Test
public void shouldSetMFAToken() throws Exception {
event.setMFAToken("mfatoken");
assertThat(event.getMFAToken(), is("mfatoken"));
}
}

0 comments on commit 1603fbd

Please sign in to comment.