Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ClientException wrapper for native auth #2080

Merged
merged 25 commits into from May 14, 2024
Merged

Conversation

Yuki-YuXin
Copy link
Contributor

@Yuki-YuXin Yuki-YuXin commented Apr 16, 2024

Goal:

The SDK currently throws Exceptions in some flows (e.g. ClientException when an empty username is passed, refresh token exceptions, etc.). We should refactor this and replace it with exceptions being wrapped into an Error class and returned as a normal SDK response. Calling applications shouldn't crash, and shouldn't require try-catch blocks.
This will also involve updating the sample app (removing try-catch surrounding SDK methods being called)

Investigation summary:

  • The incorrect configuration throws MsalClientException which it's inevitable and unsolvable by error class wrapper.
  • Under CommandResultUtil.kt, the Exception has been wrapped as UnknownError.
 if (this.status != ICommandResult.ResultStatus.COMPLETED) {
        var exception: Exception? = null
        var exceptionMessage: String? = ""

        if (this.result is Exception) {
            exception = this.result as Exception
            exceptionMessage = exception.message
        }

        return com.microsoft.identity.common.java.nativeauth.controllers.results.INativeAuthCommandResult.UnknownError(
            error = UNSUCCESSFUL_COMMAND_ERROR,
            errorDescription = exceptionMessage,
            exception = exception,
            correlationId = this.correlationId
        ) as ExpectedType
  • BaseNativeAuthController throws ClientException. They could only be called in NativeAuthMsalController and we didn't.

  • NativeAuthMsalController throws IOException::class, ClientException::class, ServiceException::class ArgumentException::class, ServiceException::class. mainly from acquireTokenSilent & renewAT used by AcquireTokenNoFixedScopesCommand (accountState.getAccessToken). It will be handled by is Exception in getAccessToken`

  • AcquireTokenNoFixedScopesCommandParameters throws ArgumentException -> this parameter will be delted

  • Request throws ClientException -> NativeAuthRequestHandlerTest include

  • verifyNoUserIsSignedIn() throws MsalClientException

Changes summary:

  1. Add GetAccountError and SignOutError error in Error.kt
  2. Use try catch block with corresponding errors in interface methods.

Company PRs:

native sample app: Azure-Samples/ms-identity-ciam-native-auth-android-sample#24

… signout and NativeAuthPublicClientApplication.kt getCurrentAccount as they have no associated errors.
# Conflicts:
#	msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt
#	msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
#	msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/ResetPasswordStates.kt
#	msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt
#	msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignUpStates.kt
@Yuki-YuXin Yuki-YuXin marked this pull request as ready for review May 9, 2024 16:08
@Yuki-YuXin Yuki-YuXin requested a review from a team as a code owner May 9, 2024 16:08
correlationId = result.correlationId
)
}
// This should be caught earlier in the flow, so throwing UnexpectedError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's update this comment: // This should be caught earlier in the flow, so returning generic error

@Yuki-YuXin Yuki-YuXin added the No-Changelog This change does not update the changelog. label May 10, 2024
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(ErrorTypes.UNSUCCESSFUL_COMMAND_ERROR)) // ClientException will be caught in CommandResultUtil.kt and converted to generic error in interface layer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is ErrorTypes.UNSUCCESSFUL_COMMAND_ERROR set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It set under CommandResultUtil.kt const val UNSUCCESSFUL_COMMAND_ERROR = "unsuccessful_command"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think those changes are pushed, I don't see them in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They originally existed in the common repository.
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this PR also introduces const val UNSUCCESSFUL_COMMAND_ERROR = "unsuccessful_command" in Error.kt. Is that one used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't used in the SDK code base because this PR just adds try catch block with ErrorType = "client_exception".
ErryType = "unsuccessful_command" is defined wrapping exceptions in common repo under CommandResultUtil.
However, the existing "unsuccessful_command" has some difference from "client_exception" in my opinion. They can't replace each other.
"unsuccessful_command" is used in the appending test testEmptyRequestParametersToGenericErrorNotThrownException() and will be displayed in the sample app to user SubmitError(error= "unsuccessful_command").
We cannot use assertTrue((submitCodeResult as SubmitCodeError).error.equals(CommandResultUtil.UNSUCCESSFUL_COMMAND_ERROR)) instead of assertTrue((submitCodeResult as SubmitCodeError).error.equals(ErrorTypes.UNSUCCESSFUL_COMMAND_ERROR)) in the NativeAuthPublicClientApplicationKotlinTest
We can either remove testEmptyRequestParametersToGenericErrorNotThrownException() test or remove the introduction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offline discussion conclusion: Use direct string in the test file instead of adding new definition in the code base.

@Yuki-YuXin Yuki-YuXin merged commit 687c745 into dev May 14, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
msal No-Changelog This change does not update the changelog.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants