Skip to content

Commit

Permalink
Add ClientException wrapper for native auth (#2080)
Browse files Browse the repository at this point in the history
### Changes summary:
1. Add GetAccountError and SignOutError error in Error.kt
2. Use try catch block with corresponding errors in interface methods.
3. Add testEmptyRequestParametersToGenericErrorNotThrownException()

### Company PRs:
native sample app:
Azure-Samples/ms-identity-ciam-native-auth-android-sample#24
  • Loading branch information
Yuki-YuXin committed May 14, 2024
1 parent c207fcc commit 687c745
Show file tree
Hide file tree
Showing 8 changed files with 1,461 additions and 1,291 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
}
}

Expand Down Expand Up @@ -157,3 +164,21 @@ class ResendCodeError(
override val errorCodes: List<Int>? = 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<Int>? = 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<Int>? = null,
override var exception: Exception? = null
): SignOutResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Boolean?, BaseException?> {
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<Boolean?, BaseException?> {
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
)
}
}
Expand Down Expand Up @@ -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
)
}
}
}
Expand Down
Loading

0 comments on commit 687c745

Please sign in to comment.