diff --git a/src/main/java/com/auth0/client/auth/AuthAPI.java b/src/main/java/com/auth0/client/auth/AuthAPI.java index d0797dab..3f5378c3 100644 --- a/src/main/java/com/auth0/client/auth/AuthAPI.java +++ b/src/main/java/com/auth0/client/auth/AuthAPI.java @@ -38,6 +38,7 @@ public class AuthAPI { private static final String KEY_REFRESH_TOKEN = "refresh_token"; private static final String KEY_OTP = "otp"; private static final String KEY_REALM = "realm"; + private static final String KEY_MFA_TOKEN = "mfa_token"; private static final String PATH_OAUTH = "oauth"; private static final String PATH_TOKEN = "token"; @@ -835,4 +836,45 @@ public CustomRequest startPasswordlessSmsFlow(String ph request.addParameter("phone_number", phoneNumber); return request; } + + /** + * Creates a request to exchange the mfa token and one-time password (OTP) to authenticate a user with an MFA OTP Authenticator. + * + *
+     * {@code
+     * AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne");
+     * try {
+     *      TokenHolder result = auth.exchangeMfaOtp("the-mfa-token”, new char[]{‘a','n','o','t',’p’})
+     *          .execute();
+     * } catch (Auth0Exception e) {
+     *      //Something happened
+     * }
+     * }
+     * 
+ * + * @param mfaToken the mfa_token received from the mfa_required error that occurred during login. Must not be null. + * @param otp the OTP Code provided by the user. Must not be null. + * + * @return a Request to configure and execute. + * + * @see Verify with one-time password (OTP) API documentation + */ + public AuthRequest exchangeMfaOtp(String mfaToken, char[] otp) { + Asserts.assertNotNull(mfaToken, "mfa token"); + Asserts.assertNotNull(otp, "otp"); + + String url = baseUrl + .newBuilder() + .addPathSegment(PATH_OAUTH) + .addPathSegment(PATH_TOKEN) + .build() + .toString(); + TokenRequest request = new TokenRequest(client, url); + request.addParameter(KEY_CLIENT_ID, clientId); + request.addParameter(KEY_CLIENT_SECRET, clientSecret); + request.addParameter(KEY_GRANT_TYPE, "http://auth0.com/oauth/grant-type/mfa-otp"); + request.addParameter(KEY_MFA_TOKEN, mfaToken); + request.addParameter(KEY_OTP, otp); + return request; + } } diff --git a/src/test/java/com/auth0/client/auth/AuthAPITest.java b/src/test/java/com/auth0/client/auth/AuthAPITest.java index 397ee757..098f34ad 100644 --- a/src/test/java/com/auth0/client/auth/AuthAPITest.java +++ b/src/test/java/com/auth0/client/auth/AuthAPITest.java @@ -1169,4 +1169,47 @@ public void shouldCreateRenewAuthRequest() throws Exception { assertThat(response.getTokenType(), not(isEmptyOrNullString())); assertThat(response.getExpiresIn(), is(notNullValue())); } + + // MFA grant + + @Test + public void shouldThrowWhenExchangeMfaOtpCalledWithNullMfaToken() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'mfa token' cannot be null!"); + api.exchangeMfaOtp(null, new char[]{'o','t','p'}); + } + + @Test + public void shouldThrowWhenExchangeMfaOtpCalledWithNullOtp() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'otp' cannot be null!"); + api.exchangeMfaOtp("mfaToken", null); + } + + @Test + public void shouldCreateExchangeMfaOtpRequest() throws Exception { + AuthRequest request = api.exchangeMfaOtp("mfaToken", new char[]{'o','t','p'}); + assertThat(request, is(notNullValue())); + + server.jsonResponse(AUTH_TOKENS, 200); + TokenHolder response = request.execute(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath("POST", "/oauth/token")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + + Map body = bodyFromRequest(recordedRequest); + assertThat(body, hasEntry("grant_type", (Object) "http://auth0.com/oauth/grant-type/mfa-otp")); + assertThat(body, hasEntry("client_id", (Object) CLIENT_ID)); + assertThat(body, hasEntry("client_secret", (Object) CLIENT_SECRET)); + assertThat(body, hasEntry("mfa_token", (Object) "mfaToken")); + assertThat(body, hasEntry("otp", (Object) "otp")); + + assertThat(response, is(notNullValue())); + assertThat(response.getAccessToken(), not(isEmptyOrNullString())); + assertThat(response.getIdToken(), not(isEmptyOrNullString())); + assertThat(response.getRefreshToken(), not(isEmptyOrNullString())); + assertThat(response.getTokenType(), not(isEmptyOrNullString())); + assertThat(response.getExpiresIn(), is(notNullValue())); + } }