diff --git a/msal/src/main/java/com/microsoft/identity/client/Logger.java b/msal/src/main/java/com/microsoft/identity/client/Logger.java index 6e13690995..83455eaab2 100644 --- a/msal/src/main/java/com/microsoft/identity/client/Logger.java +++ b/msal/src/main/java/com/microsoft/identity/client/Logger.java @@ -175,6 +175,11 @@ public void log(String tag, com.microsoft.identity.common.internal.logging.Logge mExternalLogger = externalLogger; } + public synchronized void removeExternalLogger() { + mExternalLogger = null; + } + + /** * Enable/Disable the Android logcat logging. By default, the sdk enables it. * diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt index 5af29796c4..11e4e334e3 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt @@ -60,7 +60,9 @@ import com.microsoft.identity.common.nativeauth.internal.commands.ResetPasswordS import com.microsoft.identity.common.nativeauth.internal.commands.SignInStartCommand import com.microsoft.identity.common.nativeauth.internal.commands.SignUpStartCommand import com.microsoft.identity.common.nativeauth.internal.controllers.NativeAuthMsalController +import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication.Companion.pcaScope import com.microsoft.identity.nativeauth.statemachine.errors.ErrorTypes +import com.microsoft.identity.nativeauth.statemachine.errors.GetAccountError import com.microsoft.identity.nativeauth.statemachine.errors.ResetPasswordError import com.microsoft.identity.nativeauth.statemachine.errors.SignInError import com.microsoft.identity.nativeauth.statemachine.errors.SignInErrorTypes @@ -245,17 +247,26 @@ class NativeAuthPublicClientApplication( */ override suspend fun getCurrentAccount(): GetAccountResult { return withContext(Dispatchers.IO) { - val account = getCurrentAccountInternal(nativeAuthConfig) - return@withContext if (account != null) { - GetAccountResult.AccountFound( - resultValue = AccountState.createFromAccountResult( - account = account, - correlationId = DiagnosticContext.INSTANCE.threadCorrelationId, - config = nativeAuthConfig + try { + val account = getCurrentAccountInternal(nativeAuthConfig) + return@withContext if (account != null) { + GetAccountResult.AccountFound( + resultValue = AccountState.createFromAccountResult( + account = account, + correlationId = DiagnosticContext.INSTANCE.threadCorrelationId, + config = nativeAuthConfig + ) ) + } else { + GetAccountResult.NoAccountFound + } + } catch (e: Exception) { + GetAccountError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in getCurrentAccount.", + exception = e, + correlationId = DiagnosticContext.INSTANCE.threadCorrelationId ) - } else { - GetAccountResult.NoAccountFound } } } @@ -270,7 +281,6 @@ class NativeAuthPublicClientApplication( * @param scopes (Optional) list of scopes to request. * @param callback [com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication.SignInCallback] to receive the result. * @return [com.microsoft.identity.nativeauth.statemachine.results.SignInResult] see detailed possible return state under the object. - * @throws MsalClientException if an account is already signed in. */ override fun signIn( username: String, @@ -314,29 +324,29 @@ class NativeAuthPublicClientApplication( methodName = "${TAG}.signIn" ) return withContext(Dispatchers.IO) { + try { - verifyNoUserIsSignedIn() + verifyNoUserIsSignedIn() - if (username.isBlank()) { - return@withContext SignInError( - errorType = ErrorTypes.INVALID_USERNAME, - errorMessage = "Empty or blank username", - correlationId = "UNSET" - ) - } + if (username.isBlank()) { + return@withContext SignInError( + errorType = ErrorTypes.INVALID_USERNAME, + errorMessage = "Empty or blank username", + correlationId = "UNSET" + ) + } - val hasPassword = password?.isNotEmpty() == true + val hasPassword = password?.isNotEmpty() == true - val params = - CommandParametersAdapter.createSignInStartCommandParameters( - nativeAuthConfig, - nativeAuthConfig.oAuth2TokenCache, - username, - password, - scopes - ) + val params = + CommandParametersAdapter.createSignInStartCommandParameters( + nativeAuthConfig, + nativeAuthConfig.oAuth2TokenCache, + username, + password, + scopes + ) - try { val command = SignInStartCommand( params, NativeAuthMsalController(), @@ -345,135 +355,151 @@ class NativeAuthPublicClientApplication( val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - return@withContext when (val result = - rawCommandResult.checkAndWrapCommandResultType()) { - is SignInCommandResult.Complete -> { - if (hasPassword) { - val authenticationResult = - AuthenticationResultAdapter.adapt(result.authenticationResult) - - SignInResult.Complete( - resultValue = AccountState.createFromAuthenticationResult( - authenticationResult = authenticationResult, - correlationId = result.correlationId, - config = nativeAuthConfig + try { + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignInCommandResult.Complete -> { + if (hasPassword) { + val authenticationResult = + AuthenticationResultAdapter.adapt(result.authenticationResult) + + SignInResult.Complete( + resultValue = AccountState.createFromAuthenticationResult( + authenticationResult = authenticationResult, + correlationId = result.correlationId, + config = nativeAuthConfig + ) ) - ) - } else { - Logger.warnWithObject( - TAG, - "Sign in received unexpected result: ", - result - ) - SignInError( - errorMessage = "unexpected state", - error = ErrorTypes.INVALID_STATE, - correlationId = result.correlationId - ) + } else { + Logger.warnWithObject( + TAG, + "Sign in received unexpected result: ", + result + ) + SignInError( + errorMessage = "unexpected state", + error = ErrorTypes.INVALID_STATE, + correlationId = result.correlationId + ) + } } - } - is SignInCommandResult.CodeRequired -> { - Logger.warn( - TAG, - result.correlationId, - "Server requires a code" - ) - SignInResult.CodeRequired( - nextState = SignInCodeRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - scopes = scopes, - config = nativeAuthConfig - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel - ) - } - is INativeAuthCommandResult.InvalidUsername -> { - SignInError( - errorType = ErrorTypes.INVALID_USERNAME, - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - errorCodes = result.errorCodes - ) - } - is SignInCommandResult.PasswordRequired -> { - if (hasPassword) { - Logger.warnWithObject( + + is SignInCommandResult.CodeRequired -> { + Logger.warn( TAG, - "Sign in using password received unexpected result: ", - result + result.correlationId, + "Server requires a code" ) - SignInError( - errorMessage = "unexpected state", - error = ErrorTypes.INVALID_STATE, - correlationId = result.correlationId - ) - } else { - SignInResult.PasswordRequired( - nextState = SignInPasswordRequiredState( + SignInResult.CodeRequired( + nextState = SignInCodeRequiredState( continuationToken = result.continuationToken, correlationId = result.correlationId, scopes = scopes, config = nativeAuthConfig - ) + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel ) } - } - is SignInCommandResult.UserNotFound -> { - SignInError( - errorType = ErrorTypes.USER_NOT_FOUND, - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - errorCodes = result.errorCodes - ) - } - is SignInCommandResult.InvalidCredentials -> { - if (hasPassword) { + + is INativeAuthCommandResult.InvalidUsername -> { SignInError( - errorType = SignInErrorTypes.INVALID_CREDENTIALS, + errorType = ErrorTypes.INVALID_USERNAME, errorMessage = result.errorDescription, error = result.error, correlationId = result.correlationId, errorCodes = result.errorCodes ) - } else { - Logger.warnWithObject( - TAG, - "Sign in received Unexpected result: ", - result - ) + } + + is SignInCommandResult.PasswordRequired -> { + if (hasPassword) { + Logger.warnWithObject( + TAG, + "Sign in using password received unexpected result: ", + result + ) + SignInError( + errorMessage = "unexpected state", + error = ErrorTypes.INVALID_STATE, + correlationId = result.correlationId + ) + } else { + SignInResult.PasswordRequired( + nextState = SignInPasswordRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + scopes = scopes, + config = nativeAuthConfig + ) + ) + } + } + + is SignInCommandResult.UserNotFound -> { SignInError( - errorMessage = "unexpected state", - error = ErrorTypes.INVALID_STATE, + errorType = ErrorTypes.USER_NOT_FOUND, + errorMessage = result.errorDescription, + error = result.error, correlationId = result.correlationId, errorCodes = result.errorCodes ) } + + is SignInCommandResult.InvalidCredentials -> { + if (hasPassword) { + SignInError( + errorType = SignInErrorTypes.INVALID_CREDENTIALS, + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + errorCodes = result.errorCodes + ) + } else { + Logger.warnWithObject( + TAG, + "Sign in received Unexpected result: ", + result + ) + SignInError( + errorMessage = "unexpected state", + error = ErrorTypes.INVALID_STATE, + correlationId = result.correlationId, + errorCodes = result.errorCodes + ) + } + } + + is INativeAuthCommandResult.Redirect -> { + SignInError( + errorType = ErrorTypes.BROWSER_REQUIRED, + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.UnknownError -> { + SignInError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + errorCodes = result.errorCodes, + exception = result.exception + ) + } } - is INativeAuthCommandResult.Redirect -> { - SignInError( - errorType = ErrorTypes.BROWSER_REQUIRED, - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId - ) - } - is INativeAuthCommandResult.UnknownError -> { - SignInError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - errorCodes = result.errorCodes, - exception = result.exception - ) - } + } finally { + StringUtil.overwriteWithNull(params.password) } - } finally { - StringUtil.overwriteWithNull(params.password) + } catch (e: Exception) { + SignInError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in signIn.", + exception = e, + correlationId = "UNSET" + ) } } } @@ -489,7 +515,6 @@ class NativeAuthPublicClientApplication( * @param attributes (Optional) user attributes to be used during account creation * @param callback [com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication.SignUpCallback] to receive the result. * @return [com.microsoft.identity.nativeauth.statemachine.results.SignUpResult] see detailed possible return state under the object. - * @throws MsalClientException if an account is already signed in. */ override fun signUp( username: String, @@ -520,7 +545,6 @@ class NativeAuthPublicClientApplication( * @param password (Optional) password of the account to sign up. * @param attributes (Optional) user attributes to be used during account creation * @return [com.microsoft.identity.nativeauth.statemachine.results.SignUpResult] see detailed possible return state under the object. - * @throws MsalClientException if an account is already signed in. */ override suspend fun signUp( username: String, @@ -535,180 +559,189 @@ class NativeAuthPublicClientApplication( var hasPassword = password?.isNotEmpty() == true return withContext(Dispatchers.IO) { - val doesAccountExist = checkForPersistedAccount().get() - if (doesAccountExist) { - throw MsalClientException( - MsalClientException.INVALID_PARAMETER, - "An account is already signed in." - ) - } + try { + val doesAccountExist = checkForPersistedAccount().get() + if (doesAccountExist) { + throw MsalClientException( + MsalClientException.INVALID_PARAMETER, + "An account is already signed in." + ) + } - if (username.isBlank()) { - return@withContext SignUpError( - errorType = ErrorTypes.INVALID_USERNAME, - errorMessage = "Empty or blank username", - correlationId = "UNSET" - ) - } + if (username.isBlank()) { + return@withContext SignUpError( + errorType = ErrorTypes.INVALID_USERNAME, + errorMessage = "Empty or blank username", + correlationId = "UNSET" + ) + } - val parameters = - CommandParametersAdapter.createSignUpStartCommandParameters( - nativeAuthConfig, - nativeAuthConfig.oAuth2TokenCache, - username, - password, - attributes?.toMap() - ) + val parameters = + CommandParametersAdapter.createSignUpStartCommandParameters( + nativeAuthConfig, + nativeAuthConfig.oAuth2TokenCache, + username, + password, + attributes?.toMap() + ) - val command = SignUpStartCommand( - parameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_UP_START - ) + val command = SignUpStartCommand( + parameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_UP_START + ) - try { val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - return@withContext when (val result = - rawCommandResult.checkAndWrapCommandResultType()) { - is SignUpCommandResult.Complete -> { - SignUpResult.Complete( - nextState = SignInContinuationState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = nativeAuthConfig + try { + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignUpCommandResult.Complete -> { + SignUpResult.Complete( + nextState = SignInContinuationState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = nativeAuthConfig + ) ) - ) - } - - is SignUpCommandResult.AttributesRequired -> { - SignUpResult.AttributesRequired( - nextState = SignUpAttributesRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = nativeAuthConfig - ), - requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() - ) - } - - is SignUpCommandResult.CodeRequired -> { - SignUpResult.CodeRequired( - nextState = SignUpCodeRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = nativeAuthConfig - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel, - ) - } + } - is SignUpCommandResult.PasswordRequired -> { - if (hasPassword) { - Logger.warnWithObject( - TAG, - "Sign up using password received unexpected result: ", - result - ) - SignUpError( - errorMessage = "Unexpected state", - error = ErrorTypes.INVALID_STATE, - correlationId = result.correlationId + is SignUpCommandResult.AttributesRequired -> { + SignUpResult.AttributesRequired( + nextState = SignUpAttributesRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = nativeAuthConfig + ), + requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() ) - } else { - SignUpResult.PasswordRequired( - nextState = SignUpPasswordRequiredState( + } + + is SignUpCommandResult.CodeRequired -> { + SignUpResult.CodeRequired( + nextState = SignUpCodeRequiredState( continuationToken = result.continuationToken, correlationId = result.correlationId, username = username, config = nativeAuthConfig - ) + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel, ) } - } - is SignUpCommandResult.AuthNotSupported -> { - SignUpError( - errorType = SignUpErrorTypes.AUTH_NOT_SUPPORTED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is SignUpCommandResult.PasswordRequired -> { + if (hasPassword) { + Logger.warnWithObject( + TAG, + "Sign up using password received unexpected result: ", + result + ) + SignUpError( + errorMessage = "Unexpected state", + error = ErrorTypes.INVALID_STATE, + correlationId = result.correlationId + ) + } else { + SignUpResult.PasswordRequired( + nextState = SignUpPasswordRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = nativeAuthConfig + ) + ) + } + } - is SignUpCommandResult.InvalidPassword -> { - if (hasPassword) { + is SignUpCommandResult.AuthNotSupported -> { SignUpError( - errorType = ErrorTypes.INVALID_PASSWORD, + errorType = SignUpErrorTypes.AUTH_NOT_SUPPORTED, error = result.error, errorMessage = result.errorDescription, correlationId = result.correlationId ) - } else { - Logger.warnWithObject( - TAG, - "Sign up received unexpected result: ", - result - ) + } + + is SignUpCommandResult.InvalidPassword -> { + if (hasPassword) { + SignUpError( + errorType = ErrorTypes.INVALID_PASSWORD, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } else { + Logger.warnWithObject( + TAG, + "Sign up received unexpected result: ", + result + ) + SignUpError( + error = ErrorTypes.INVALID_STATE, + errorMessage = "Unexpected state", + correlationId = result.correlationId, + ) + } + } + + is SignUpCommandResult.UsernameAlreadyExists -> { SignUpError( - error = ErrorTypes.INVALID_STATE, - errorMessage = "Unexpected state", - correlationId = result.correlationId, + errorType = SignUpErrorTypes.USER_ALREADY_EXISTS, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId ) } - } - - is SignUpCommandResult.UsernameAlreadyExists -> { - SignUpError( - errorType = SignUpErrorTypes.USER_ALREADY_EXISTS, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - is INativeAuthCommandResult.InvalidUsername -> { - SignUpError( - errorType = ErrorTypes.INVALID_USERNAME, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is INativeAuthCommandResult.InvalidUsername -> { + SignUpError( + errorType = ErrorTypes.INVALID_USERNAME, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - is SignUpCommandResult.InvalidAttributes -> { - SignUpError( - errorType = SignUpErrorTypes.INVALID_ATTRIBUTES, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is SignUpCommandResult.InvalidAttributes -> { + SignUpError( + errorType = SignUpErrorTypes.INVALID_ATTRIBUTES, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - is INativeAuthCommandResult.Redirect -> { - SignUpError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is INativeAuthCommandResult.Redirect -> { + SignUpError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - is INativeAuthCommandResult.UnknownError -> { - SignUpError( - errorMessage = "Unexpected state", - error = ErrorTypes.INVALID_STATE, - correlationId = result.correlationId - ) + is INativeAuthCommandResult.UnknownError -> { + SignUpError( + errorMessage = "Unexpected state", + error = ErrorTypes.INVALID_STATE, + correlationId = result.correlationId + ) + } } + } finally { + StringUtil.overwriteWithNull(parameters.password) } - } finally { - StringUtil.overwriteWithNull(parameters.password) + } catch (e: Exception) { + SignUpError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in signIn.", + exception = e, + correlationId = "UNSET" + ) } } } @@ -721,7 +754,6 @@ class NativeAuthPublicClientApplication( * @param username username of the account to reset password. * @param callback [com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication.ResetPasswordCallback] to receive the result. * @return [com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordStartResult] see detailed possible return state under the object. - * @throws MsalClientException if an account is already signed in. */ override fun resetPassword(username: String, callback: ResetPasswordCallback) { LogSession.logMethodCall( @@ -745,7 +777,6 @@ class NativeAuthPublicClientApplication( * * @param username username of the account to reset password. * @return [com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordStartResult] see detailed possible return state under the object. - * @throws MsalClientException if an account is already signed in. */ override suspend fun resetPassword(username: String): ResetPasswordStartResult { LogSession.logMethodCall( @@ -753,119 +784,128 @@ class NativeAuthPublicClientApplication( correlationId = null, methodName = "${TAG}.resetPassword(username: String)" ) - - return withContext(Dispatchers.IO) { - val doesAccountExist = checkForPersistedAccount().get() - if (doesAccountExist) { - throw MsalClientException( - MsalClientException.INVALID_PARAMETER, - "An account is already signed in." - ) - } - - if (username.isBlank()) { - return@withContext ResetPasswordError( - errorType = ErrorTypes.INVALID_USERNAME, - errorMessage = "Empty or blank username", - correlationId = "UNSET" - ) - } - - val parameters = CommandParametersAdapter.createResetPasswordStartCommandParameters( - nativeAuthConfig, - nativeAuthConfig.oAuth2TokenCache, - username - ) - - val command = ResetPasswordStartCommand( - parameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_RESET_PASSWORD_START - ) - - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is ResetPasswordCommandResult.CodeRequired -> { - ResetPasswordStartResult.CodeRequired( - nextState = ResetPasswordCodeRequiredState( - continuationToken = result.continuationToken, - username = username, - correlationId = result.correlationId, - config = nativeAuthConfig - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel - ) - } - - is ResetPasswordCommandResult.UserNotFound -> { - ResetPasswordError( - errorType = ErrorTypes.USER_NOT_FOUND, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - is INativeAuthCommandResult.InvalidUsername -> { - ResetPasswordError( - errorType = ErrorTypes.INVALID_USERNAME, - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - errorCodes = result.errorCodes - ) - } - - is INativeAuthCommandResult.UnknownError -> { - ResetPasswordError( - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - exception = result.exception - ) - } - - is INativeAuthCommandResult.Redirect -> { - ResetPasswordError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - is ResetPasswordCommandResult.PasswordNotSet -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Reset password received unexpected result: ", - result - ) - ResetPasswordError( - error = ErrorTypes.INVALID_STATE, - errorMessage = "Unexpected state", - correlationId = result.correlationId, - ) - } - - is ResetPasswordCommandResult.EmailNotVerified -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Reset password received unexpected result: ", - result - ) - ResetPasswordError( - error = ErrorTypes.INVALID_STATE, - errorMessage = "Unexpected state", - correlationId = result.correlationId, - ) - } - } - } + try { + return withContext(Dispatchers.IO) { + val doesAccountExist = checkForPersistedAccount().get() + if (doesAccountExist) { + throw MsalClientException( + MsalClientException.INVALID_PARAMETER, + "An account is already signed in." + ) + } + + if (username.isBlank()) { + return@withContext ResetPasswordError( + errorType = ErrorTypes.INVALID_USERNAME, + errorMessage = "Empty or blank username", + correlationId = "UNSET" + ) + } + + val parameters = CommandParametersAdapter.createResetPasswordStartCommandParameters( + nativeAuthConfig, + nativeAuthConfig.oAuth2TokenCache, + username + ) + + val command = ResetPasswordStartCommand( + parameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_RESET_PASSWORD_START + ) + + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is ResetPasswordCommandResult.CodeRequired -> { + ResetPasswordStartResult.CodeRequired( + nextState = ResetPasswordCodeRequiredState( + continuationToken = result.continuationToken, + username = username, + correlationId = result.correlationId, + config = nativeAuthConfig + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel + ) + } + + is ResetPasswordCommandResult.UserNotFound -> { + ResetPasswordError( + errorType = ErrorTypes.USER_NOT_FOUND, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.InvalidUsername -> { + ResetPasswordError( + errorType = ErrorTypes.INVALID_USERNAME, + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + errorCodes = result.errorCodes + ) + } + + is INativeAuthCommandResult.UnknownError -> { + ResetPasswordError( + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId, + exception = result.exception + ) + } + + is INativeAuthCommandResult.Redirect -> { + ResetPasswordError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is ResetPasswordCommandResult.PasswordNotSet -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Reset password received unexpected result: ", + result + ) + ResetPasswordError( + error = ErrorTypes.INVALID_STATE, + errorMessage = "Unexpected state", + correlationId = result.correlationId, + ) + } + + is ResetPasswordCommandResult.EmailNotVerified -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Reset password received unexpected result: ", + result + ) + ResetPasswordError( + error = ErrorTypes.INVALID_STATE, + errorMessage = "Unexpected state", + correlationId = result.correlationId, + ) + } + } + } + } catch (e: Exception) { + return ResetPasswordError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in resetPassword.", + exception = e, + correlationId = "UNSET" + ) + } } private fun verifyNoUserIsSignedIn() { @@ -890,7 +930,7 @@ class NativeAuthPublicClientApplication( methodName = "${TAG}.checkForPersistedAccount" ) val future = ResultFuture() - getCurrentAccount(object : GetCurrentAccountCallback { + getCurrentAccount(object : NativeAuthPublicClientApplication.GetCurrentAccountCallback { override fun onResult(result: GetAccountResult) { future.setResult(result is GetAccountResult.AccountFound) } diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt index 6cc234ac5f..6c0c4bd003 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt @@ -23,13 +23,18 @@ package com.microsoft.identity.nativeauth.statemachine.errors +import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult +import com.microsoft.identity.nativeauth.statemachine.results.GetAccountResult import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResendCodeResult import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResult import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordStartResult import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitCodeResult +import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitPasswordResult import com.microsoft.identity.nativeauth.statemachine.results.SignInResendCodeResult import com.microsoft.identity.nativeauth.statemachine.results.SignInResult import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitCodeResult +import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitPasswordResult +import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult import com.microsoft.identity.nativeauth.statemachine.results.SignUpSubmitAttributesResult @@ -77,6 +82,8 @@ internal class ErrorTypes { * in state transitions. If this occurs, the flow should be restarted. */ const val INVALID_STATE = "invalid_state" + + const val CLIENT_EXCEPTION = "client_exception" } } @@ -157,3 +164,21 @@ class ResendCodeError( override val errorCodes: List? = null, override var exception: Exception? = null ): SignInResendCodeResult, SignUpResendCodeResult, ResetPasswordResendCodeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) + +class GetAccountError( + override val errorType: String? = null, + override val error: String? = null, + override val errorMessage: String?, + override val correlationId: String, + override val errorCodes: List? = null, + override var exception: Exception? = null +): GetAccountResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) + +class SignOutError( + override val errorType: String? = null, + override val error: String? = null, + override val errorMessage: String?, + override val correlationId: String, + override val errorCodes: List? = null, + override var exception: Exception? = null +): SignOutResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception) \ No newline at end of file diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt index 8f34321a20..8530765c6d 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt @@ -50,8 +50,10 @@ import com.microsoft.identity.common.nativeauth.internal.commands.AcquireTokenNo import com.microsoft.identity.common.nativeauth.internal.controllers.NativeAuthMsalController import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration +import com.microsoft.identity.nativeauth.statemachine.errors.ErrorTypes import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenError import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenErrorTypes +import com.microsoft.identity.nativeauth.statemachine.errors.SignOutError import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult import com.microsoft.identity.nativeauth.utils.serializable @@ -103,62 +105,73 @@ class AccountState private constructor( */ suspend fun signOut(): SignOutResult { return withContext(Dispatchers.IO) { - LogSession.logMethodCall( - tag = TAG, - correlationId = null, - methodName = "$TAG.signOut.withContext" - ) - - val account: IAccount = - NativeAuthPublicClientApplication.getCurrentAccountInternal(config) - ?: throw MsalClientException( - MsalClientException.NO_CURRENT_ACCOUNT, - MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE - ) - - val requestAccountRecord = AccountRecord() - requestAccountRecord.environment = (account as Account).environment - requestAccountRecord.homeAccountId = account.homeAccountId - - val params = CommandParametersAdapter.createRemoveAccountCommandParameters( - config, - config.oAuth2TokenCache, - requestAccountRecord - ) - - val removeCurrentAccountCommandParameters = RemoveCurrentAccountCommand( - params, - LocalMSALController().asControllerFactory(), - object : CommandCallback { - override fun onError(error: BaseException?) { - // Do nothing, handled by CommandDispatcher.submitSilentReturningFuture() - } - - override fun onTaskCompleted(result: Boolean?) { - // Do nothing, handled by CommandDispatcher.submitSilentReturningFuture() - } + try { + LogSession.logMethodCall( + tag = TAG, + correlationId = null, + methodName = "$TAG.signOut.withContext" + ) - override fun onCancel() { - // Do nothing - } - }, - PublicApiId.NATIVE_AUTH_ACCOUNT_SIGN_OUT - ) + val account: IAccount = + NativeAuthPublicClientApplication.getCurrentAccountInternal(config) + ?: throw MsalClientException( + MsalClientException.NO_CURRENT_ACCOUNT, + MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE + ) + + val requestAccountRecord = AccountRecord() + requestAccountRecord.environment = (account as Account).environment + requestAccountRecord.homeAccountId = account.homeAccountId + + val params = CommandParametersAdapter.createRemoveAccountCommandParameters( + config, + config.oAuth2TokenCache, + requestAccountRecord + ) - val result = CommandDispatcher.submitSilentReturningFuture(removeCurrentAccountCommandParameters) - .get().result as Boolean + val removeCurrentAccountCommandParameters = RemoveCurrentAccountCommand( + params, + LocalMSALController().asControllerFactory(), + object : CommandCallback { + override fun onError(error: BaseException?) { + // Do nothing, handled by CommandDispatcher.submitSilentReturningFuture() + } + + override fun onTaskCompleted(result: Boolean?) { + // Do nothing, handled by CommandDispatcher.submitSilentReturningFuture() + } + + override fun onCancel() { + // Do nothing + } + }, + PublicApiId.NATIVE_AUTH_ACCOUNT_SIGN_OUT + ) - return@withContext if (result) { - SignOutResult.Complete - } else { - Logger.error( - TAG, - "Unexpected error during signOut.", - null + val result = CommandDispatcher.submitSilentReturningFuture( + removeCurrentAccountCommandParameters ) - throw MsalClientException( - MsalClientException.UNKNOWN_ERROR, - "Unexpected error during signOut." + .get().result as Boolean + + return@withContext if (result) { + SignOutResult.Complete + } else { + Logger.error( + TAG, + "Unexpected error during signOut.", + null + ) + throw MsalClientException( + MsalClientException.UNKNOWN_ERROR, + "Unexpected error during signOut." + ) + } + } catch (e: Exception) { + SignOutError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in signOut.", + exception = e, + correlationId = correlationId ) } } @@ -233,62 +246,75 @@ class AccountState private constructor( methodName = "$TAG.getAccessToken(forceRefresh: Boolean)" ) return withContext(Dispatchers.IO) { - val currentAccount = - NativeAuthPublicClientApplication.getCurrentAccountInternal(config) as? Account - ?: return@withContext GetAccessTokenError( - errorType = GetAccessTokenErrorTypes.NO_ACCOUNT_FOUND, - error = MsalClientException.NO_CURRENT_ACCOUNT, - errorMessage = MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE, - correlationId = "UNSET" - ) + try { + val currentAccount = + NativeAuthPublicClientApplication.getCurrentAccountInternal(config) as? Account + ?: return@withContext GetAccessTokenError( + errorType = GetAccessTokenErrorTypes.NO_ACCOUNT_FOUND, + error = MsalClientException.NO_CURRENT_ACCOUNT, + errorMessage = MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE, + correlationId = "UNSET" + ) + + val acquireTokenSilentParameters = AcquireTokenSilentParameters.Builder() + .forAccount(currentAccount) + .fromAuthority(currentAccount.authority) + .build() + + val accountToBeUsed = PublicClientApplication.selectAccountRecordForTokenRequest( + config, + acquireTokenSilentParameters + ) - val acquireTokenSilentParameters = AcquireTokenSilentParameters.Builder() - .forAccount(currentAccount) - .fromAuthority(currentAccount.authority) - .build() + val params = + CommandParametersAdapter.createAcquireTokenNoFixedScopesCommandParameters( + config, + config.oAuth2TokenCache, + accountToBeUsed, + forceRefresh, + correlationId + ) - val accountToBeUsed = PublicClientApplication.selectAccountRecordForTokenRequest( - config, - acquireTokenSilentParameters - ) + val command = AcquireTokenNoFixedScopesCommand( + params, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_ACCOUNT_GET_ACCESS_TOKEN + ) - val params = CommandParametersAdapter.createAcquireTokenNoFixedScopesCommandParameters( - config, - config.oAuth2TokenCache, - accountToBeUsed, - forceRefresh, - correlationId - ) + val commandResult = CommandDispatcher.submitSilentReturningFuture(command) + .get().result - val command = AcquireTokenNoFixedScopesCommand( - params, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_ACCOUNT_GET_ACCESS_TOKEN - ) + return@withContext when (commandResult) { + is ServiceException -> { + GetAccessTokenError( + exception = ExceptionAdapter.convertToNativeAuthException(commandResult), + correlationId = "UNSET" + ) + } - val commandResult = CommandDispatcher.submitSilentReturningFuture(command) - .get().result + is Exception -> { + GetAccessTokenError( + exception = commandResult, + correlationId = "UNSET" + ) + } - return@withContext when (commandResult) { - is ServiceException -> { - GetAccessTokenError( - exception = ExceptionAdapter.convertToNativeAuthException(commandResult), - correlationId = "UNSET" - ) - } - is Exception -> { - GetAccessTokenError( - exception = commandResult, - correlationId = "UNSET" - ) - } - else -> { - // Account and Id token data could change after access token refresh, update the account object in the state - account = AuthenticationResultAdapter.adapt(commandResult as ILocalAuthenticationResult).account - GetAccessTokenResult.Complete( - resultValue = AuthenticationResultAdapter.adapt(commandResult) - ) + else -> { + // Account and Id token data could change after access token refresh, update the account object in the state + account = + AuthenticationResultAdapter.adapt(commandResult as ILocalAuthenticationResult).account + GetAccessTokenResult.Complete( + resultValue = AuthenticationResultAdapter.adapt(commandResult) + ) + } } + } catch (e: Exception) { + GetAccessTokenError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in getAccessToken.", + exception = e, + correlationId = correlationId + ) } } } diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt index d085c13ccb..c4eb87f055 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt @@ -57,6 +57,7 @@ import com.microsoft.identity.nativeauth.utils.serializable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.lang.Exception /** * Native Auth uses a state machine to denote state of and transitions within a flow. @@ -119,69 +120,80 @@ class ResetPasswordCodeRequiredState internal constructor( correlationId = correlationId, methodName = "${TAG}.submitCode(code: String)" ) + return withContext(Dispatchers.IO) { - val parameters = - CommandParametersAdapter.createResetPasswordSubmitCodeCommandParameters( - config, - config.oAuth2TokenCache, - code, - correlationId, - continuationToken + try { + val parameters = + CommandParametersAdapter.createResetPasswordSubmitCodeCommandParameters( + config, + config.oAuth2TokenCache, + code, + correlationId, + continuationToken + ) + + val command = ResetPasswordSubmitCodeCommand( + parameters = parameters, + controller = NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_RESET_PASSWORD_SUBMIT_CODE ) - val command = ResetPasswordSubmitCodeCommand( - parameters = parameters, - controller = NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_RESET_PASSWORD_SUBMIT_CODE - ) + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is ResetPasswordCommandResult.PasswordRequired -> { + ResetPasswordSubmitCodeResult.PasswordRequired( + nextState = ResetPasswordPasswordRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ) + ) + } - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is ResetPasswordCommandResult.PasswordRequired -> { - ResetPasswordSubmitCodeResult.PasswordRequired( - nextState = ResetPasswordPasswordRequiredState( - continuationToken = result.continuationToken, + is ResetPasswordCommandResult.IncorrectCode -> { + SubmitCodeError( + errorType = ErrorTypes.INVALID_CODE, + error = result.error, + errorMessage = result.errorDescription, correlationId = result.correlationId, - username = username, - config = config + subError = result.subError ) - ) - } - - is ResetPasswordCommandResult.IncorrectCode -> { - SubmitCodeError( - errorType = ErrorTypes.INVALID_CODE, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - subError = result.subError - ) - } + } - is INativeAuthCommandResult.Redirect -> { - SubmitCodeError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is INativeAuthCommandResult.Redirect -> { + SubmitCodeError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit code received unexpected result: ", - result - ) - SubmitCodeError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - exception = result.exception - ) + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit code received unexpected result: ", + result + ) + SubmitCodeError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + exception = result.exception + ) + } } + } catch (e: Exception) { + SubmitCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -226,53 +238,63 @@ class ResetPasswordCodeRequiredState internal constructor( methodName = "${TAG}.resendCode" ) return withContext(Dispatchers.IO) { - val parameters = - CommandParametersAdapter.createResetPasswordResendCodeCommandParameters( - config, - config.oAuth2TokenCache, - correlationId, - continuationToken - ) + try { + val parameters = + CommandParametersAdapter.createResetPasswordResendCodeCommandParameters( + config, + config.oAuth2TokenCache, + correlationId, + continuationToken + ) - val command = ResetPasswordResendCodeCommand( - parameters = parameters, - controller = NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_RESET_PASSWORD_RESEND_CODE - ) + val command = ResetPasswordResendCodeCommand( + parameters = parameters, + controller = NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_RESET_PASSWORD_RESEND_CODE + ) - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is ResetPasswordCommandResult.CodeRequired -> { - ResetPasswordResendCodeResult.Success( - nextState = ResetPasswordCodeRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel - ) - } + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is ResetPasswordCommandResult.CodeRequired -> { + ResetPasswordResendCodeResult.Success( + nextState = ResetPasswordCodeRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel + ) + } - is INativeAuthCommandResult.Redirect, - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Resend code received unexpected result: ", - result - ) - ResendCodeError( - errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, - error = (result as INativeAuthCommandResult.Error).error, - correlationId = (result as INativeAuthCommandResult.Error).correlationId, - errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, - exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null - ) + is INativeAuthCommandResult.Redirect, + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Resend code received unexpected result: ", + result + ) + ResendCodeError( + errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, + error = (result as INativeAuthCommandResult.Error).error, + correlationId = (result as INativeAuthCommandResult.Error).correlationId, + errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, + exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null + ) + } } + } catch (e: Exception) { + ResendCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in resendCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -361,87 +383,97 @@ class ResetPasswordPasswordRequiredState internal constructor( methodName = "${TAG}.submitPassword(password: CharArray)" ) return withContext(Dispatchers.IO) { - val parameters = - CommandParametersAdapter.createResetPasswordSubmitNewPasswordCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - correlationId, - password - ) + try { + val parameters = + CommandParametersAdapter.createResetPasswordSubmitNewPasswordCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + correlationId, + password + ) - val command = ResetPasswordSubmitNewPasswordCommand( - parameters = parameters, - controller = NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_RESET_PASSWORD_SUBMIT_NEW_PASSWORD - ) + val command = ResetPasswordSubmitNewPasswordCommand( + parameters = parameters, + controller = NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_RESET_PASSWORD_SUBMIT_NEW_PASSWORD + ) - try { - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + try { + val rawCommandResult = + CommandDispatcher.submitSilentReturningFuture(command).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is ResetPasswordCommandResult.Complete -> { + ResetPasswordResult.Complete( + nextState = SignInContinuationState( + continuationToken = result.continuationToken, + username = username, + correlationId = result.correlationId, + config = config + ) + ) + } - return@withContext when (val result = - rawCommandResult.checkAndWrapCommandResultType()) { - is ResetPasswordCommandResult.Complete -> { - ResetPasswordResult.Complete( - nextState = SignInContinuationState( - continuationToken = result.continuationToken, - username = username, + is ResetPasswordCommandResult.PasswordNotAccepted -> { + ResetPasswordSubmitPasswordError( + errorType = ErrorTypes.INVALID_PASSWORD, + error = result.error, + errorMessage = result.errorDescription, correlationId = result.correlationId, - config = config + subError = result.subError ) - ) - } - - is ResetPasswordCommandResult.PasswordNotAccepted -> { - ResetPasswordSubmitPasswordError( - errorType = ErrorTypes.INVALID_PASSWORD, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - subError = result.subError - ) - } - - is ResetPasswordCommandResult.PasswordResetFailed -> { - ResetPasswordSubmitPasswordError( - errorType = ResetPasswordErrorTypes.PASSWORD_RESET_FAILED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - is ResetPasswordCommandResult.UserNotFound -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - ResetPasswordSubmitPasswordError( - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - ResetPasswordSubmitPasswordError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - exception = result.exception - ) + } + + is ResetPasswordCommandResult.PasswordResetFailed -> { + ResetPasswordSubmitPasswordError( + errorType = ResetPasswordErrorTypes.PASSWORD_RESET_FAILED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is ResetPasswordCommandResult.UserNotFound -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + ResetPasswordSubmitPasswordError( + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + ResetPasswordSubmitPasswordError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + exception = result.exception + ) + } } + } finally { + StringUtil.overwriteWithNull(parameters.newPassword) } - } finally { - StringUtil.overwriteWithNull(parameters.newPassword) + } catch (e: Exception) { + ResetPasswordSubmitPasswordError( + error = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitPassword.", + exception = e, + correlationId = correlationId + ) } } } diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt index f1de60613f..8b99a0c641 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt @@ -128,73 +128,82 @@ class SignInCodeRequiredState internal constructor( methodName = "${TAG}.submitCode(code: String)" ) return withContext(Dispatchers.IO) { - val params = CommandParametersAdapter.createSignInSubmitCodeCommandParameters( - config, - config.oAuth2TokenCache, - code, - continuationToken, - correlationId, - scopes - ) - - val signInSubmitCodeCommand = SignInSubmitCodeCommand( - parameters = params, - controller = NativeAuthMsalController(), - publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_SUBMIT_CODE - ) - - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(signInSubmitCodeCommand).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignInCommandResult.IncorrectCode -> { - SubmitCodeError( - errorType = ErrorTypes.INVALID_CODE, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - errorCodes = result.errorCodes, - subError = result.subError - ) + try { + val params = CommandParametersAdapter.createSignInSubmitCodeCommandParameters( + config, + config.oAuth2TokenCache, + code, + continuationToken, + correlationId, + scopes + ) - } + val signInSubmitCodeCommand = SignInSubmitCodeCommand( + parameters = params, + controller = NativeAuthMsalController(), + publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_SUBMIT_CODE + ) - is SignInCommandResult.Complete -> { - val authenticationResult = - AuthenticationResultAdapter.adapt(result.authenticationResult) + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(signInSubmitCodeCommand).get() - SignInResult.Complete( - resultValue = AccountState.createFromAuthenticationResult( - authenticationResult = authenticationResult, + return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { + is SignInCommandResult.IncorrectCode -> { + SubmitCodeError( + errorType = ErrorTypes.INVALID_CODE, + error = result.error, + errorMessage = result.errorDescription, correlationId = result.correlationId, - config = config + errorCodes = result.errorCodes, + subError = result.subError ) - ) - } - is INativeAuthCommandResult.Redirect -> { - SubmitCodeError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + } - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit code received unexpected result: ", - result - ) - SubmitCodeError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - errorCodes = result.errorCodes, - exception = result.exception - ) + is SignInCommandResult.Complete -> { + val authenticationResult = + AuthenticationResultAdapter.adapt(result.authenticationResult) + + SignInResult.Complete( + resultValue = AccountState.createFromAuthenticationResult( + authenticationResult = authenticationResult, + correlationId = result.correlationId, + config = config + ) + ) + } + + is INativeAuthCommandResult.Redirect -> { + SubmitCodeError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit code received unexpected result: ", + result + ) + SubmitCodeError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + errorCodes = result.errorCodes, + exception = result.exception + ) + } } + } catch (e: Exception) { + SubmitCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -239,51 +248,62 @@ class SignInCodeRequiredState internal constructor( methodName = "${TAG}.resendCode()" ) return withContext(Dispatchers.IO) { - val params = CommandParametersAdapter.createSignInResendCodeCommandParameters( - config, - config.oAuth2TokenCache, - correlationId, - continuationToken - ) - - val signInResendCodeCommand = SignInResendCodeCommand( - parameters = params, - controller = NativeAuthMsalController(), - publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_RESEND_CODE - ) - - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(signInResendCodeCommand).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignInCommandResult.CodeRequired -> { - SignInResendCodeResult.Success( - nextState = SignInCodeRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - scopes = scopes, - config = config - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel - ) - } + try { + val params = CommandParametersAdapter.createSignInResendCodeCommandParameters( + config, + config.oAuth2TokenCache, + correlationId, + continuationToken + ) - is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Resend code received unexpected result: ", - result - ) - ResendCodeError( - errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, - error = (result as INativeAuthCommandResult.Error).error, - correlationId = (result as INativeAuthCommandResult.Error).correlationId, - errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, - exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null - ) + val signInResendCodeCommand = SignInResendCodeCommand( + parameters = params, + controller = NativeAuthMsalController(), + publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_RESEND_CODE + ) + + val rawCommandResult = + CommandDispatcher.submitSilentReturningFuture(signInResendCodeCommand).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignInCommandResult.CodeRequired -> { + SignInResendCodeResult.Success( + nextState = SignInCodeRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + scopes = scopes, + config = config + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel + ) + } + + is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Resend code received unexpected result: ", + result + ) + ResendCodeError( + errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, + error = (result as INativeAuthCommandResult.Error).error, + correlationId = (result as INativeAuthCommandResult.Error).correlationId, + errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, + exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null + ) + } } + } catch (e: Exception) { + ResendCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in resendCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -375,65 +395,76 @@ class SignInPasswordRequiredState( methodName = "${TAG}.submitPassword(password: CharArray)" ) return withContext(Dispatchers.IO) { - val params = CommandParametersAdapter.createSignInSubmitPasswordCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - password, - correlationId, - scopes - ) - - try - { - val signInSubmitPasswordCommand = SignInSubmitPasswordCommand( - parameters = params, - controller = NativeAuthMsalController(), - publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_SUBMIT_PASSWORD + try { + val params = CommandParametersAdapter.createSignInSubmitPasswordCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + password, + correlationId, + scopes ) - val rawCommandResult = - CommandDispatcher.submitSilentReturningFuture(signInSubmitPasswordCommand).get() + try { + val signInSubmitPasswordCommand = SignInSubmitPasswordCommand( + parameters = params, + controller = NativeAuthMsalController(), + publicApiId = PublicApiId.NATIVE_AUTH_SIGN_IN_SUBMIT_PASSWORD + ) - return@withContext when (val result = - rawCommandResult.checkAndWrapCommandResultType()) { - is SignInCommandResult.InvalidCredentials -> { - SignInSubmitPasswordError( - errorType = SignInErrorTypes.INVALID_CREDENTIALS, - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId - ) - } - is SignInCommandResult.Complete -> { - val authenticationResult = - AuthenticationResultAdapter.adapt(result.authenticationResult) - SignInResult.Complete( - resultValue = AccountState.createFromAuthenticationResult( - authenticationResult = authenticationResult, - correlationId = result.correlationId, - config = config + val rawCommandResult = + CommandDispatcher.submitSilentReturningFuture(signInSubmitPasswordCommand) + .get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignInCommandResult.InvalidCredentials -> { + SignInSubmitPasswordError( + errorType = SignInErrorTypes.INVALID_CREDENTIALS, + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId ) - ) - } - is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - SignInSubmitPasswordError( - errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, - error = (result as INativeAuthCommandResult.Error).error, - correlationId = (result as INativeAuthCommandResult.Error).correlationId, - errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, - exception = (result as INativeAuthCommandResult.UnknownError).exception - ) + } + + is SignInCommandResult.Complete -> { + val authenticationResult = + AuthenticationResultAdapter.adapt(result.authenticationResult) + SignInResult.Complete( + resultValue = AccountState.createFromAuthenticationResult( + authenticationResult = authenticationResult, + correlationId = result.correlationId, + config = config + ) + ) + } + + is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + SignInSubmitPasswordError( + errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, + error = (result as INativeAuthCommandResult.Error).error, + correlationId = (result as INativeAuthCommandResult.Error).correlationId, + errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, + exception = (result as INativeAuthCommandResult.UnknownError).exception + ) + } } + } finally { + StringUtil.overwriteWithNull(params.password) } - } finally { - StringUtil.overwriteWithNull(params.password) + } catch (e: Exception) { + SignInSubmitPasswordError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitPassword.", + exception = e, + correlationId = correlationId + ) } } } @@ -522,70 +553,81 @@ class SignInContinuationState( */ suspend fun signIn(scopes: List? = null): SignInResult { return withContext(Dispatchers.IO) { - LogSession.logMethodCall( - tag = TAG, - correlationId = correlationId, - methodName = "${TAG}.signIn(scopes: List)" - ) - // Check if verification code was passed. If not, return an UnknownError with instructions to call the other - // sign in flows (code or password). - if (continuationToken.isNullOrEmpty()) { - Logger.warn( - TAG, - "Sign in after sign up received unexpected result: continuationToken was null" - ) - return@withContext SignInContinuationError( - errorMessage = "Sign In is not available through this state, please use the standalone sign in method.", - error = ErrorTypes.INVALID_STATE, - correlationId = "UNSET", + try { + LogSession.logMethodCall( + tag = TAG, + correlationId = correlationId, + methodName = "${TAG}.signIn(scopes: List)" ) - } - - val commandParameters = CommandParametersAdapter.createSignInWithContinuationTokenCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - username, - correlationId, - scopes - ) - - val command = SignInWithContinuationTokenCommand( - commandParameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_IN_WITH_SLT - ) - - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignInCommandResult.Complete -> { - val authenticationResult = - AuthenticationResultAdapter.adapt(result.authenticationResult) - SignInResult.Complete( - resultValue = AccountState.createFromAuthenticationResult( - authenticationResult = authenticationResult, - correlationId = result.correlationId, - config = config - ) - ) - } - is INativeAuthCommandResult.Redirect, - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( + // Check if verification code was passed. If not, return a generic error with instructions to call the other + // sign in flows (code or password). + if (continuationToken.isNullOrEmpty()) { + Logger.warn( TAG, - result.correlationId, - "Sign in after sign up received unexpected result: ", - result + "Sign in after sign up received unexpected result: continuationToken was null" ) - SignInContinuationError( - errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, - error = (result as INativeAuthCommandResult.Error).error, - correlationId = (result as INativeAuthCommandResult.Error).correlationId, - errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, - exception = (result as INativeAuthCommandResult.UnknownError).exception + return@withContext SignInContinuationError( + errorMessage = "Sign In is not available through this state, please use the standalone sign in method.", + error = ErrorTypes.INVALID_STATE, + correlationId = "UNSET", ) } + + val commandParameters = + CommandParametersAdapter.createSignInWithContinuationTokenCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + username, + correlationId, + scopes + ) + + val command = SignInWithContinuationTokenCommand( + commandParameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_IN_WITH_SLT + ) + + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignInCommandResult.Complete -> { + val authenticationResult = + AuthenticationResultAdapter.adapt(result.authenticationResult) + SignInResult.Complete( + resultValue = AccountState.createFromAuthenticationResult( + authenticationResult = authenticationResult, + correlationId = result.correlationId, + config = config + ) + ) + } + + is INativeAuthCommandResult.Redirect, + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Sign in after sign up received unexpected result: ", + result + ) + SignInContinuationError( + errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, + error = (result as INativeAuthCommandResult.Error).error, + correlationId = (result as INativeAuthCommandResult.Error).correlationId, + errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, + exception = (result as INativeAuthCommandResult.UnknownError).exception + ) + } + } + } catch (e: Exception) { + SignInContinuationError( + errorMessage = "MSAL client exception occurred in signIn.", + exception = e, + correlationId = correlationId + ) } } } diff --git a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt index a8f1ad6b87..97245735fe 100644 --- a/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt +++ b/msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt @@ -130,106 +130,116 @@ class SignUpCodeRequiredState internal constructor( methodName = "${TAG}.submitCode(code: String)" ) return withContext(Dispatchers.IO) { - val commandParameters = - CommandParametersAdapter.createSignUpSubmitCodeCommandParameters( - config, - config.oAuth2TokenCache, - code, - continuationToken, - correlationId + try { + val commandParameters = + CommandParametersAdapter.createSignUpSubmitCodeCommandParameters( + config, + config.oAuth2TokenCache, + code, + continuationToken, + correlationId + ) + + val command = SignUpSubmitCodeCommand( + commandParameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_CODE ) + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - val command = SignUpSubmitCodeCommand( - commandParameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_CODE - ) - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignUpCommandResult.PasswordRequired -> { - SignUpResult.PasswordRequired( - nextState = SignUpPasswordRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignUpCommandResult.PasswordRequired -> { + SignUpResult.PasswordRequired( + nextState = SignUpPasswordRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ) ) - ) - } + } - is SignUpCommandResult.AttributesRequired -> { - SignUpResult.AttributesRequired( - nextState = SignUpAttributesRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config - ), - requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() - ) - } + is SignUpCommandResult.AttributesRequired -> { + SignUpResult.AttributesRequired( + nextState = SignUpAttributesRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ), + requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() + ) + } - is SignUpCommandResult.Complete -> { - SignUpResult.Complete( - nextState = SignInContinuationState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config + is SignUpCommandResult.Complete -> { + SignUpResult.Complete( + nextState = SignInContinuationState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ) ) - ) - } + } - is SignUpCommandResult.InvalidCode -> { - SubmitCodeError( - errorType = ErrorTypes.INVALID_CODE, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - subError = result.subError - ) - } + is SignUpCommandResult.InvalidCode -> { + SubmitCodeError( + errorType = ErrorTypes.INVALID_CODE, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId, + subError = result.subError + ) + } - is INativeAuthCommandResult.Redirect -> { - SubmitCodeError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } + is INativeAuthCommandResult.Redirect -> { + SubmitCodeError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - // This should be caught earlier in the flow, so throwing UnexpectedError - is SignUpCommandResult.UsernameAlreadyExists -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit code received unexpected result: ", - result - ) - SubmitCodeError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId - ) - } + // This should be caught earlier in the flow, so returning generic error + is SignUpCommandResult.UsernameAlreadyExists -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit code received unexpected result: ", + result + ) + SubmitCodeError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId + ) + } - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit code received unexpected result: ", - result - ) - SubmitCodeError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - exception = result.exception - ) + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit code received unexpected result: ", + result + ) + SubmitCodeError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId, + exception = result.exception + ) + } } + } catch (e: Exception) { + SubmitCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -273,50 +283,60 @@ class SignUpCodeRequiredState internal constructor( methodName = "${TAG}.resendCode()" ) return withContext(Dispatchers.IO) { - val commandParameters = - CommandParametersAdapter.createSignUpResendCodeCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - correlationId - ) - val command = SignUpResendCodeCommand( - commandParameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_UP_RESEND_CODE - ) - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignUpCommandResult.CodeRequired -> { - SignUpResendCodeResult.Success( - nextState = SignUpCodeRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config - ), - codeLength = result.codeLength, - sentTo = result.challengeTargetLabel, - channel = result.challengeChannel + try { + val commandParameters = + CommandParametersAdapter.createSignUpResendCodeCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + correlationId ) - } + val command = SignUpResendCodeCommand( + commandParameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_UP_RESEND_CODE + ) + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() - is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Resend code received unexpected result: ", - result - ) - ResendCodeError( - errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, - error = (result as INativeAuthCommandResult.Error).error, - correlationId = (result as INativeAuthCommandResult.Error).correlationId, - errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, - exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null - ) + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignUpCommandResult.CodeRequired -> { + SignUpResendCodeResult.Success( + nextState = SignUpCodeRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ), + codeLength = result.codeLength, + sentTo = result.challengeTargetLabel, + channel = result.challengeChannel + ) + } + + is INativeAuthCommandResult.Redirect, is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Resend code received unexpected result: ", + result + ) + ResendCodeError( + errorMessage = (result as INativeAuthCommandResult.Error).errorDescription, + error = (result as INativeAuthCommandResult.Error).error, + correlationId = (result as INativeAuthCommandResult.Error).correlationId, + errorCodes = (result as INativeAuthCommandResult.Error).errorCodes, + exception = if (result is INativeAuthCommandResult.UnknownError) result.exception else null + ) + } } + } catch (e: Exception) { + ResendCodeError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in resendCode.", + exception = e, + correlationId = correlationId + ) } } } @@ -409,114 +429,124 @@ class SignUpPasswordRequiredState internal constructor( methodName = "${TAG}.submitPassword(password: CharArray)" ) return withContext(Dispatchers.IO) { - val commandParameters = - CommandParametersAdapter.createSignUpSubmitPasswordCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - correlationId, - password + try { + val commandParameters = + CommandParametersAdapter.createSignUpSubmitPasswordCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + correlationId, + password + ) + val command = SignUpSubmitPasswordCommand( + commandParameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_PASSWORD ) - val command = SignUpSubmitPasswordCommand( - commandParameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_PASSWORD - ) - try { - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + try { + val rawCommandResult = + CommandDispatcher.submitSilentReturningFuture(command).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignUpCommandResult.Complete -> { + SignUpResult.Complete( + nextState = SignInContinuationState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ) + ) + } + + is SignUpCommandResult.AttributesRequired -> { + SignUpResult.AttributesRequired( + nextState = SignUpAttributesRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ), + requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() + ) + } - return@withContext when (val result = - rawCommandResult.checkAndWrapCommandResultType()) { - is SignUpCommandResult.Complete -> { - SignUpResult.Complete( - nextState = SignInContinuationState( - continuationToken = result.continuationToken, + is SignUpCommandResult.InvalidPassword -> { + SignUpSubmitPasswordError( + errorType = ErrorTypes.INVALID_PASSWORD, + error = result.error, + errorMessage = result.errorDescription, correlationId = result.correlationId, - username = username, - config = config + subError = result.subError ) - ) - } - - is SignUpCommandResult.AttributesRequired -> { - SignUpResult.AttributesRequired( - nextState = SignUpAttributesRequiredState( - continuationToken = result.continuationToken, + } + + is INativeAuthCommandResult.Redirect -> { + SignUpSubmitPasswordError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + // This should be caught earlier in the flow, so throwing UnexpectedError + is SignUpCommandResult.UsernameAlreadyExists -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + SignUpSubmitPasswordError( + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + // This should be caught earlier in the flow, so returning generic error + is INativeAuthCommandResult.InvalidUsername -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + SignUpSubmitPasswordError( + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit password received unexpected result: ", + result + ) + SignUpSubmitPasswordError( + errorMessage = result.errorDescription, + error = result.error, correlationId = result.correlationId, - username = username, - config = config - ), - requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() - ) - } - - is SignUpCommandResult.InvalidPassword -> { - SignUpSubmitPasswordError( - errorType = ErrorTypes.INVALID_PASSWORD, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId, - subError = result.subError - ) - } - - is INativeAuthCommandResult.Redirect -> { - SignUpSubmitPasswordError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - // This should be caught earlier in the flow, so throwing UnexpectedError - is SignUpCommandResult.UsernameAlreadyExists -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - SignUpSubmitPasswordError( - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - // This should be caught earlier in the flow, so throwing UnexpectedError - is INativeAuthCommandResult.InvalidUsername -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - SignUpSubmitPasswordError( - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit password received unexpected result: ", - result - ) - SignUpSubmitPasswordError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - exception = result.exception - ) + exception = result.exception + ) + } } + } finally { + StringUtil.overwriteWithNull(commandParameters.password) } - } finally { - StringUtil.overwriteWithNull(commandParameters.password) + } catch (e: Exception) { + SignUpSubmitPasswordError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitPassword.", + exception = e, + correlationId = correlationId + ) } } } @@ -609,89 +639,103 @@ class SignUpAttributesRequiredState internal constructor( methodName = "${TAG}.submitAttributes(attributes: UserAttributes)" ) return withContext(Dispatchers.IO) { - val commandParameters = - CommandParametersAdapter.createSignUpStarSubmitUserAttributesCommandParameters( - config, - config.oAuth2TokenCache, - continuationToken, - correlationId, - attributes.toMap() + try { + val commandParameters = + CommandParametersAdapter.createSignUpStarSubmitUserAttributesCommandParameters( + config, + config.oAuth2TokenCache, + continuationToken, + correlationId, + attributes.toMap() + ) + + val command = SignUpSubmitUserAttributesCommand( + commandParameters, + NativeAuthMsalController(), + PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_ATTRIBUTES ) - val command = SignUpSubmitUserAttributesCommand( - commandParameters, - NativeAuthMsalController(), - PublicApiId.NATIVE_AUTH_SIGN_UP_SUBMIT_ATTRIBUTES - ) + val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + + return@withContext when (val result = + rawCommandResult.checkAndWrapCommandResultType()) { + is SignUpCommandResult.AttributesRequired -> { + SignUpResult.AttributesRequired( + nextState = SignUpAttributesRequiredState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ), + requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() + ) + } + + is SignUpCommandResult.Complete -> { + SignUpResult.Complete( + nextState = SignInContinuationState( + continuationToken = result.continuationToken, + correlationId = result.correlationId, + username = username, + config = config + ) + ) + } - val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get() + is SignUpCommandResult.InvalidAttributes -> { + SignUpSubmitAttributesError( + errorType = SignUpErrorTypes.INVALID_ATTRIBUTES, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } - return@withContext when (val result = rawCommandResult.checkAndWrapCommandResultType()) { - is SignUpCommandResult.AttributesRequired -> { - SignUpResult.AttributesRequired( - nextState = SignUpAttributesRequiredState( - continuationToken = result.continuationToken, - correlationId = result.correlationId, - username = username, - config = config - ), - requiredAttributes = result.requiredAttributes.toListOfRequiredUserAttribute() - ) - } - is SignUpCommandResult.Complete -> { - SignUpResult.Complete( - nextState = SignInContinuationState( - continuationToken = result.continuationToken, + is INativeAuthCommandResult.Redirect -> { + SignUpSubmitAttributesError( + errorType = ErrorTypes.BROWSER_REQUIRED, + error = result.error, + errorMessage = result.errorDescription, + correlationId = result.correlationId + ) + } + // This should be caught earlier in the flow, so returning generic error + is SignUpCommandResult.UsernameAlreadyExists -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit attributes received unexpected result: ", + result + ) + SignUpSubmitAttributesError( + errorMessage = result.errorDescription, + error = result.error, + correlationId = result.correlationId + ) + } + + is INativeAuthCommandResult.UnknownError -> { + Logger.warnWithObject( + TAG, + result.correlationId, + "Submit attributes received unexpected result: ", + result + ) + SignUpSubmitAttributesError( + errorMessage = result.errorDescription, + error = result.error, correlationId = result.correlationId, - username = username, - config = config + exception = result.exception ) - ) - } - is SignUpCommandResult.InvalidAttributes -> { - SignUpSubmitAttributesError( - errorType = SignUpErrorTypes.INVALID_ATTRIBUTES, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - is INativeAuthCommandResult.Redirect -> { - SignUpSubmitAttributesError( - errorType = ErrorTypes.BROWSER_REQUIRED, - error = result.error, - errorMessage = result.errorDescription, - correlationId = result.correlationId - ) - } - // This should be caught earlier in the flow, so throwing UnexpectedError - is SignUpCommandResult.UsernameAlreadyExists -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit attributes received unexpected result: ", - result - ) - SignUpSubmitAttributesError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId - ) - } - is INativeAuthCommandResult.UnknownError -> { - Logger.warnWithObject( - TAG, - result.correlationId, - "Submit attributes received unexpected result: ", - result - ) - SignUpSubmitAttributesError( - errorMessage = result.errorDescription, - error = result.error, - correlationId = result.correlationId, - exception = result.exception - ) + } } + } catch (e: Exception) { + SignUpSubmitAttributesError( + errorType = ErrorTypes.CLIENT_EXCEPTION, + errorMessage = "MSAL client exception occurred in submitAttributes.", + exception = e, + correlationId = correlationId + ) } } } diff --git a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationJavaTest.java b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationJavaTest.java index fc86b49a99..e69a658c21 100644 --- a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationJavaTest.java +++ b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationJavaTest.java @@ -27,12 +27,12 @@ import android.app.Activity; import android.content.Context; +import com.microsoft.identity.client.ILoggerCallback; import com.microsoft.identity.client.Logger; import com.microsoft.identity.client.PublicClientApplication; import com.microsoft.identity.client.e2e.shadows.ShadowAndroidSdkStorageEncryptionManager; import com.microsoft.identity.client.e2e.tests.PublicClientApplicationAbstractTest; import com.microsoft.identity.client.e2e.utils.AcquireTokenTestHelper; -import com.microsoft.identity.client.exception.MsalClientException; import com.microsoft.identity.client.exception.MsalException; import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenError; import com.microsoft.identity.nativeauth.statemachine.errors.ResetPasswordError; @@ -74,6 +74,7 @@ import com.microsoft.identity.common.java.util.ResultFuture; import com.microsoft.identity.internal.testutils.TestUtils; import com.microsoft.identity.nativeauth.statemachine.states.SignUpPasswordRequiredState; +import com.microsoft.identity.nativeauth.utils.LoggerCheckHelper; import org.junit.After; import org.junit.AfterClass; @@ -82,13 +83,17 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; -import org.robolectric.RobolectricTestRunner; +import org.mockito.MockitoAnnotations; +import org.robolectric.ParameterizedRobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.annotation.LooperMode; import java.io.File; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -106,7 +111,7 @@ import static org.mockito.Mockito.spy; import static org.robolectric.annotation.LooperMode.Mode.LEGACY; -@RunWith(RobolectricTestRunner.class) +@RunWith(ParameterizedRobolectricTestRunner.class) @LooperMode(LEGACY) @Config(shadows = {ShadowAndroidSdkStorageEncryptionManager.class}) public class NativeAuthPublicClientApplicationJavaTest extends PublicClientApplicationAbstractTest { @@ -115,11 +120,20 @@ public class NativeAuthPublicClientApplicationJavaTest extends PublicClientAppli private IPlatformComponents components; private Activity activity; private INativeAuthPublicClientApplication application; + private LoggerCheckHelper loggerCheckHelper; private final String username = "user@email.com"; private final String invalidUsername = "invalidUsername"; private final char[] password = "verySafePassword".toCharArray(); private final String code = "1234"; private final String emptyString = ""; + private final boolean allowPII; + + public NativeAuthPublicClientApplicationJavaTest(boolean allowPII) { + this.allowPII = allowPII; + } + + @Mock + private ILoggerCallback externalLogger; @Override @@ -137,21 +151,26 @@ public static void tearDownClass() { setUseMockApiForNativeAuth(false); } + @ParameterizedRobolectricTestRunner.Parameters + public static Collection data() { + return Arrays.asList(true, false); + } + @Before public void setup() { + MockitoAnnotations.initMocks(this); context = ApplicationProvider.getApplicationContext(); components = AndroidPlatformComponentsFactory.createFromContext(context); activity = Mockito.mock(Activity.class); + loggerCheckHelper = new LoggerCheckHelper(externalLogger, allowPII); Mockito.when(activity.getApplicationContext()).thenReturn(context); setupPCA(); - Logger.getInstance().setEnableLogcatLog(true); - Logger.getInstance().setEnablePII(true); - Logger.getInstance().setLogLevel(Logger.LogLevel.VERBOSE); CommandDispatcherHelper.clear(); } @After public void cleanup() { + loggerCheckHelper.checkSafeLogging(); AcquireTokenTestHelper.setAccount(null); // remove everything from cache after test ends TestUtils.clearCache(SHARED_PREFERENCES_NAME); @@ -606,24 +625,18 @@ public void onError(@NonNull BaseException exception) { assertTrue(signInWithPasswordResult.get(10, TimeUnit.SECONDS) instanceof SignInResult.Complete); - ResultFuture signInExceptionResult = new ResultFuture<>(); - NativeAuthPublicClientApplication.SignInCallback signInWithPasswordResultCallback2 - = new NativeAuthPublicClientApplication.SignInCallback() { - @Override - public void onResult(SignInResult result) { - - } + SignInTestCallback signInWithPasswordResultTestCallback = new SignInTestCallback(); - @Override - public void onError(@NonNull BaseException exception) { - signInExceptionResult.setResult(exception); - } - }; + application.signIn( + username, + password, + null, + signInWithPasswordResultTestCallback + ); + SignInResult signInError = signInWithPasswordResultTestCallback.get(); - application.signIn(username, password, null, signInWithPasswordResultCallback2); - BaseException exception = signInExceptionResult.get(10, TimeUnit.SECONDS); - assertEquals(MsalClientException.INVALID_PARAMETER, exception.getErrorCode()); - assertEquals("An account is already signed in.", exception.getMessage()); + assertTrue(signInError instanceof SignInError); + assertEquals("An account is already signed in.", ((SignInError) signInError).getException().getMessage()); } /** @@ -855,25 +868,14 @@ public void onError(@NonNull BaseException exception) { assertTrue(signInWithPasswordResult.get(10, TimeUnit.SECONDS) instanceof SignInResult.Complete); - ResultFuture signUpExceptionResult = new ResultFuture<>(); - NativeAuthPublicClientApplication.SignUpCallback signUpWithPasswordResultCallback - = new NativeAuthPublicClientApplication.SignUpCallback() { - - @Override - public void onResult(SignUpResult result) { + SignUpTestCallback signUpTestCallback = new SignUpTestCallback (); - } + application.signUp(username, password, null, signUpTestCallback); - @Override - public void onError(@NonNull BaseException exception) { - signUpExceptionResult.setResult(exception); - } - }; - application.signUp(username, password, null, signUpWithPasswordResultCallback); - BaseException exception = signUpExceptionResult.get(10, TimeUnit.SECONDS); + SignUpResult signUpError = signUpTestCallback.get(); - assertEquals(MsalClientException.INVALID_PARAMETER, exception.getErrorCode()); - assertEquals("An account is already signed in.", exception.getMessage()); + assertTrue(signUpError instanceof SignUpError); + assertEquals("An account is already signed in.", ((SignUpError) signUpError).getException().getMessage()); } /** @@ -924,24 +926,14 @@ public void onError(@NonNull BaseException exception) { assertTrue(signInWithPasswordResult.get(10, TimeUnit.SECONDS) instanceof SignInResult.Complete); ResultFuture signUpExceptionResult = new ResultFuture<>(); - NativeAuthPublicClientApplication.SignUpCallback signUpWithCodeResultCallback - = new NativeAuthPublicClientApplication.SignUpCallback() { + SignUpTestCallback signUpTestCallback = new SignUpTestCallback(); - @Override - public void onResult(SignUpResult result) { + application.signUp(username, null, null, signUpTestCallback); - } + SignUpResult signUpError = signUpTestCallback.get(); - @Override - public void onError(@NonNull BaseException exception) { - signUpExceptionResult.setResult(exception); - } - }; - application.signUp(username, null, null, signUpWithCodeResultCallback); - - BaseException exception = signUpExceptionResult.get(10, TimeUnit.SECONDS); - assertEquals(MsalClientException.INVALID_PARAMETER, exception.getErrorCode()); - assertEquals("An account is already signed in.", exception.getMessage()); + assertTrue(signUpError instanceof SignUpError); + assertEquals("An account is already signed in.", ((SignUpError) signUpError).getException().getMessage()); } /** @@ -982,25 +974,12 @@ public void testResetPasswordBlocked() throws ExecutionException, InterruptedExc assertTrue(signInWithPasswordResult instanceof SignInResult.Complete); - ResultFuture resetPasswordExceptionResult = new ResultFuture<>(); - - NativeAuthPublicClientApplication.ResetPasswordCallback resetPasswordCallback - = new NativeAuthPublicClientApplication.ResetPasswordCallback() { - @Override - public void onResult(ResetPasswordStartResult result) { - } - - @Override - public void onError(@NonNull BaseException exception) { - resetPasswordExceptionResult.setResult(exception); - - } - }; - application.resetPassword(username, resetPasswordCallback); + ResetPasswordStartTestCallback resetPasswordStartTestCallback = new ResetPasswordStartTestCallback(); + application.resetPassword(username, resetPasswordStartTestCallback); + ResetPasswordStartResult resetPasswordError = resetPasswordStartTestCallback.get(); - BaseException exception = resetPasswordExceptionResult.get(10, TimeUnit.SECONDS); - assertEquals(MsalClientException.INVALID_PARAMETER, exception.getErrorCode()); - assertEquals("An account is already signed in.", exception.getMessage()); + assertTrue(resetPasswordError instanceof ResetPasswordError); + assertEquals("An account is already signed in.", ((ResetPasswordError) resetPasswordError).getException().getMessage()); } /** diff --git a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt index 7c0fdbb537..310ababd15 100644 --- a/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt +++ b/msal/src/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt @@ -25,15 +25,27 @@ package com.microsoft.identity.nativeauth import android.app.Activity import android.content.Context import androidx.test.core.app.ApplicationProvider +import com.microsoft.identity.client.ILoggerCallback import com.microsoft.identity.client.PublicClientApplication import com.microsoft.identity.client.e2e.shadows.ShadowAndroidSdkStorageEncryptionManager import com.microsoft.identity.client.e2e.tests.PublicClientApplicationAbstractTest import com.microsoft.identity.client.e2e.utils.AcquireTokenTestHelper -import com.microsoft.identity.client.exception.MsalClientException import com.microsoft.identity.client.exception.MsalException +import com.microsoft.identity.common.components.AndroidPlatformComponentsFactory +import com.microsoft.identity.common.internal.controllers.CommandDispatcherHelper +import com.microsoft.identity.common.java.exception.BaseException +import com.microsoft.identity.common.java.interfaces.IPlatformComponents +import com.microsoft.identity.common.java.nativeauth.BuildValues +import com.microsoft.identity.common.java.util.ResultFuture +import com.microsoft.identity.common.nativeauth.MockApiEndpoint +import com.microsoft.identity.common.nativeauth.MockApiResponseType +import com.microsoft.identity.common.nativeauth.MockApiUtils.Companion.configureMockApi +import com.microsoft.identity.internal.testutils.TestUtils +import com.microsoft.identity.nativeauth.statemachine.errors.ErrorTypes import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenError import com.microsoft.identity.nativeauth.statemachine.errors.ResetPasswordError import com.microsoft.identity.nativeauth.statemachine.errors.ResetPasswordSubmitPasswordError +import com.microsoft.identity.nativeauth.statemachine.errors.SignInContinuationError import com.microsoft.identity.nativeauth.statemachine.errors.SignInError import com.microsoft.identity.nativeauth.statemachine.errors.SignUpError import com.microsoft.identity.nativeauth.statemachine.errors.SignUpSubmitAttributesError @@ -48,20 +60,9 @@ import com.microsoft.identity.nativeauth.statemachine.results.SignInResult import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult -import com.microsoft.identity.common.components.AndroidPlatformComponentsFactory -import com.microsoft.identity.common.internal.controllers.CommandDispatcherHelper -import com.microsoft.identity.common.nativeauth.MockApiEndpoint -import com.microsoft.identity.common.nativeauth.MockApiResponseType -import com.microsoft.identity.common.nativeauth.MockApiUtils.Companion.configureMockApi -import com.microsoft.identity.common.java.exception.BaseException -import com.microsoft.identity.common.java.interfaces.IPlatformComponents -import com.microsoft.identity.common.java.nativeauth.BuildValues -import com.microsoft.identity.common.java.util.ResultFuture -import com.microsoft.identity.internal.testutils.TestUtils -import com.microsoft.identity.nativeauth.statemachine.errors.ErrorTypes import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState +import com.microsoft.identity.nativeauth.utils.LoggerCheckHelper import com.microsoft.identity.nativeauth.utils.mockCorrelationId -import com.microsoft.identity.nativeauth.statemachine.errors.SignInContinuationError import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest @@ -76,11 +77,13 @@ import org.junit.BeforeClass import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mock import org.mockito.Mockito +import org.mockito.MockitoAnnotations import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.whenever -import org.robolectric.RobolectricTestRunner +import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config import java.io.File import java.util.UUID @@ -88,23 +91,34 @@ import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException + @ExperimentalCoroutinesApi -@RunWith(RobolectricTestRunner::class) +@RunWith(ParameterizedRobolectricTestRunner::class) @Config(shadows = [ShadowAndroidSdkStorageEncryptionManager::class]) -class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstractTest() { +class NativeAuthPublicClientApplicationKotlinTest(private val allowPII: Boolean) : PublicClientApplicationAbstractTest() { private lateinit var context: Context private lateinit var components: IPlatformComponents private lateinit var activity: Activity private lateinit var application: INativeAuthPublicClientApplication + private lateinit var loggerCheckHelper: LoggerCheckHelper private val username = "user@email.com" private val invalidUsername = "invalidUsername" private val password = "verySafePassword".toCharArray() private val code = "1234" private val emptyString = "" + @Mock + private lateinit var externalLogger: ILoggerCallback + override fun getConfigFilePath() = "src/test/res/raw/native_auth_native_only_test_config.json" companion object { + @JvmStatic + @ParameterizedRobolectricTestRunner.Parameters + fun data(): Collection { + return listOf(true, false) + } + @BeforeClass @JvmStatic fun setupClass() { @@ -120,9 +134,11 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr @Before override fun setup() { + MockitoAnnotations.initMocks(this) context = ApplicationProvider.getApplicationContext() components = AndroidPlatformComponentsFactory.createFromContext(context) activity = Mockito.mock(Activity::class.java) + loggerCheckHelper = LoggerCheckHelper(externalLogger, allowPII) whenever(activity.applicationContext).thenReturn(context) setupPCA() CommandDispatcherHelper.clear() @@ -130,6 +146,7 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr @After fun cleanup() { + loggerCheckHelper.checkSafeLogging() AcquireTokenTestHelper.setAccount(null) // remove everything from cache after test ends TestUtils.clearCache(SHARED_PREFERENCES_NAME) @@ -364,14 +381,9 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr val result = application.signIn(username, password) assertTrue(result is SignInResult.Complete) - try { - application.signIn(username, password) - } catch (exception: MsalException) { - assertEquals(MsalClientException.INVALID_PARAMETER, exception.errorCode) - assertEquals("An account is already signed in.", exception.message) - return@runTest - } - fail() // An exception should happen + val newResult = application.signIn(username) + assertTrue(newResult is SignInError) + assertTrue((newResult as SignInError).exception?.message.equals("An account is already signed in.")) } /** @@ -424,6 +436,7 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr ) val result = continuationTokenState.signIn(scopes = null) assertTrue(result is SignInContinuationError) + } /** @@ -1105,14 +1118,9 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr val result = application.signIn(username, password) assertTrue(result is SignInResult.Complete) - try { - application.signIn(username, password) - } catch (exception: MsalException) { - assertEquals(MsalClientException.INVALID_PARAMETER, exception.errorCode) - assertEquals("An account is already signed in.", exception.message) - return@runTest - } - fail() // An exception should happen + val newResult = application.signUp(username) + assertTrue(newResult is SignUpError) + assertTrue((newResult as SignUpError).exception?.message.equals("An account is already signed in.")) } /** @@ -1142,14 +1150,9 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr val result = application.signIn(username, password) assertTrue(result is SignInResult.Complete) - try { - application.signUp(username) - } catch (exception: MsalException) { - assertEquals(MsalClientException.INVALID_PARAMETER, exception.errorCode) - assertEquals("An account is already signed in.", exception.message) - return@runTest - } - fail() // An exception should happen + val newResult = application.signUp(username) + assertTrue(newResult is SignUpError) + assertTrue((newResult as SignUpError).exception?.message.equals("An account is already signed in.")) } /** @@ -1179,14 +1182,9 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr val result = application.signIn(username, password) assertTrue(result is SignInResult.Complete) - try { - application.resetPassword(username) - } catch (exception: MsalException) { - assertEquals(MsalClientException.INVALID_PARAMETER, exception.errorCode) - assertEquals("An account is already signed in.", exception.message) - return@runBlocking - } - fail() // An exception should happen + val newResult = application.resetPassword(username) + assertTrue(newResult is ResetPasswordError) + assertTrue((newResult as ResetPasswordError).exception?.message.equals("An account is already signed in.")) } /** @@ -2292,4 +2290,35 @@ class NativeAuthPublicClientApplicationKotlinTest : PublicClientApplicationAbstr assertTrue(result is SignUpError) assertTrue((result as SignUpError).isInvalidUsername()) } + + @Test + fun testEmptyRequestParametersToGenericErrorNotThrownException() = runTest { + val correlationId = UUID.randomUUID().toString() + + configureMockApi( + endpointType = MockApiEndpoint.SignUpStart, + correlationId = correlationId, + responseType = MockApiResponseType.SIGNUP_START_SUCCESS + ) + + configureMockApi( + endpointType = MockApiEndpoint.SignUpChallenge, + correlationId = correlationId, + responseType = MockApiResponseType.CHALLENGE_TYPE_OOB + ) + + val result = application.signUp(username) + assertTrue(result is SignUpResult.CodeRequired) + + configureMockApi( + endpointType = MockApiEndpoint.SignUpContinue, + correlationId = correlationId, + responseType = MockApiResponseType.INVALID_OOB_VALUE + ) + + val submitCodeState = spy((result as SignUpResult.CodeRequired).nextState) + val submitCodeResult = submitCodeState.submitCode(emptyString) // Empty code will trigger ArgUtils.validateNonNullArg(oob, "oob") of the SignUpContinueRequest to thrown ClientException + assertTrue(submitCodeResult is SubmitCodeError) + assertTrue((submitCodeResult as SubmitCodeError).error.equals("unsuccessful_command")) // ClientException will be caught in CommandResultUtil.kt and converted to generic error in interface layer + } } diff --git a/msal/src/test/java/com/microsoft/identity/nativeauth/utils/LoggerCheckHelper.kt b/msal/src/test/java/com/microsoft/identity/nativeauth/utils/LoggerCheckHelper.kt new file mode 100644 index 0000000000..ab5f9ef6ff --- /dev/null +++ b/msal/src/test/java/com/microsoft/identity/nativeauth/utils/LoggerCheckHelper.kt @@ -0,0 +1,89 @@ +package com.microsoft.identity.nativeauth.utils + +import com.microsoft.identity.client.ILoggerCallback +import com.microsoft.identity.client.Logger +import org.mockito.ArgumentMatcher +import org.mockito.ArgumentMatchers +import org.mockito.kotlin.any +import org.mockito.kotlin.argThat +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +class LoggerCheckHelper(private val externalLogger: ILoggerCallback, private val allowPII: Boolean) { + + private val sensitivePIIMessages = listOf( + """(?() + val disableList: List + + if (allowPII) { + allowList = permittedPIIMessages + disableList = sensitivePIIMessages + } else { + disableList = sensitivePIIMessages + permittedPIIMessages + } + + allowList.forEach { regex -> + verifyLogCouldContain(regex) + } + disableList.forEach { regex -> + verifyLogDoesNotContain(regex) + } + + clearLogger() + } + + private fun verifyLogDoesNotContain(regex: String) { + verify(externalLogger, never()).log( + any(), + any(), + argThat(RegexMatcher(regex)), + ArgumentMatchers.anyBoolean() + ) + } + + private fun verifyLogCouldContain(regex: String) { + verify(externalLogger, never()).log( + any(), + any(), + argThat(RegexMatcher(regex)), // allowList items are logged but the containsPII should be true. + eq(false) + ) + } + + class RegexMatcher(private val regex: String) : ArgumentMatcher { + override fun matches(argument: String?): Boolean { + return regex.toRegex().containsMatchIn(argument ?: "") + } + } +}