Skip to content

Commit 687c745

Browse files
authored
Add ClientException wrapper for native auth (#2080)
### 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
1 parent c207fcc commit 687c745

8 files changed

Lines changed: 1461 additions & 1291 deletions

File tree

msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt

Lines changed: 435 additions & 395 deletions
Large diffs are not rendered by default.

msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@
2323

2424
package com.microsoft.identity.nativeauth.statemachine.errors
2525

26+
import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
27+
import com.microsoft.identity.nativeauth.statemachine.results.GetAccountResult
2628
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResendCodeResult
2729
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordResult
2830
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordStartResult
2931
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitCodeResult
32+
import com.microsoft.identity.nativeauth.statemachine.results.ResetPasswordSubmitPasswordResult
3033
import com.microsoft.identity.nativeauth.statemachine.results.SignInResendCodeResult
3134
import com.microsoft.identity.nativeauth.statemachine.results.SignInResult
3235
import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitCodeResult
36+
import com.microsoft.identity.nativeauth.statemachine.results.SignInSubmitPasswordResult
37+
import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult
3338
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResendCodeResult
3439
import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
3540
import com.microsoft.identity.nativeauth.statemachine.results.SignUpSubmitAttributesResult
@@ -77,6 +82,8 @@ internal class ErrorTypes {
7782
* in state transitions. If this occurs, the flow should be restarted.
7883
*/
7984
const val INVALID_STATE = "invalid_state"
85+
86+
const val CLIENT_EXCEPTION = "client_exception"
8087
}
8188
}
8289

@@ -157,3 +164,21 @@ class ResendCodeError(
157164
override val errorCodes: List<Int>? = null,
158165
override var exception: Exception? = null
159166
): SignInResendCodeResult, SignUpResendCodeResult, ResetPasswordResendCodeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
167+
168+
class GetAccountError(
169+
override val errorType: String? = null,
170+
override val error: String? = null,
171+
override val errorMessage: String?,
172+
override val correlationId: String,
173+
override val errorCodes: List<Int>? = null,
174+
override var exception: Exception? = null
175+
): GetAccountResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
176+
177+
class SignOutError(
178+
override val errorType: String? = null,
179+
override val error: String? = null,
180+
override val errorMessage: String?,
181+
override val correlationId: String,
182+
override val errorCodes: List<Int>? = null,
183+
override var exception: Exception? = null
184+
): SignOutResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)

msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt

Lines changed: 127 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ import com.microsoft.identity.common.nativeauth.internal.commands.AcquireTokenNo
5050
import com.microsoft.identity.common.nativeauth.internal.controllers.NativeAuthMsalController
5151
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication
5252
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration
53+
import com.microsoft.identity.nativeauth.statemachine.errors.ErrorTypes
5354
import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenError
5455
import com.microsoft.identity.nativeauth.statemachine.errors.GetAccessTokenErrorTypes
56+
import com.microsoft.identity.nativeauth.statemachine.errors.SignOutError
5557
import com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult
5658
import com.microsoft.identity.nativeauth.statemachine.results.SignOutResult
5759
import com.microsoft.identity.nativeauth.utils.serializable
@@ -103,62 +105,73 @@ class AccountState private constructor(
103105
*/
104106
suspend fun signOut(): SignOutResult {
105107
return withContext(Dispatchers.IO) {
106-
LogSession.logMethodCall(
107-
tag = TAG,
108-
correlationId = null,
109-
methodName = "$TAG.signOut.withContext"
110-
)
111-
112-
val account: IAccount =
113-
NativeAuthPublicClientApplication.getCurrentAccountInternal(config)
114-
?: throw MsalClientException(
115-
MsalClientException.NO_CURRENT_ACCOUNT,
116-
MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE
117-
)
118-
119-
val requestAccountRecord = AccountRecord()
120-
requestAccountRecord.environment = (account as Account).environment
121-
requestAccountRecord.homeAccountId = account.homeAccountId
122-
123-
val params = CommandParametersAdapter.createRemoveAccountCommandParameters(
124-
config,
125-
config.oAuth2TokenCache,
126-
requestAccountRecord
127-
)
128-
129-
val removeCurrentAccountCommandParameters = RemoveCurrentAccountCommand(
130-
params,
131-
LocalMSALController().asControllerFactory(),
132-
object : CommandCallback<Boolean?, BaseException?> {
133-
override fun onError(error: BaseException?) {
134-
// Do nothing, handled by CommandDispatcher.submitSilentReturningFuture()
135-
}
136-
137-
override fun onTaskCompleted(result: Boolean?) {
138-
// Do nothing, handled by CommandDispatcher.submitSilentReturningFuture()
139-
}
108+
try {
109+
LogSession.logMethodCall(
110+
tag = TAG,
111+
correlationId = null,
112+
methodName = "$TAG.signOut.withContext"
113+
)
140114

141-
override fun onCancel() {
142-
// Do nothing
143-
}
144-
},
145-
PublicApiId.NATIVE_AUTH_ACCOUNT_SIGN_OUT
146-
)
115+
val account: IAccount =
116+
NativeAuthPublicClientApplication.getCurrentAccountInternal(config)
117+
?: throw MsalClientException(
118+
MsalClientException.NO_CURRENT_ACCOUNT,
119+
MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE
120+
)
121+
122+
val requestAccountRecord = AccountRecord()
123+
requestAccountRecord.environment = (account as Account).environment
124+
requestAccountRecord.homeAccountId = account.homeAccountId
125+
126+
val params = CommandParametersAdapter.createRemoveAccountCommandParameters(
127+
config,
128+
config.oAuth2TokenCache,
129+
requestAccountRecord
130+
)
147131

148-
val result = CommandDispatcher.submitSilentReturningFuture(removeCurrentAccountCommandParameters)
149-
.get().result as Boolean
132+
val removeCurrentAccountCommandParameters = RemoveCurrentAccountCommand(
133+
params,
134+
LocalMSALController().asControllerFactory(),
135+
object : CommandCallback<Boolean?, BaseException?> {
136+
override fun onError(error: BaseException?) {
137+
// Do nothing, handled by CommandDispatcher.submitSilentReturningFuture()
138+
}
139+
140+
override fun onTaskCompleted(result: Boolean?) {
141+
// Do nothing, handled by CommandDispatcher.submitSilentReturningFuture()
142+
}
143+
144+
override fun onCancel() {
145+
// Do nothing
146+
}
147+
},
148+
PublicApiId.NATIVE_AUTH_ACCOUNT_SIGN_OUT
149+
)
150150

151-
return@withContext if (result) {
152-
SignOutResult.Complete
153-
} else {
154-
Logger.error(
155-
TAG,
156-
"Unexpected error during signOut.",
157-
null
151+
val result = CommandDispatcher.submitSilentReturningFuture(
152+
removeCurrentAccountCommandParameters
158153
)
159-
throw MsalClientException(
160-
MsalClientException.UNKNOWN_ERROR,
161-
"Unexpected error during signOut."
154+
.get().result as Boolean
155+
156+
return@withContext if (result) {
157+
SignOutResult.Complete
158+
} else {
159+
Logger.error(
160+
TAG,
161+
"Unexpected error during signOut.",
162+
null
163+
)
164+
throw MsalClientException(
165+
MsalClientException.UNKNOWN_ERROR,
166+
"Unexpected error during signOut."
167+
)
168+
}
169+
} catch (e: Exception) {
170+
SignOutError(
171+
errorType = ErrorTypes.CLIENT_EXCEPTION,
172+
errorMessage = "MSAL client exception occurred in signOut.",
173+
exception = e,
174+
correlationId = correlationId
162175
)
163176
}
164177
}
@@ -233,62 +246,75 @@ class AccountState private constructor(
233246
methodName = "$TAG.getAccessToken(forceRefresh: Boolean)"
234247
)
235248
return withContext(Dispatchers.IO) {
236-
val currentAccount =
237-
NativeAuthPublicClientApplication.getCurrentAccountInternal(config) as? Account
238-
?: return@withContext GetAccessTokenError(
239-
errorType = GetAccessTokenErrorTypes.NO_ACCOUNT_FOUND,
240-
error = MsalClientException.NO_CURRENT_ACCOUNT,
241-
errorMessage = MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE,
242-
correlationId = "UNSET"
243-
)
249+
try {
250+
val currentAccount =
251+
NativeAuthPublicClientApplication.getCurrentAccountInternal(config) as? Account
252+
?: return@withContext GetAccessTokenError(
253+
errorType = GetAccessTokenErrorTypes.NO_ACCOUNT_FOUND,
254+
error = MsalClientException.NO_CURRENT_ACCOUNT,
255+
errorMessage = MsalClientException.NO_CURRENT_ACCOUNT_ERROR_MESSAGE,
256+
correlationId = "UNSET"
257+
)
258+
259+
val acquireTokenSilentParameters = AcquireTokenSilentParameters.Builder()
260+
.forAccount(currentAccount)
261+
.fromAuthority(currentAccount.authority)
262+
.build()
263+
264+
val accountToBeUsed = PublicClientApplication.selectAccountRecordForTokenRequest(
265+
config,
266+
acquireTokenSilentParameters
267+
)
244268

245-
val acquireTokenSilentParameters = AcquireTokenSilentParameters.Builder()
246-
.forAccount(currentAccount)
247-
.fromAuthority(currentAccount.authority)
248-
.build()
269+
val params =
270+
CommandParametersAdapter.createAcquireTokenNoFixedScopesCommandParameters(
271+
config,
272+
config.oAuth2TokenCache,
273+
accountToBeUsed,
274+
forceRefresh,
275+
correlationId
276+
)
249277

250-
val accountToBeUsed = PublicClientApplication.selectAccountRecordForTokenRequest(
251-
config,
252-
acquireTokenSilentParameters
253-
)
278+
val command = AcquireTokenNoFixedScopesCommand(
279+
params,
280+
NativeAuthMsalController(),
281+
PublicApiId.NATIVE_AUTH_ACCOUNT_GET_ACCESS_TOKEN
282+
)
254283

255-
val params = CommandParametersAdapter.createAcquireTokenNoFixedScopesCommandParameters(
256-
config,
257-
config.oAuth2TokenCache,
258-
accountToBeUsed,
259-
forceRefresh,
260-
correlationId
261-
)
284+
val commandResult = CommandDispatcher.submitSilentReturningFuture(command)
285+
.get().result
262286

263-
val command = AcquireTokenNoFixedScopesCommand(
264-
params,
265-
NativeAuthMsalController(),
266-
PublicApiId.NATIVE_AUTH_ACCOUNT_GET_ACCESS_TOKEN
267-
)
287+
return@withContext when (commandResult) {
288+
is ServiceException -> {
289+
GetAccessTokenError(
290+
exception = ExceptionAdapter.convertToNativeAuthException(commandResult),
291+
correlationId = "UNSET"
292+
)
293+
}
268294

269-
val commandResult = CommandDispatcher.submitSilentReturningFuture(command)
270-
.get().result
295+
is Exception -> {
296+
GetAccessTokenError(
297+
exception = commandResult,
298+
correlationId = "UNSET"
299+
)
300+
}
271301

272-
return@withContext when (commandResult) {
273-
is ServiceException -> {
274-
GetAccessTokenError(
275-
exception = ExceptionAdapter.convertToNativeAuthException(commandResult),
276-
correlationId = "UNSET"
277-
)
278-
}
279-
is Exception -> {
280-
GetAccessTokenError(
281-
exception = commandResult,
282-
correlationId = "UNSET"
283-
)
284-
}
285-
else -> {
286-
// Account and Id token data could change after access token refresh, update the account object in the state
287-
account = AuthenticationResultAdapter.adapt(commandResult as ILocalAuthenticationResult).account
288-
GetAccessTokenResult.Complete(
289-
resultValue = AuthenticationResultAdapter.adapt(commandResult)
290-
)
302+
else -> {
303+
// Account and Id token data could change after access token refresh, update the account object in the state
304+
account =
305+
AuthenticationResultAdapter.adapt(commandResult as ILocalAuthenticationResult).account
306+
GetAccessTokenResult.Complete(
307+
resultValue = AuthenticationResultAdapter.adapt(commandResult)
308+
)
309+
}
291310
}
311+
} catch (e: Exception) {
312+
GetAccessTokenError(
313+
errorType = ErrorTypes.CLIENT_EXCEPTION,
314+
errorMessage = "MSAL client exception occurred in getAccessToken.",
315+
exception = e,
316+
correlationId = correlationId
317+
)
292318
}
293319
}
294320
}

0 commit comments

Comments
 (0)