问题
I am using ADALiOS to access OneDrive for business service, and I use below snippet to authenticate a user after account disambiguation,
ADAuthenticationContext *context = ....
[context acquireTokenWithResource:resource
clientId:clientId
redirectUri:redirectUri
promptBehavior:AD_PROMPT_ALWAYS
userId:@"userA@mydomain.com"
extraQueryParameters:@""
completionBlock:^(ADAuthenticationResult *result) {
...
}
The web authentication UI will prompt, and allow user to input credential to continue. The issue here is, in this page, user can also change the user id to "userB@mydomain.com", and completion block will be invoked with a error like "Different user was authenticated. Expected userA@mydomain.com, actual userB@mydomain.com". Am I using ADALiOS wrong?
回答1:
When you specify a userId, ADAL assumes that it was specified because that was truly the user that you wanted. If the resulting user is not the one that was asked for, then you get the error you are seeing. If you don't care which user is authenticated then you can pass nil as the userId parameter and you won't get the error.
The ADAL library does this out of an over abundance of caution. Despite what the userId parameter implies, when you call ADAL there is no way for ADAL to guarantee that you will get a token for the user that you asked for. It will do it's best. If it can find a token for that user in the cache then it will return it. However, as you've seen, if the interactive flow is invoked, nothing prevents the user from entering a different username than the one that was asked for. In that case, a token for a different user will be returned. What happens if the app has associated some UI elements or private resources with a particular userId? If it get's a token for a different user than was asked for, then it might be mixing users in a way that is not obvious to the user of the app. If the user originally signed in as a low privilege user, but subsequently signed in as an admin, and the app doesn't notice this, then bad things may happen. So, the library assumes that if you asked for a specific user, then that is the only user that is acceptable.
What follows is probably more detail than you need but just to be complete I will continue. The userId parameter is unfortunately overloaded for three different purposes.
As a cache lookup key. If ADAL has previously authenticated the passed user, and has a token or refresh token for that user in the cache, then it can look up that token directly and avoid any need for an AAD request. If you only ever authenticate a single user at a time, then this doesn't add any value as ADAL will attempt to find a token valid for the resource that was passed.
As a login hint to the server. The userId is used to prefill the username field as a convenience to the user. However, the user is free to erase that username and provide a different one.
As a hint for home realm discovery. If the user is a federated user, meaning that AAD needs to refer to ADFS (or some other federation server) for authentication, it needs a username to determine the address of the server to which it should refer. Normally this is done via a two step process. The user first lands on an AAD page and types their username. As soon as the username field loses focus the server looks up the username, and if it is a federated user, it starts redirecting them to their ADFS server. Finally, the user lands on the ADFS login page and they find their username already filled in. They enter their password and authentication completes. However, if you pass a userId parameter its value is passed to AAD. As a result, AAD no longer needs to wait for the user to type their username and can send them directly to their federation server, eliminating one page, and allowing the user to go directly to signing in.
If you need 2 or 3, but you don't care about 1 there is a workaround. You can specify nil for the userId but add "login_hint="username" as the extraQueryParameters parameter. Replace "username" with the username you would have passed in the userId parameter. If you do this, then ADAL will be oblivious to the user you have asked for, but AAD will interpret the username as a login hint to prefill the username field, and as a home realm discovery hint. The check to ensure that the user asked for is the user returned will be bypassed. You need to be very aware that you may not get a token for the user you provided as the login_hint. You should ensure that you verify the user before making any assumptions about who they are or what they might have access to.
回答2:
@Ryan Pangrle is right.
The above work around may not work properly anymore if Microsoft Authenticator is installed.
ADAL has been providing the following API to solve this issue since ADAL 2.1+ :
- (void)acquireTokenWithResource:(NSString*)resource
clientId:(NSString*)clientId
redirectUri:(NSURL*)redirectUri
promptBehavior:(ADPromptBehavior)promptBehavior
userIdentifier:(ADUserIdentifier*)userId
extraQueryParameters:(NSString*)queryParams
completionBlock:(ADAuthenticationCallback)completionBlock;
where developers can set the UPN match to be optional in the (ADUserIdentifier*)userId parameter.
来源:https://stackoverflow.com/questions/26694432/adal-ios-different-user-was-authenticated-expected-useramydomain-com-actual