Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

com.microsoft.aad.msal4j.MsalInteractionRequiredException: AADSTS65001 when calling acquireTokenSilently with IMAP/SMTP scopes #82

Open
avayaappdev opened this issue May 25, 2022 · 8 comments
Assignees
Labels
bug Something isn't working

Comments

@avayaappdev
Copy link

I'm currently facing an issue with Java MSAL implementing the OAuth 2.0 authorization code flow when attempting
to get a new access token using the refresh token stored in token cache by calling the library method acquireTokenSilently method and that only happen when passed silent parameters has any one of the below scopes which I need to authenticate IMAP/SMTP access to mailbox using OAuth as mentioned in the Microsoft document below
https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

        SilentParameters parameters = SilentParameters.builder( 
        Collections.singleton("https://outlook.com/IMAP.AccessAsUser.All"), account).forceRefresh(true).build();
        IAuthenticationResult result = app.acquireTokenSilently(parameters).get();

That caused the below Error

com.microsoft.aad.msal4j.MsalInteractionRequiredException: AADSTS65001: The user or administrator has not consented to use the application with ID '692c6df0-1d90-43a6-953d-5ccc50a65efe' named 'microsoft-sdk-poc'. Send an interactive authorization request for this user and resource.

Although when fetching the authorization code used to acquire an access token for the first time I signed in with my Global administrator account that registered the app on Microsoft Azure Active Directory and granted all the permissions requested
by my registered app

but when the IMAP/SMTP scopes are as below without specifying the resource identifier outlook.office365.com or outlook.com
the acquireTokenSilently method worked successfully and returned an access token.

  • IMAP.AccessAsUser.All
  • SMTP.Send

but when trying to use that returned token from acquireTokenSilently method to access the mailbox using javax.mail library 1.6.1

        Properties props = new Properties();
        props.put("mail.imaps.ssl.enable", "true");
        props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
        props.setProperty("mail.imaps.starttls.enable", "true");
        props.setProperty("mail.debug", "true");
        props.put("mail.debug.auth", "true");
        Session session = Session.getInstance(props);
        Store store = session.getStore("imaps");
        store.connect("outlook.com", 993, emailAddress, accessToken );

That returned an Authentication Error

A1 NO AUTHENTICATE failed.
store.connect() method throws javax.mail.AuthenticationFailedException : AUTHENTICATE failed.

Any help much appreciated.

@Avery-Dunn
Copy link
Contributor

Hello @avayaappdev : Just to be clear, your basic situation is:

  • You got some tokens successfully, including a refresh token
  • You tried to get a new access token with that refresh token (your first block of code, with SilentParameters)
  • That silent refresh attempt failed with that 'has not consented' error
  • You removed the 'outlook.com' parts of the scope, and the silent token refresh succeeded
  • The new token (from the refresh attempt) is causing an 'authenticate failed' error when using javax.mail

Did I understand your situation right? If so, I have a couple questions to try to find the root cause:

  • When you got the tokens originally (before trying to refresh), you requested the full scope strings like 'https://outlook.com/IMAP.AccessAsUser.All', right?
  • If you decode the original access token (you can do so simply here), what are the scopes in the 'scp' field of the token payload? And how about the token returned by the silent call, are they different?
  • Did the original tokens work with javax.mail? Does it only start failing after you refresh them?

@Avery-Dunn Avery-Dunn self-assigned this May 27, 2022
@Avery-Dunn Avery-Dunn added the bug Something isn't working label May 27, 2022
@avayaappdev
Copy link
Author

Hi @Avery-Dunn , yes right the situation happened as you described

  • The scp field in the decoded original access token ( before trying to refresh ) was just IMAP.AccessAsUser.All
    so the https://outlook.com/ resource identifier was automatically omitted from scp value, And this original access token
    ( fetched using the authorization code ) was able to connect to mailbox with javax.mail which seems weird to me as I expected the scope in decoded token to be the full URL https://outlook.com/IMAP.AccessAsUser.All
    as per Microsoft documentation not IMAP.AccessAsUser.All

  • When requesting an access token (using authorization code) with scope is IMAP.AccessAsUser.All not https://outlook.com/IMAP.AccessAsUser.All that original access token first fetched was not able to connect to mailbox with javax.mail because of AUTHENTICATE failed error

so to be able to connect to mailbox with javax.mail on behalf of the signed in user.
every time when the original access token expires, The user must sign in again in a browser with his Microsoft account and consent the requested permissions by the application because only the first original fetched access token ( using authorization code with scope https://outlook.com/IMAP.AccessAsUser.All ) work and I can't refresh that token because of the error: com.microsoft.aad.msal4j.MsalInteractionRequiredException: AADSTS65001: The user or administrator has not consented

@Avery-Dunn
Copy link
Contributor

I don't have a javax.mail app to test with, but I was able to recreate the behavior where the token service seems to drop the 'https://outlook.com/' part of the requested scope and the 'has not consented' error when trying to refresh it.

Scopes usually don't need the full resource ID (though I think this was more common in the past), but I'm not sure why the token service would drop it when returning the access token. My best guess is that it understands what you meant when you included 'https://outlook.com/ '. and is implying that 'IMAP.AccessAsUser.All' is enough and should get what you need.

I noticed that the link you posted is under the category of 'legacy protocols' (and seems to use 'https://outlook.office.com/', rather than 'https://outlook.com/' as you've been writing here), so it's possible that these aren't the correct scopes to use anymore, or they only work for some specific scenarios.

Try using 'https://graph.microsoft.com/IMAP.AccessAsUser.All' instead. This seems to be the equivalent 'delegated permission' that you'd be able to find in the 'API Permissions' tab of your Azure app, and for me at least I was able to refresh it without getting that 'has not consented message'. However, as I said I wasn't able to test the javax.mail side of things so I'm not sure if it'll work there, nor do I know what configuration might need to be changed.

@avayaappdev
Copy link
Author

I've already used the https://graph.microsoft.com/IMAP.AccessAsUser.All scope during my trials
and the refresh token worked in that case without getting that 'has not consented message' error message but the original fetched token and the refreshed token didn't work when trying to connect to the mailbox with IMAP and SMTP protocols
with javax.mail which just implements OAuth authentication logic to connect with IMAP or SMTP as mentioned in the Microsoft document https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth
when using Microsoft Graph scopes I'm getting below error

A1 NO AUTHENTICATE failed.

and I think that is expected because I'm not accessing the mailbox resource using Microsoft Graph API, I'm connecting using IMAP and SMTP legacy protocols and authenticating them using OAuth.

also, I've found that if the resource identifier is omitted in the scope parameter, the resource is assumed to be Microsoft Graph by default as stated in the the below document.
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent

@Avery-Dunn
Copy link
Contributor

Could you describe your scenario a bit? Is there a reason you can't just use Graph? I don't have much experience programmatically sending email or using the scopes that Azure AD provides, but most of the documentation I've found (and the one you've linked) seems to be saying that these are mostly deprecated in favor of Microsoft Graph.

Also I've noticed you've written "https://outlook.com/" as the resource before the actual scopes. Is there a reason for that? The documentation you linked says it should be "https://outlook.office.com/".

And for the 'user has not consented' error message, the reason for that may be that the Azure AD app is not be remembering that the user has consented to them. If you go to the 'API Permissions' tab of your app registration, are any of the IMAP/SMTP permissions showing up as having been consented to? For me, only the time I consented to the Microsoft Graph version seems to have registered in the Azure app, and I can't find any non-Graph versions of those scopes as an option when trying to add new permissions:

image

@avayaappdev
Copy link
Author

avayaappdev commented Jun 1, 2022

I can't use Graph API because the system I'm currently working on is already using IMAP and SMTP protocols for mail scenarios and migrating to Microsoft Graph API will take time, also I couldn't see a direct saying in Microsoft documentations that the old permissions are really deprecated.

@Avery-Dunn
Copy link
Contributor

I also couldn't find anything directly saying that the old permissions are deprecated, it's just something I assumed since the IMAP.AccessAsUser.All permission in the app registration is only available for Graph, the link you posted is under a section called 'Legacy Protocols', and most of the docs I've been able to find assume you're using Graph (even the original version of that 'Legacy Protocols' doc assumed Graph).

I asked some people with more knowledge of Outlook/Graph and they recommended checking out the Microsoft Graph SDK (here is the Java sdk) and the Outlook mail REST API to see if they could help make accessing the mailboxes easier.

@hardtmedia
Copy link

Good day, I currently face the same issue in that I need to migrate a service that talks to IMAP from Basic Auth to OAuth, without switching to Graph. From what I understand, it is possible to retrieve a token for a daemon type application. However, this token cannot be reliably used to access IMAP with javax.mail.

Did you find a solution since? Or is the solution truly to fully migrate to Graph instead?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants