postalsys / imapflow

IMAP Client library for EmailEngine Email API (https://emailengine.app)
https://imapflow.com
Other
367 stars 64 forks source link

Add oAuth2 for outlook365 with msal generated token #117

Closed erivalo closed 1 year ago

erivalo commented 1 year ago

Problem Can't connect as App to outlook365 using oAuth2.0 with msal generated tokens

Describe the solution you'd like I would like to connect using my generated token by msal library like in library "node-imap" or "imap",

const getImapConfig = (token) => {
  const mailId = 'NiceEmail@niceCompany.onmicrosoft.com';
  const auth2 = Buffer.from(
    [`user=${mailId}`, `auth=Bearer ${token.accessToken}`, '', ''].join('\x01'),
    'utf-8'
  ).toString('base64');
  const imapConfig = {
    xoauth2: auth2,
    host: 'outlook.office365.com',
    port: 993,
    tls: true,
    debug: console.log,
    authTimeout: 25000,
    connTimeout: 30000,
    tlsOptions: {
      rejectUnauthorized: false,
      servername: 'outlook.office365.com',
    },
  };

  return imapConfig;
};
const imapConfig = getImapConfig(token);
// const imap = new Imap(imapConfig);
const client = new ImapFlow(imapConfig);

Alternatives Did not found for now, suggestions are welcome?

Additional context In order to get the token msal library provided by microsoft is being used, the app to generate the token registered on azure has App to app permissions

andris9 commented 1 year ago

ImapFlow can work with any OAuth2 capable IMAP server, assuming that you generate the access token yourself. To use it:

Instead of a password like here

auth: {
    user: "username",
    pass: "password"
}

use the access token

auth: {
    user: "username",
    accessToken: "secret-access-token"
}

ImapFlow itself does not generate access tokens, this is on yourself.

erivalo commented 1 year ago

I'm generating the token from "msal" library, and tried several variations on this:

auth: {
  user: "NiceEmail@niceCompany.onmicrosoft.com",
  accessToken: token.accessToken,
},

// xoauth2: auth2,
auth: {
  user: "NiceEmail@niceCompany.onmicrosoft.com",
  accessToken: Buffer.from(
    [`user=NiceEmail@niceCompany.onmicrosoft.com`, `auth=Bearer ${token.accessToken}`, "", ""].join("\x01"),
   "utf-8"
   ).toString("base64"),
},

I'm still getting the message: '3 NO AUTHENTICATE failed.' am I missing something?

andris9 commented 1 year ago

You only need to provide the token string, do not encode it or convert it

erivalo commented 1 year ago

I tried that option too, this would be the object injected to the method:

const imapConfig = {
    auth: {
      user: "NiceEmail@niceCompany.onmicrosoft.com",
      accessToken: token.accessToken, // string
    },
    host: "outlook.office365.com",
    port: 993,
    secure: true,
    authTimeout: 10000,
    connectionTimeout: 30000,
    debug: console.log,
    tls: {
      rejectUnauthorized: false,
    },
  };
  const client = new ImapFlow(imapConfig);
  await client.connect();

With the token provided it is passing on imap library, so I guess the token is good, and it still with "authenticate failed" response...

Airdevelopments commented 1 year ago

I had the same issue. Adding the Delegated Permission "IMAP.AccessAsUser.All"

image (sorry that the screenshot is in german)

and requesting the access_token with the scope "https://outlook.office.com/IMAP.AccessAsUser.All" resolved the issue. Before this, I usually just used app registrations to access the GraphAPI, which scope always was "https://graph.microsoft.com/.default" (and I just copy pasted...).

This Microsoft Documentation explains it in detail: https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth