Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds some wrapper methods that allow callers to write idiomatic Kotlin code when using this library.
Examples of the usage differences are highlighted below.
Creating a
PublicClientApplicationcan become a suspending function
scope.launch { try { val app = PublicClientApplicationKtx.createSingleAccountPublicClientApplication( context = context, configFileResourceId = R.raw.auth_config_single_account, ) // Use application here } catch (exception: Exception) { showException(exception) } }The same thing applies to
createMultipleAccountPublicClientApplication.[Single Account] Signing in
Usage of builders is not as common in Kotlin unless they need to perform complex operations. In this case, the builder for
SignInParametersmerely sets values, so we can just usesignIndirectly with named parameters while still keeping the proper defaults for the other unspecified ones.app.signIn( activity = activity, scopes = scopes, callback = object : AuthenticationCallback { override fun onSuccess(authenticationResult: IAuthenticationResult) { // Use result here } override fun onError(exception: MsalException) { showException(exception) } override fun onCancel() { } }, )Furthermore, this can also become a suspending function
scope.launch { try { val authResult = app.signIn( activity = activity, scopes = scopes, ) // Use result here } catch (exception: MsalException) { showException(exception) } }In this scenario,
authResultwould benullin theonCancelcase.I think this is a reasonable default for a lot of applications, however, when user cancellation needs to be handled explicitly, developers can still opt to use the callback.
[Single Account] Signing out
can become a suspending function
scope.launch { try { app.signOutSuspend() // handle cleanup } catch (exception: MsalException) { showException(exception) } }[Single Account] Get signed-in account
can become a suspending function
scope.launch { try { val account = app.getCurrentAccountSuspend() // use account here } catch (exception: MsalException) { showException(exception) } }In this scenario,
onAccountChangedis ignored, but again, if an application needs to handle that in a special way, it can still use the callback.[Multiple Account] Get signed-in accounts
can become a suspending function
scope.launch { try { val accounts = app.getAccountsSuspend() // use accounts here } catch (exception: MsalException) { showException(exception) } }[Multiple Account] Remove account
can become a suspending function
scope.launch { try { app.removeAccountSuspend(account) // handle cleanup } catch (exception: MsalException) { showException(exception) } }Acquire a token
can become a suspending function
We can also get rid of the builder here as well.
The only builder method that has multiple overloads is
fromAuthority, which we can replace by having a separateAuthorityclass with multiple constructors.scope.launch { try { app.acquireTokenSuspend( activity = activity, scopes = scopes, account = account, authority = Authority.from(<any of the overloads>), ) // use result here } catch (exception: MsalException) { showException(exception) } }The same thing applies to
acquireTokenSilent.Throughout these examples I've added
scope.launchand error handling for completeness, but in practice the difference is even more noticeable because often these functions will be called from another suspend function so you don't need to launch a new coroutine scope, or you may be handling errors at a higher level so you don't need to handle every one separately.