-
Notifications
You must be signed in to change notification settings - Fork 126
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
Support for multiple tokens in native auth flows #2082
Conversation
msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java
Outdated
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
@@ -58,6 +60,7 @@ import com.microsoft.identity.nativeauth.utils.serializable | |||
import kotlinx.coroutines.Dispatchers | |||
import kotlinx.coroutines.launch | |||
import kotlinx.coroutines.withContext | |||
import java.util.Collections | |||
|
|||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in the technical design document review, the documentation in comments needs to be updated to explain the difference between these two methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comments updated
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
return getAccessTokenInternal(forceRefresh, ArrayList(scopeSet2)) | ||
} | ||
|
||
suspend fun getAccessTokenInternal(forceRefresh: Boolean, scopes: List<String>?): GetAccessTokenResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should scopes
be nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, the scopes are null from the old deprecated method.
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
...c/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
...c/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt
Outdated
Show resolved
Hide resolved
...c/test/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationKotlinTest.kt
Outdated
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
* | ||
* @return [com.microsoft.identity.nativeauth.statemachine.results.GetAccessTokenResult] The result of the getAccessToken action | ||
*/ | ||
suspend fun getAccessToken(forceRefresh: Boolean = false, scopes: List<String>?): GetAccessTokenResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the scopes list should not be optional here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making it null allows the developers to move to this function from the old deprecated method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nilo-ms can you confirm what should happen here? Is scopes optional, or not?
- in current MSAL, it's not optional (i.e. if an empty scopes list is passed, the method returns an error)
- if we allow empty/null scopes param, how do we communicate to developers how this method works in those scenarios?
- how is iOS implementing this for native auth?
- I agree with Saurabh that developers need a way to migrate from the old, now-deprecated method to the new one. If the old one doesn't take a scopes parameter, then the new one needs to have the possibility of also not passing any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think native auth should work in the same way as the platform specific MSAL works. For example, for MSAL Objective-C, the scope list is not optional, but the external developer can specify an empty list of scopes, no error is thrown. This is how we plan to implement it.
Given that you want to provide a way to migrate to the new method, I suggest to slightly deviate from how MSAL Android work. I propose:
- adding the scope list as mandatory
- If an empty scope list is passed by the external developer, add the default OIDC scopes internally (as we do for the signIn)
- clarify this behaviour in the method comment
Be mindful that if you want to align with the MSAL Android behaviour, it will be considered a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have made the parameter as NonNull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SaurabhMSFT can you clarify which approach you're taking:
- conceptually, scopes are optional. Similar to how getAccessToken() behaves right now.
- scopes are not optional. Similar to how MSAL browser-delegated behaves right now.
If we're going with 1, then the following should be implemented IMO:
- scopes param may be
null
. - scopes list may be empty.
As these two are effectively the same, I don't see a reason to limit one but not the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scopes are optional. An empty list of scopes or a null value could show the same intent. Supporting one or the other is just a matter of taste and consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree @SaurabhMSFT. If scopes are optional, the whole parameter should be nullable. It's a pain for developers to have to create an empty list, only because they can't pas null
. both null
and empty list serve the same functional purpose, so both should be allowed.
@@ -98,6 +100,19 @@ class AccountState private constructor( | |||
} | |||
} | |||
|
|||
private fun addDefaultScopes(scopes: List<String>?): List<String> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this done here, rather than inside the controller? The latter is what MSAL is doing currently, and wherever possible I would like to stick to existing designs/architectures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function wasn't used and has been removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not the question. I can see default scopes are added in the interface layer (in getAccessTokenInternal()
), rather than inside the controller. Why not the latter, and align with how MSAL's browser-delegated flow operates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the code in getAccessMethod so that empty scopes are not passed to getAccessTokenInternal
obviating the need to merge scopes.
@@ -194,14 +209,16 @@ class AccountState private constructor( | |||
interface GetAccessTokenCallback : Callback<GetAccessTokenResult> | |||
|
|||
/** | |||
* Retrieves the access token for the currently signed in account from the cache. | |||
* Retrieves the access token for the currently signed in account from the cache. If multiple |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment doesn't make sense, as this method doesn't accept a scopes parameter.
Also, shouldn't this method be returning a token for the default scopes? If so, that should be made clear from the comments.
@nilo-ms can you review this based on your technical proposal please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest adding a comment like:
Retrieves an access token with default OIDC scopes as permission
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on Teams, please propose how these changes should be tested by reviewers, before this is merged in.
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/GetAccessTokenError.kt
Show resolved
Hide resolved
* The INVALID_SCOPES value indicates the scopes provided by the user are not valid | ||
* If this occurs, valid scopes should be resubmitted | ||
*/ | ||
const val INVALID_SCOPES = "invalid_scopes" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a shared/common error, right? If so and if you agree with my other comment, please move this out of the common error class and into the one that's specific for this scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not shared now but should be as we should return similar error from signin method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should return similar error from signin method
what makes you say that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have moved the variable to GetAccessTokenErrorTypes
class
|
||
assertTrue(accessTokenState is GetAccessTokenError) | ||
assertTrue((accessTokenState as GetAccessTokenError).isInvalidScopes()) | ||
assertEquals((accessTokenState as GetAccessTokenError).errorType, ErrorTypes.INVALID_SCOPES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please update this to use the utility method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a call to utility method on line 770. Are you looking for some other utility method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah sorry, missed that. We don't need line 271, IMO, but nit.
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Outdated
Show resolved
Hide resolved
msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/AccountState.kt
Show resolved
Hide resolved
) | ||
} | ||
|
||
is Exception -> { | ||
GetAccessTokenError( | ||
exception = commandResult, | ||
correlationId = "UNSET" | ||
correlationId = correlationId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should actually be using commandResult.correlationid
here, for the unlikely case that the API has returned a different correlation ID. That field might be null, so we'd need to update GetAccessTokenError
. And that improvement would have to be applied to more places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change can be done when commandResult is ServiceException
if commandResult is Exception
then we will use the old correlationId.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to move this into a separate PR. It's not blocking.
This PR and its companion PR (AzureAD/microsoft-authentication-library-for-android#2082) enables multiple access token support for Native Auth. A pair of new method have been added in the AccountState class to allow developer to retrieve different access tokens according to the requested scopes. The way these methods acquire tokens is exactly as same as the one in MSAL. The old method 'getAccessToken''s functionality hasn't been changed. It still returns the access token matching the default OIDC scope. This method though has been deprecated. Two classes that have become unused have been removed.
This PR and its companion PR (AzureAD/microsoft-authentication-library-common-for-android#2390) enables multiple access token support for Native Auth. A pair of new method have been added in the AccountState class to allow developer to retrieve different access tokens according to the requested scopes. The way these methods acquire tokens is exactly as same as the one in MSAL. The old method 'getAccessToken''s functionality hasn't been changed. It still returns the access token matching the default OIDC scope. This method though has been deprecated.