Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [PATCH] Translate MFA token error to UIRequiredException instead of ServiceException (#2538)

Version 18.2.2
----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import com.microsoft.identity.common.java.logging.Logger;
import com.microsoft.identity.common.java.net.HttpResponse;
import com.microsoft.identity.common.java.providers.microsoft.MicrosoftAuthorizationErrorResponse;
import com.microsoft.identity.common.java.providers.microsoft.MicrosoftTokenErrorResponse;
import com.microsoft.identity.common.java.providers.oauth2.AuthorizationErrorResponse;
import com.microsoft.identity.common.java.providers.oauth2.AuthorizationResult;
import com.microsoft.identity.common.java.providers.oauth2.TokenErrorResponse;
Expand Down Expand Up @@ -224,33 +223,7 @@ public static ServiceException getExceptionFromTokenErrorResponse(@NonNull final

final ServiceException outErr;

if (isNativeAuthenticationMFAError(errorResponse)) {
ServiceException apiError = new ServiceException(
errorResponse.getError(),
errorResponse.getErrorDescription(),
null);

String developerDescription = "Multi-factor authentication is required, which can't be fulfilled as part of this flow. Please sign out and perform a new sign in operation. Please see exception details for more information.";
outErr = new ServiceException(
errorResponse.getError(),
developerDescription,
apiError
);
} else if (isNativeAuthenticationResetPasswordRequiredError(errorResponse)) {
ServiceException apiError = new ServiceException(
errorResponse.getError(),
errorResponse.getErrorDescription(),
null);

String developerDescription = "User password change is required, which can't be fulfilled as part of this flow."+
"Please reset the password and perform a new sign in operation. Please see exception details for more information.";
outErr = new ServiceException(
errorResponse.getError(),
developerDescription,
apiError
);
}
else if (shouldBeConvertedToUiRequiredException(errorResponse.getError())) {
if (shouldBeConvertedToUiRequiredException(errorResponse.getError())) {
outErr = new UiRequiredException(
errorResponse.getError(),
errorResponse.getErrorDescription());
Expand All @@ -275,10 +248,17 @@ else if (shouldBeConvertedToUiRequiredException(errorResponse.getError())) {
*/
public static ServiceException convertToNativeAuthException(@NonNull final ServiceException exception) {
final ServiceException outErr;
String customDescription = "";
if (isNativeAuthenticationMFAException(exception)) {
customDescription = "Multi-factor authentication is required, which can't be fulfilled as part of this flow. Please sign out and perform a new sign in operation. Please see exception details for more information.";
} else if (isNativeAuthenticationResetPasswordRequiredException(exception)) {
customDescription = "User password change is required, which can't be fulfilled as part of this flow."+
"Please reset the password and perform a new sign in operation. Please see exception details for more information.";
}

outErr = new ServiceException(
exception.getErrorCode(),
exception.getMessage(),
customDescription + exception.getMessage(),
exception.getHttpStatusCode(),
exception
);
Expand Down Expand Up @@ -514,40 +494,34 @@ private static boolean isIntunePolicyRequiredError(

/**
* Identifies whether an error is specific to native authentication MFA scenarios.
* @param errorResponse
* @param exception A ServiceException from which we will check the error code
* @return true if errorResponse is a native authentication MFA error
*/
private static boolean isNativeAuthenticationMFAError(
@NonNull final TokenErrorResponse errorResponse) {
return doesErrorContainsErrorCode(50076, errorResponse);
private static boolean isNativeAuthenticationMFAException(
@NonNull final ServiceException exception) {
return doesExceptionContainsErrorCode(50076, exception);
}

/**
* Identifies whether an error is specific to native authentication reset password required scenarios.
* @param errorResponse
* @param exception A ServiceException from which we will check the error code
* @return true if errorResponse is a native authentication reset password required error
*/
private static boolean isNativeAuthenticationResetPasswordRequiredError(
@NonNull final TokenErrorResponse errorResponse) {
return doesErrorContainsErrorCode(50142, errorResponse);
private static boolean isNativeAuthenticationResetPasswordRequiredException(
@NonNull final ServiceException exception) {
return doesExceptionContainsErrorCode(50142, exception);
}

/**
* Identifies whether an error contains a specific error code.
* @param errorCode Error code to check
* @param errorResponse A TokenErrorResponse from which we will check the error code
* @param exception A ServiceException from which we will check the error code
* @return true if errorResponse contains the error code
*/
private static boolean doesErrorContainsErrorCode(
private static boolean doesExceptionContainsErrorCode(
int errorCode,
@NonNull final TokenErrorResponse errorResponse) {
if (!(errorResponse instanceof MicrosoftTokenErrorResponse)) {
return false;
}

MicrosoftTokenErrorResponse microsoftTokenErrorResponse = ((MicrosoftTokenErrorResponse) errorResponse);
return microsoftTokenErrorResponse.getErrorCodes() != null &&
!microsoftTokenErrorResponse.getErrorCodes().isEmpty() &&
microsoftTokenErrorResponse.getErrorCodes().contains((long) errorCode);
@NonNull final ServiceException exception) {
return exception.getCliTelemErrorCode() != null &&
exception.getCliTelemErrorCode().equalsIgnoreCase(String.valueOf(errorCode));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@

import com.microsoft.identity.common.java.exception.BaseException;
import com.microsoft.identity.common.java.exception.ClientException;
import com.microsoft.identity.common.java.exception.ServiceException;
import com.microsoft.identity.common.java.exception.TerminalException;
import com.microsoft.identity.common.java.exception.UiRequiredException;
import com.microsoft.identity.common.java.providers.microsoft.MicrosoftTokenErrorResponse;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeoutException;

@RunWith(JUnit4.class)
Expand All @@ -45,6 +50,38 @@ public void testBaseExceptionFromException_TerminalException() throws Exception{
Assert.assertEquals(e.getCause(), t);
}

@Test
public void testMFATokenErrorResponse_IsTranslatedToUIRequiredException() {
final MicrosoftTokenErrorResponse tokenErrorResponse = new MicrosoftTokenErrorResponse();
tokenErrorResponse.setError("invalid_grant");
tokenErrorResponse.setErrorDescription("AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a new location, you must use multi-factor authentication to access '7ae46e1'. Trace ID: 01276277-3a30020d900900 Correlation ID: 6209e18a-f89b-4f14-a05e-0371c6757adb Timestamp: 2024-11-14 13:09:18Z");
tokenErrorResponse.setErrorCodes(new ArrayList<Long>(Arrays.asList(50076L)));
tokenErrorResponse.setSubError("basic_action");

BaseException e = ExceptionAdapter.getExceptionFromTokenErrorResponse(tokenErrorResponse);
Assert.assertTrue("Expected exception of UiRequiredException type", e instanceof UiRequiredException);
Assert.assertEquals(e.getErrorCode(), tokenErrorResponse.getError());
Assert.assertEquals(e.getMessage(), tokenErrorResponse.getErrorDescription());
}

@Test
public void testNativeAuthMFAException_ContainsCorrectDescription() {
String description = "description";
ServiceException outErr = new ServiceException("errorCode", description, null);
outErr.setCliTelemErrorCode("50076");
ServiceException result = ExceptionAdapter.convertToNativeAuthException(outErr);
Assert.assertEquals("Multi-factor authentication is required, which can't be fulfilled as part of this flow. Please sign out and perform a new sign in operation. Please see exception details for more information." + description, result.getMessage());
}

@Test
public void testNativeAuthResetPasswordRequiredException_ContainsCorrectDescription() {
String description = "description";
ServiceException outErr = new ServiceException("errorCode", description, null);
outErr.setCliTelemErrorCode("50142");
ServiceException result = ExceptionAdapter.convertToNativeAuthException(outErr);
Assert.assertEquals("User password change is required, which can't be fulfilled as part of this flow.Please reset the password and perform a new sign in operation. Please see exception details for more information." + description, result.getMessage());
}

@Test
public void testClientExceptionFromException_TimeoutException() {
final TimeoutException t = new TimeoutException();
Expand Down