Skip to content
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

[iOS] [issue] For Outlook personal accounts (imap) - Not expired silent access token, requested from background, is loosing lease/access to IMAP 1h after application start (beyond that 1h - background requested tokens do not work). #2334

Open
Be-Maps opened this issue Sep 11, 2024 · 7 comments
Assignees
Labels

Comments

@Be-Maps
Copy link

Be-Maps commented Sep 11, 2024

I guess there should be an MSAL log entry with something for offline access (which I have in Azure in delegated permissions):
image

MSAL requests by default "openid profile offline_access" scopes. I was reading openid does not work for personal accounts - and maybe this makes it fail and not requests the offline_access.

I am not sure if my conclusions here are correct, but below is the behaviour I am seeing.

For delegated permissions for personal account I use scopes:
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"

I was trying to pass to MSAL offline_access in various ways , but MSAL does not take it, and I get error:
//"offline_access",
//"https://outlook.office.com/offline_access",
//"https://graph.microsoft.com/offline_access",
//"https://graph.microsoft.com/.default",
//".default",

In general I was using examples, and all started working until 1h passes:
https://cocoapods.org/pods/MSAL
https://learn.microsoft.com/de-de/graph/tutorials/ios-swift?tutorial-step=3

According to this Microsoft specification it should be possible to get access tokens and refresh tokens with silent token request:
https://learn.microsoft.com/pt-br/entra/identity-platform/v2-oauth2-auth-code-flow

Based on the articles below offline_access must be requested for mobile applications and possibly due to lack of that any tokens will be valid only for 1h from application start because offline_access was not requested, and because than refresh token is only granted 1h lifetime:
https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth
https://learn.microsoft.com/en-gb/entra/identity-platform/access-tokens

I can only see in the MSAL log following requested and returned (for interactive and silent requests):

MSAL log: TID=20301803 MSAL 1.3.2 iOS 17.5.1 [2024-09-09 16:37:48 - F2CC8655-27DC-4A1F-9772-BDF5B4B82013] [MSAL] -[MSALPublicClientApplication acquireTokenSilentForScopes:(
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"
)
account:Masked(not-null)
authority:<MSALAADAuthority: 0x301a1d060>
validateAuthority:Yes
forceRefresh:Yes
correlationId:(null)
capabilities:(null)
claimsRequest:(null)]

...
...
MSAL log: TID=20301801 MSAL 1.3.2 iOS 17.5.1 [2024-09-09 16:37:49 - F2CC8655-27DC-4A1F-9772-BDF5B4B82013] [MSAL] Silent flow finished. Result (not-null), error: 0 error domain: (null)
MSAL log: TID=20301801 MSAL 1.3.2 iOS 17.5.1 [2024-09-09 16:37:49] Parsing result access token
MSAL log: TID=20301801 MSAL 1.3.2 iOS 17.5.1 [2024-09-09 16:37:49 - F2CC8655-27DC-4A1F-9772-BDF5B4B82013] [MSAL] acquireTokenSilent returning with at: a754306 scopes:(
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"
) expiration:2024-09-09 17:37:49 +0000

Result:

  • After 1hour I am still getting tokens, but they are not allowing to IMAP login.
  • When I force refresh next token, valid longer than the 1h from app start, it works for IMAP until the 1h and stops to work even when it has 40min lifetime left.
  • tested it also from thread on background - strangely I get the token but it does not work at all (from beginning)

NOTE: Above is in debug mode - I guess debug mode does not limit the lifetime of tokens?

I also described the problem in: #2325

Please help.

@Be-Maps
Copy link
Author

Be-Maps commented Sep 12, 2024

Strange, when I request DEFAULT scope only:
["https://graph.microsoft.com/.default"] // now I have only this value in scopes
I get the token like below (with other scopes for IMAP & SMTP calls, but I do not see "offline_access" scope in the response):
MSAL log: TID=267018 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 09:11:56 - EFAAB961-C878-4C84-BA24-6D70641C4B03] [MSAL] Silent flow finished. Result (not-null), error: 0 error domain: (null)
MSAL log: TID=267018 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 09:11:56] Parsing result access token
MSAL log: TID=267018 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 09:11:56 - EFAAB961-C878-4C84-BA24-6D70641C4B03] [MSAL] acquireTokenSilent returning with at: 014600e scopes:(
"https://graph.microsoft.com/.default",
openid,
profile,
"https://graph.microsoft.com/IMAP.AccessAsUser.All",
"https://graph.microsoft.com/SMTP.Send"
) expiration:2024-09-12 10:06:58 +0000
2024-09-12 10:11:56 SettingsDialog : : acquireTokenSilently: Access token expiery is: 2024-09-12 11:06:58
2024-09-12 10:11:56 SettingsDialog : : acquireTokenSilently: Refreshed Access token is:

........

But For personal outlook account I need slightly different with "outlook.office.com" (So above token does not work for private outlook emails):
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"

Do you know why MSAL works this way?

@Be-Maps
Copy link
Author

Be-Maps commented Sep 12, 2024

Different test:
When I request only one scope, this way:
["https://graph.microsoft.com/.default"]

Now I get token with following scopes:
MSAL log: TID=369728 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 15:04:34 - 5F269A48-7F1D-4B1B-9808-AD8ED672045F] [MSAL] Saved token response successfully.
MSAL log: TID=369728 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 15:04:34] Stop background task with type 1 & taskId : 10
MSAL log: TID=369728 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 15:04:34 - 5F269A48-7F1D-4B1B-9808-AD8ED672045F] [MSAL] Silent flow finished. Result (not-null), error: 0 error domain: (null)
MSAL log: TID=369728 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 15:04:34] Parsing result access token
MSAL log: TID=369728 MSAL 1.3.2 iOS 17.5.1 [2024-09-12 15:04:34 - 5F269A48-7F1D-4B1B-9808-AD8ED672045F] [MSAL] acquireTokenSilent returning with at: 8c1b336 scopes:(
"https://graph.microsoft.com/.default",
openid,
profile,
"https://graph.microsoft.com/IMAP.AccessAsUser.All",
"https://graph.microsoft.com/SMTP.Send"
) expiration:2024-09-12 16:04:34 +0000
2024-09-12 16:04:34 AppDelegate : : acquireTokenSilently: Access Token is:

But this is as per Microsoft documents that scopes with "graph.microsoft.com" will not work for Private email accounts. They have to be:
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send"

Also for Private email accounts Microsoft documentation says (so we need that offline_access in the token request):
image

@ameyapat ameyapat self-assigned this Sep 12, 2024
@ameyapat
Copy link
Contributor

Hi @Be-Maps , offline_access is a scope as defined in openid connect protocol. for v2.0 endpoints, it must be requested explicitly. For more details about it and how long the resulting access token obtained is please refer https://learn.microsoft.com/en-us/entra/identity-platform/scopes-oidc#the-offline_access-scope

To clarify, can you consolidate the questions that you have so that I can try to answer them accordingly? Thanks

@Be-Maps
Copy link
Author

Be-Maps commented Sep 13, 2024

Hi Patil,
Thank You for helping on this one ...

Please note (as this might be important) I am using outlook.office.com FQDN (as only this one gives working token for Outlook Private Personal Accounts), not graph.microsoft.com or not *office365.com;
The problem is - in the first hour from app start I am able to get emails with FQDN outlook.office.com for IMAP scope (After 1h I am able to refresh tokens silently , but they do not work).

--------------------- observation 1 ---------------

It says in some of the AzureAD posts MSAL is requesting offline_access by default, and for example when I request token with scope offline_access (interactive or silent) I get following error:
MSAL log: TID=7009 MSAL 1.3.2 iOS 17.5.1 [2024-09-13 16:35:14] Requiring default broker type due to app being built with iOS 13 SDK
MSAL log: TID=7009 MSAL 1.3.2 iOS 17.5.1 [2024-09-13 16:35:14] Encountered error with code -51112, description Masked(not-null)
MSAL log: TID=7009 MSAL 1.3.2 iOS 17.5.1 [2024-09-13 16:35:14] acquireToken returning with error: (MSALErrorDomain, -50000) Masked(not-null)
2024-09-13 17:35:14 SettingsDialog : : acquireTokenInteractively: Could not acquire token: Error Domain=MSALErrorDomain Code=-50000 "(null)" UserInfo={MSALErrorDescriptionKey={(
openid,
profile,
"offline_access"
)} are reserved scopes and may not be specified in the acquire token call., MSALInternalErrorCodeKey=-42000}


I was requesting above with following scopes (which give above error):
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send",
"offline_access"


When I remove offline_access from scopes (I keep IMAP & SMTP) I get a token which works for one hour, but next refreshed token does not work. So I stop the app.
Now when I start the app again, I get a token in a silent way, which works for 1h, next silent token will not work longer than 1h from app start (I tried to request the next one before and after 1h, when second token was requested before 1h from app start it will loose the IMAP lease at the time of 1h from app start - that token is not expired, it just does not have the IMAP lease).

Q1: Why I cannot add explicitly "offline_access" , and how to do this if it is possible? - Some of the posts on AzureAD say it is added by default and it is done by scope: "https://outlook.office.com/.default" (So I tried, but it did not help for outlook.office.com FQDN, but helps for the other one - "https://graph.microsoft.com/.default")

Q2: Why the second silent token does not work / or stops working after 1h from app start? - this might be a bug as a first silent token after the app start works (so it looks the refresh token woks OK only for the first silent token requested by the app, not properly working for the second one which will loose the IMAP Lease after 1h from app start)

Q2a: What could be responsible for the IMAP Lease up to 1h from the app start? as I am getting valid tokens, but they do not work after 1h from the app start.

Q2b: How the first Silent request via MSAL is different to the second one (same place in code on my end)? - It looks like the first get of silent token gives the IMAP lease for 1h. Why the next silent token does not get the IMAP Lease extended according to its expiry?

---------------------- observation 2 ---------------

I did just for a test when I request token from graph FQDN with scopes like below (this token will not work for Private Personal accounts as will not get the IMAP Lease for private account, but just a test):
["https://graph.microsoft.com/.default"]

I get a token with the DEFAULT scopes:
[MSAL] acquireTokenSilent returning with at: 8c1b336 scopes:(
"https://graph.microsoft.com/.default",
openid,
profile,
"https://graph.microsoft.com/IMAP.AccessAsUser.All",
"https://graph.microsoft.com/SMTP.Send"
) expiration:2024-09-12 16:04:34 +0000
2024-09-12 16:04:34 AppDelegate : : acquireTokenSilently: Access Token is: .........

So MSAL is adding these "MSAL restricted / Default " scopes for graph FQDN.

Q3: Is this a defect on MSAL end , as MSAL is not adding "offline_access" as default for outlook.office.com FQDN? or how should I request "offline_access", because when I add MSAL says "error-reserved" as one of the default scopes, MSAL error is - "... reserved scopes and may not be specified in the acquire token call".

Q4: If I am doing something wrong please advise. Or if needed maybe we may have a Skype call and I will share the screen to show the behaviour and logs for various scenarios?

---------------------- observation 3 ---------------

In comparison, in MSAL for Android, silent tokens work for few hours after refreshes, with the below scopes:
"https://outlook.office.com/IMAP.AccessAsUser.All",
"https://outlook.office.com/SMTP.Send",
So the problem I see is only on iOS MSAL (in my case XCode/Swift).

@willDTBM
Copy link

I have the same issue on iOS (valid token stops working - after 1h from app start - when requested from background location), did you manage to sort this out?
Equivalent code on android works all the time with no problems. Maybe this is a defect on iOS MSAL?
I will be grateful for any guidance.

@Be-Maps
Copy link
Author

Be-Maps commented Sep 19, 2024

@willDTBM - I was testing it with various combinations of parameters including force refresh (and - plus removed browsing data and account, logged out, changed to a new device) and it did not help. I can only tell: on iOS valid token requested in background stops working before its 1h lifetime ends.
As the the same Android app with MSAL works well in background for the same Azure app config and api permissions, it concludes the Azure setup for the app is OK, and the problem is related to iOS MSAL.

@Be-Maps Be-Maps changed the title [iOS] [issue] For Outlook personal accounts (imap) - when requesting token with scopes from swift, MSAL is not requesting default scope : offline_access [iOS] [issue] For Outlook personal accounts (imap) - Not expired silent access token, requested from background, is loosing lease/access to IMAP 1h after application start (beyond that 1h background requested tokens do not work). Sep 19, 2024
@Be-Maps Be-Maps changed the title [iOS] [issue] For Outlook personal accounts (imap) - Not expired silent access token, requested from background, is loosing lease/access to IMAP 1h after application start (beyond that 1h background requested tokens do not work). [iOS] [issue] For Outlook personal accounts (imap) - Not expired silent access token, requested from background, is loosing lease/access to IMAP 1h after application start (beyond that 1h - background requested tokens do not work). Sep 19, 2024
@Be-Maps
Copy link
Author

Be-Maps commented Sep 20, 2024

Hi Will, and Patil,
I guess the problem was around MSAL objects persistence on iOS.
When I stored the MSAL object in following way the problem is gone.

actor OAuthDetails {
@mainactor static let shared = OAuthDetails()
@mainactor private static var accessToken:String = ""
@mainactor static func setToken( _ accessToken:String ) { self.accessToken = accessToken }
@mainactor static func getToken() -> String { return self.accessToken }
@mainactor private static var expiresOn:Date = Date()
@mainactor static func setExpiery( _ expiresOn:Date ) { self.expiresOn = expiresOn }
@mainactor static func getExpiery() -> Date { return self.expiresOn }
@mainactor private static var applicationContext:MSALPublicClientApplication?
@mainactor static func setApplicationContext( _ applicationContext:MSALPublicClientApplication? ) { self.applicationContext = applicationContext }
@mainactor static func getApplicationContext() -> MSALPublicClientApplication? { return self.applicationContext }
@mainactor private static var currentAccount:MSALAccount?
@mainactor static func setCurrentAccount( _ currentAccount:MSALAccount? ) { self.currentAccount = currentAccount }
@mainactor static func getCurrentAccount() -> MSALAccount? { return self.currentAccount }}

Hi Patil,
Please keep it open for 2 weeks just in case, I'll make longer term tests.
Thank You.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants