Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

java - Office 365 XOAUTH2 for IMAP and SMTP Authentication fails

Recently the support for OAuth 2.0 for IMAP and SMTP in the Exchange Online has been announced. Following the guide I've set up the application permissions and IMAP and SMTP connection. The application is configured as Accounts in any organizational directory (Any Azure AD directory - Multitenant) and uses authorization code flow.

URLs below are used for authorization:

And the following Delegated Microsoft Graph scopes have been added:

enter image description here

The scopes, requests from code:

final List<String> scopes = Arrays.asList(
    "offline_access",
    "email",
    "openid",
    "profile",
    "User.Read",
    "Mail.ReadWrite",
    "https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All",
    "https%3A%2F%2Foutlook.office365.com%2FSMTP.Send"
);

I successfully receive the access and refresh tokens:

{
    "token_type": "Bearer",
    "scope": "email IMAP.AccessAsUser.All Mail.ReadWrite openid profile SMTP.Send User.Read",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "edited",
    "refresh_token": "edited",
    "id_token": "edited"
}

Here's the code, used to connect to IMAP:

Properties props = new Properties();
props.put("mail.imap.ssl.enable", "true");
props.put("mail.imaps.sasl.enable", "true");
props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
props.put("mail.imap.auth.login.disable", "true");
props.put("mail.imap.auth.plain.disable", "true");
props.put("mail.debug", "true");
props.put("mail.debug.auth", "true");

Session session = Session.getInstance(props);
session.setDebug(true);

String userEmail = "[email protected]";
String accessToken = "access_token_received_on_previous_step";

final Store store = session.getStore("imaps");
store.connect("outlook.office365.com", 993, userEmail, accessToken);

Which generates the following output:

DEBUG: JavaMail version 1.6.2
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle]
DEBUG IMAPS: mail.imap.fetchsize: 16384
DEBUG IMAPS: mail.imap.ignorebodystructuresize: false
DEBUG IMAPS: mail.imap.statuscachetimeout: 1000
DEBUG IMAPS: mail.imap.appendbuffersize: -1
DEBUG IMAPS: mail.imap.minidletime: 10
DEBUG IMAPS: enable SASL
DEBUG IMAPS: SASL mechanisms allowed: XOAUTH2
DEBUG IMAPS: closeFoldersOnStoreFailure
DEBUG IMAPS: trying to connect to host "outlook.office365.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready. [QQBNADc...]
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS MOVE ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAPS: AUTH: PLAIN
DEBUG IMAPS: AUTH: XOAUTH2
DEBUG IMAPS: protocolConnect login, host=outlook.office365.com, [email protected], password=<non-null>
DEBUG IMAPS: SASL Mechanisms:
DEBUG IMAPS:  XOAUTH2
DEBUG IMAPS: 
DEBUG IMAPS: SASL client XOAUTH2
DEBUG IMAPS: SASL callback length: 2
DEBUG IMAPS: SASL callback 0: javax.security.auth.callback.NameCallback@17046283
DEBUG IMAPS: SASL callback 1: javax.security.auth.callback.PasswordCallback@5bd03f44
A1 AUTHENTICATE XOAUTH2 dXNlcj1o...
A1 NO AUTHENTICATE failed.

Exception in thread "main" javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
    at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:732)
    at javax.mail.Service.connect(Service.java:366)

And the following code is used for connecting to SMTP:

Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
props.put("mail.smtp.auth.login.disable","true");
props.put("mail.smtp.auth.plain.disable","true");
props.put("mail.debug.auth", "true");

Session session = Session.getInstance(props);
session.setDebug(true);

String userEmail = "[email protected]";
String accessToken = "access_token_received_on_previous_step";

Transport transport = session.getTransport("smtp");
transport.connect("smtp.office365.com", 587, userEmail, accessToken);

Which provides the output below:

DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.office365.com", port 587, isSSL false
220 AM5PR0701CA0005.outlook.office365.com Microsoft ESMTP MAIL Service ready at Mon, 4 May 2020 15:52:28 +0000
DEBUG SMTP: connected to host "smtp.office365.com", port: 587
EHLO ubuntu-B450-AORUS-M
250-AM5PR0701CA0005.outlook.office365.com Hello [my ip here]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-STARTTLS
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "STARTTLS", arg ""
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
STARTTLS
220 2.0.0 SMTP server ready
EHLO ubuntu-B450-AORUS-M
250-AM5PR0701CA0005.outlook.office365.com Hello [my ip here]
250-SIZE 157286400
250-PIPELINING
250-DSN
250-ENHANCEDSTATUSCODES
250-AUTH LOGIN XOAUTH2
250-8BITMIME
250-BINARYMIME
250-CHUNKING
250 SMTPUTF8
DEBUG SMTP: Found extension "SIZE", arg "157286400"
DEBUG SMTP: Found extension "PIPELINING", arg ""
DEBUG SMTP: Found extension "DSN", arg ""
DEBUG SMTP: Found extension "ENHANCEDSTATUSCODES", arg ""
DEBUG SMTP: Found extension "AUTH", arg "LOGIN XOAUTH2"
DEBUG SMTP: Found extension "8BITMIME", arg ""
DEBUG SMTP: Found extension "BINARYMIME", arg ""
DEBUG SMTP: Found extension "CHUNKING", arg ""
DEBUG SMTP: Found extension "SMTPUTF8", arg ""
DEBUG SMTP: protocolConnect login, host=smtp.office365.com, [email protected], password=<non-null>
DEBUG SMTP: Attempt to authenticate using mechanisms: XOAUTH2
DEBUG SMTP: Using mechanism XOAUTH2
AUTH XOAUTH2 dXNlcj1obW9kaUB...
535 5.7.3 Authentication unsuccessful [AM5PR0701CA0005.eurprd07.prod.outlook.com]

Exception in thread "main" javax.mail.AuthenticationFailedException: 535 5.7.3 Authentication unsuccessful [AM5PR0701CA0005.eurprd07.prod.outlook.com]
    at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:965)
    at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:876)
    at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:780)
    at javax.mail.Service.connect(Service.java:366)

What I've also tried:

  • specifying scopes as https://graph.microsoft.com/SMTP.Send and just SMTP.Send
  • using https://login.microsoftonline.com/common/ url for authentication

Result is always the same.

Is it something I do wrong or there's a bug somewhere in the support for this from the Microsoft side?

Update 1:

Tried from the command line, but same result:

$ openssl s_client -crlf -connect outlook.office365.com:993
... connection part omitted
* OK The Microsoft Exchange IMAP4 service is ready. [QQBNADYAUAAxADkAMgBDAEEAMAAwADkAMQAuAEUAVQBSAFAAMQA5ADIALgBQAFIATwBEAC4ATwBVAFQATABPAE8ASwAuAEMATwBNAA==]
C01 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
C01 OK CAPABILITY completed.
A01 AUTHENTICATE XOAUTH2 dXNlcj1obW9kaUBjb...
A01 NO AUTHENTICATE failed.
* BYE Connection is closed. 13
read:errno=0

Update 2:

Tried to create brand new application in the Azure Portal with the following permissions:

enter image description here

And receiving the following screen, when trying to give consent for scopes:

enter image description here

That is odd, because the permissions from Azure Portal don't specify that the Admin consent is required and my previous app registration doesn't show such screen when IMAP and SMTP scopes are requested.

Update 3:

Thanks to comments to this post I tried the following scopes:

public static final List<String> SCOPES = Arrays.asList(
    "offline_access",
    "https%3A%2F%2Foutlook.office365.com%2FIMAP.AccessAsUser.All",
    "https%3A%2F%2Foutlook.office365.com%2FSMTP.Send"
);

Which gave me the token below:

{
    "token_type": "Bearer",
    "scope": "https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/SMTP.Send",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "eyJ0eXAiOiJKV1....",
    "refresh_token": "OAQABAAAAAAAm...."
}


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

IMAP, SMTP scopes are targeted for Exchange resource and not Graph. Whereas User.Read, Mail.ReadWrite are meant for Graph resource.

We do not support generation of tokens that are meant for two resources. Hence the error "Provided value for the input parameter scope is not valid because it contains more than one resource."

You should generate two tokens separately by two calls to /token. 1. One with the IMAP, SMTP scopes generated for the Exchange resource. 2. The other with Graph scopes (User.Read, Mail.ReadWrite) meant for Graph resource.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...