AzureAD / microsoft-authentication-library-for-objc

Microsoft Authentication Library (MSAL) for iOS and macOS
http://aka.ms/aadv2
MIT License
264 stars 141 forks source link

SSO Extension - acquireTokenSilentWithParameters Returning MSALErrorInteractionRequired When No Internet Connection #1839

Open LibertyDev18 opened 1 year ago

LibertyDev18 commented 1 year ago

In the SSO Extension context when a device has no internet connection and a call is made to acquireTokenSilentWithParameters when a token needs to be refreshed the API returns MSALErrorInteractionRequired instead of the expected NSURLErrorNotConnectedToInternet response code.

When the SSO Extension is not in play this same request returns the system level error.

Our understanding based on the documentation is that the system level error will be returned in this scenario and has an impact on what path we send users down within our apps. https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-error-handling-ios#error-handling-in-msal-for-iosmacos

We are seeing this behavior with MSAL 1.2.14 & 1.2.15 running on iOS 16 with 6.7.14 of Authenticator.

Thanks for your help in advance!

antonioalwan commented 1 year ago

Hi @LibertyDev18, thanks for your inquiry. Do you mind sharing the error Domain, Code, and possibly any logs. From testing, i see we return NSURLErrorDomain Code=-1009 "The internet connection appears to be offline with other network error details.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, for sure. Just to be clear, this ONLY occurs when we are using the SSO Extension. We get a more appropriate system level network error when the SSO Extension is not in play.

When we restore network access the acquireTokenSilentWithParameters call succeeds as expected.

Error Domain=MSALErrorDomain Code=-50002 "(null)" UserInfo={MSALErrorDescriptionKey=No token matching arguments found in the cache, user interaction is required, MSALCorrelationIDKey=...}

I can provide more in depth MSAL logs via a different channel but here is a small snippet that might be helpful.

MSAL 1.2.15 iOS 16.6 [2023-09-01 18:20:59 - [MSAL] Didn't find family refresh token MSAL 1.2.15 iOS 16.6 [2023-09-01 18:20:59 - Creating Error with description: No token matching arguments found in the cache, user interaction is required MSAL 1.2.15 iOS 16.6 [2023-09-01 18:20:59 - [MSAL] Silent flow finished. Result (null), error: -51115 error domain: MSIDErrorDomain MSAL 1.2.15 iOS 16.6 [2023-09-01 18:20:59 - [MSAL] Silent broker extension flow finished. Result (null), error: -51115 error domain: MSIDErrorDomain, shouldFallBack: 1

To recreate the issue. We first successfully get an access token with MSAL leveraging the SSO Extension. We "disconnect" the device from any valid network. After the access token expires and we attempt to refresh silently we get the interaction required response instead of the actual network error.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, just checking in to see if you have been able to find anything and/or confirm behavior? Thanks!

antonioalwan commented 1 year ago

Hi @LibertyDev18, Thanks for checking again. This is being validated and will update on the outcome soon

LibertyDev18 commented 1 year ago

@antonioalwan, bumping this for an update.

antonioalwan commented 1 year ago

@LibertyDev18 For silent flow, the logic works first by checking cached credentials before having any connection established. Once it is found that no cached credentials exist in cache for the user, the error is thrown to have an interactive request. Now, once interactive request is started, it detects the connection error and returns it. In most cases, there shouldn't be need to communicate with AAD if credentials are available in cache. So therefore, it makes sense not to check internet connection before checking the cache otherwise we would have an error for no reason.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, thank you for the additional information.

So, I guess my question here is more around the difference in behavior between a device with the SSO Extension enabled and one that does not. The behavior is NOT the same when attempting a silent auth flow.

My expectation would be that they should/would behave the same in both cases and looking to confirm if the difference is intentional?

LibertyDev18 commented 1 year ago

Hi @antonioalwan, curious if you were able to provide any additional insight to the question in my last post?

antonioalwan commented 1 year ago

Hi @LibertyDev18, Comparing silent flow for either SSO Extension and MSAL show same result as described above by checking the cache first and returning the interactive error when token. Were you able to compare that the error returned is different when no user has logged-in prior to acquireSilent using interactive flow for both cases?

LibertyDev18 commented 1 year ago

Hi @antonioalwan, yes, these are identical A/B tests where all that is happening is a token is being refreshed silently after a previous successful acquisition of a token when a network connection is present.

Exact same scenario, exact same use case, the only difference is SSO Extension. The result is not the same. Two different response codes are returned. In the non-SSO Extension flow a network error is returned and not interaction required.

The network error is what the MSAL documentation indicates should/will be returned in this case.

antonioalwan commented 1 year ago

Hi @LibertyDev18, There seems to be a difference between our setup as following the steps and applying identical A/B tests to refresh silently for both scenarios when we are using SSO extension and MSAL, the result was showing identical in both cases to 'No token matching arguments found in the cache, user interaction is required. Code= -50002'.

Note that the cache has to be cleared so that no tokens are found. If tokens are in the cache, they will be returned.

For interactive requests, it will always return Code 1009 for both SSO extension and MSAL when the internet connection is disconnected. Can I suggest writing down the steps in both scenarios and sharing the difference of the log?

LibertyDev18 commented 1 year ago

Hi @antonioalwan, I'll spin up a very quick & simple sample project that might help with this so we're working off the exact same code. Do you have a location that I can supply that to you directly?

I'll also document the exact steps used to reproduce both scenarios and supply that with the project.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, I've created a sample project that I can send over to you to run and see the results. The project has full MSAL logging enabled. Project has a min target of iOS 16.4 and is using MSAL 1.2.16.

The difference between the two devices is that one is a fully managed device with the SSO Extension enabled while the other is using MSAL only (no device management, no Authenticator, just MSAL).

Attempting to refresh, a previously acquired token, silently when the NO SSO EXTENSION device has no network connection results in the following error: 2023-09-21 13:36:52.411236-0500 MSAL Behavior Tester[38088:2659419] Task <065E26F8-5443-4131-A522-8F7C68E3B391>.<1> HTTP load failed, 0/0 bytes (error code: -1009 [1:50]) 2023-09-21 13:36:52.416974-0500 MSAL Behavior Tester[38088:2659420] Task <065E26F8-5443-4131-A522-8F7C68E3B391>.<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x2804187e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_NSURLErrorNWPathKey=unsatisfied (No network route), _kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <065E26F8-5443-4131-A522-8F7C68E3B391>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <065E26F8-5443-4131-A522-8F7C68E3B391>.<1>"

Attempting to refresh, a previously acquired token, silently when the SSO EXTENSION device has no network connection results in the following error: 2023-09-21 13:51:28.032677-0500 MSAL Behavior Tester[24471:1912172] MSAL Error: Error Domain=MSALErrorDomain Code=-50002 "(null)" UserInfo={MSALErrorDescriptionKey=No token matching arguments found in the cache, user interaction is required, MSALCorrelationIDKey=58E9EE30-5CE1-4E1B-9404-6B9F5F2BCFC8} ... 2023-09-21 13:51:28.052382-0500 MSAL Behavior Tester[24471:1912169] MSAL log: TID=1912172 MSAL 1.2.16 iOS 16.6.1 [2023-09-21 18:51:28 - 58E9EE30-5CE1-4E1B-9404-6B9F5F2BCFC8] [MSAL] acquireTokenSilent returning with error: (MSALErrorDomain, -50002) Masked(not-null)

antonioalwan commented 1 year ago

@LibertyDev18 I see, thanks for detailing the errors and resubmitting the steps. The difference seems related to expired cached tokens vs. cleared cached tokens. I need to run this scenario and check the code path.

antonioalwan commented 1 year ago

@LibertyDev18 You stated that you created a sample project. Do you mind sending it to antonioalwan@microsoft.com? Thanks!

LibertyDev18 commented 1 year ago

Hi @antonioalwan, sent the sample project over to you. Thanks!

antonioalwan commented 1 year ago

Hi @LibertyDev18, I validated this scenario and ran both cases where I saw the issue. However, in the SSO Extension case, the result shows a different error as interaction is required because no PRT exists in the cache. To achieve that, You need to log in using SSO extension with the same user prior to testing the second scenario. Once logged in, A Refresh token would be cached and then the error for interaction required won't be shown.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, thank you for looking into this further and the additional information. When we are testing the SSO extension path we are using the SSO extension to log the user in and secure the initial access token as the first app they install on the device directly through the test app. Are you saying we need to first log in directly to Authenticator first?

antonioalwan commented 1 year ago

Hi @LibertyDev18, For the first time when setting up SSO Extension, a test user can log in with a network connection. Once logged in, a PRT would be cached. The test user can validate that SSO works by logging in through the browser where SSO is validated by allowing to log in without prompting for credentials. The next step in testing, disconnect from the network and log in. PRT should be available and it shouldn't return the interaction required.

LibertyDev18 commented 1 year ago

Hi @antonioalwan, okay, so I'm not sure I'm following.

"For the first time when setting up SSO Extension, a test user can log in with a network connection. Once logged in, a PRT would be cached." This is exactly what step one of the test case is. We perform an interactive auth flow on the SSO Extension device via MSAL. Should this action not cache the PRT?

antonioalwan commented 1 year ago

@LibertyDev18 It should, can we verify that login from the browser works without reentering credentials after completing that step?

LibertyDev18 commented 1 year ago

Hi @antonioalwan, confirmed. I can navigate to a site in Safari that automatically logs the user in after step one in the test app. After thinking about it too, the PRT should be getting secured as part of the initial device enrollment and management.

For this test I:

antonioalwan commented 1 year ago

@LibertyDev18 thanks again for providing the detailed steps. Even though it is invoking SSO Extension this is still MSAL based. There are checks made for AT at MSAL level. The next step flow invokes the broker (SSOExt) and when the broker returns an error there are more checks to try using MSAL RT from cache to update AT. While it may sound simple this is a complex process and a change in this flow could satisfy certain scenarios but break another dependency. The approach we prefer you to take if this is necessary for your app is to have an error handler to check network connectivity in the scenario.