Reputation: 55
I am using node-ews to fetch emails from the Microsoft Exchange server. It was working fine with basic auth.
But, as Microsoft disabled basic auth. We are currently using the OAuth token (access token) from Graph Explorer to test.
But it's returning 401 Unauthorised error.
This is the sample code we are using to connect to the exchange server.
const ewsConfig = {
username: item.mail_username,
password: item.user_pass,
host: item.ews_host,
token: 'xxxxxxxxxxx',
auth: 'bearer'
};
// initialize node-ews
const options = {
rejectUnauthorized: false,
strictSSL: false
};
// initialize node-ews
const ews = new EWS(ewsConfig, options);
Upvotes: 0
Views: 1170
Reputation: 11
Part 1-2 - continue:
https://login.microsoftonline.com/ADD YOUR TENANTID/oauth2/v2.0/authorize?
client_id=ADD YOUR CLIENTID
&response_type=code
&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient
&response_mode=query
&scope=EWS.AccessAsUser.All
&state=12345
Upvotes: 0
Reputation: 11
Part 0 - Please find the solution we used to fix the same problem.
The solution consist of 3 parts:
Sorry for 3 posts, but as a new user I have a restrictions it impossible for new users to create posts with more than 8 links and etc... )
Upvotes: 0
Reputation: 11
Part 1-1 - Setup application in AZURE that allows to generate MSAL-access token for EWS:
Part-1-2 - continue...
Upvotes: 1
Reputation: 11
Part 2 - get accessToken by using userName + userPassword to email box:
import * as path from 'path';
import { ExchangeService, EmailMessage, MessageBody, OAuthCredentials, AutodiscoverService, Folder, Item, ExchangeVersion } from 'ews-javascript-api';
public async getEmailAccessToken(
clientId: string,
tenantId: string,
emailUserName: string,
emailUserPassword: string,
cacheFilePath: string = `.${path.sep}tokenCache.json`) {
const msal = require('@azure/msal-node');
const { promises: fs } = require('fs');
//Cache Plugin configuration
const beforeCacheAccess = async (cacheContext) => {
try {
const cacheFile = await fs.readFile(cacheFilePath, 'utf-8');
cacheContext.tokenCache.deserialize(cacheFile);
} catch (error) {
// if cache file doesn't exists, create it
cacheContext.tokenCache.deserialize(await fs.writeFile(cacheFilePath, ''));
}
};
const afterCacheAccess = async (cacheContext) => {
if (cacheContext.cacheHasChanged) {
try {
await fs.writeFile(cacheFilePath, cacheContext.tokenCache.serialize());
} catch (error) {
console.log(error);
}
}
};
const cachePlugin = {
beforeCacheAccess,
afterCacheAccess
};
const msalConfig = {
auth: {
clientId: clientId, // YOUR clientId
authority: `https://login.microsoftonline.com/${tenantId}` // YOUR tenantId
},
cache: {
cachePlugin
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose
}
}
};
const pca = new msal.PublicClientApplication(msalConfig);
const msalTokenCache = pca.getTokenCache();
const accounts = await msalTokenCache.getAllAccounts();
// Acquire Token Silently if an account is present
let accessToken = null;
if (accounts.length > 0) {
const silentRequest = {
account: accounts[0], // Index must match the account that is trying to acquire token silently
scopes: ['https://outlook.office365.com/EWS.AccessAsUser.All'],
};
const response = await pca.acquireTokenSilent(silentRequest);
accessToken = response.accessToken;
} else {
// fall back to username password if there is no account
const usernamePasswordRequest = {
scopes: ['https://outlook.office365.com/EWS.AccessAsUser.All'],
username: emailUserName, // Add your username here
password: emailUserPassword, // Add your password here
};
const response = await pca.acquireTokenByUsernamePassword(usernamePasswordRequest);
accessToken = response.accessToken;
}
return accessToken;
}
This method returns accessToken allows us to use EWS-api and also generates tokenCacheFile.json that will be used for silent usage in case of multiple calls.
Part 3 - connect to emailbox by using previously generated accessToken and ews-javascript-api :
import { ExchangeService, EmailMessage, MessageBody, OAuthCredentials, AutodiscoverService, Folder, Item, ExchangeVersion } from 'ews-javascript-api';
public async connectAndChangeAllEmailsFromBlaBla(
clientId: string,
tenantId: string,
exchangeServiceUrl: string = 'https://outlook.office365.com/Ews/Exchange.asmx',
emailUserName: string,
emailUserPassword: string,
searchMask: string = 'hasattachments:yes and from:[email protected] and received:today') {
// get acces token by method written above in part 2
const emailAccessToken = await this.getEmailAccessToken(clientId, tenantId, emailUserName, emailUserPassword);
const ews = require('ews-javascript-api');
const service = new ExchangeService(ews.ExchangeVersion.Exchange2013);
// use emailAccesToken
service.Credentials = new OAuthCredentials(emailAccessToken);
service.Url = new ews.Uri(exchangeServiceUrl);
const mailInbox = await ews.Folder.Bind(service, ews.WellKnownFolderName.Inbox);
const loadPageSize = 1000; // 1 means load last email according to filter
const view = new ews.ItemView(loadPageSize);
view.PropertySet = new ews.PropertySet(ews.BasePropertySet.FirstClassProperties);
let mailItems;
// hasattachment:yes
// isread:false
// received:today or received:[date]
mailItems = await mailInbox.FindItems(searchMask, view);
console.log(`Emails were found before processing: ${mailItems.Items.length}`);
for (const item of mailItems.Items) {
// mark mail.item as read
item.IsRead = true;
await item.Update(1);
// Do what you want
}
return mailItems.Items.length;
}
Upvotes: 0
Reputation: 22032
. We are currently using the OAuth token (access token) from Graph Explorer to test.
The Graph Explorer token won't have permissions for EWS only Graph, the only two permission that are valid in EWS are EWS.AccessAsUser.All or full_access_as_app if using the client credentials flow. https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth the Mail.Read etc permission don't work in EWS because it doesn't support the more restrictive authentication scheme that Graph supports (which is a reason to use the Graph over EWS)
If you want to accesstoken to test with use the EWSEditor https://github.com/dseph/EwsEditor/releases and grab its token
Upvotes: 1