I have an Exchange Online environment and service/daemin (no interactive user) application on the Azure VM. Service uses EWS managed API to work with emails in the mailbox of any tenant user. Now EWS client uses Basic authentication that, according to Microsoft, will become unsupported in EWS to access Exchange Online.
So, I need to find a way to get valid access token for service/daemon application to use with EWS managed API.
The following article shows an example of using OAuth 2.0 with EWS managed API. This example works, but it uses interactive method of getting consent (sign-in form appears allowing user authenticate themselves and grant requested permission to application) that is not suitable for service/daemon app scenario, because there is no interactive user.
For service/daemon application I need to use client credential
authentication flow.
Using admin account on https://aad.portal.azure.com portal I registered application with Azure Active Directory. Added client secret for registered application.
Aforementioned article uses https://outlook.office.com/EWS.AccessAsUser.All
as a scope
. But I did not find permission with such a URL on the portal. I found only the following permissions under Office 365 Exchange Online
> Application permissions
> Mail
:
https://outlook.office365.com/Mail.Read
Allows the app to read mail in all mailboxes without a signed-in userhttps://outlook.office365.com/Mail.ReadWrite
Allows the app to create, read, update, and delete mail in all mailboxes without a signed-in user. I added both of them and granted admin consent for all users.
For testing purposes and simplicity I did not use any auth libraries (ADAL, MSAL etc.). I used Postman to get access token, then set token
variable in debug (see code snippet later in the post).
I tried different endpoints to get acess token.
POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token
grant_type=client_credentials
client_id=***
client_secret=***
scope=https://outlook.office.com/EWS.AccessAsUser.All
Sending this request produces the following error response:
AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope https://outlook.office.com/EWS.AccessAsUser.All is not valid.
I tried changing scope
to https://outlook.office.com/.default
. Access token was returned, but it appeared to be invalid for EWS. EWS client throws 401 error with the following value of x-ms-diagnostics
response header:
2000008;reason="The token contains no permissions, or permissions can not be understood.";error_category="invalid_grant"
POST: https://login.microsoftonline.com/<TENANT_ID>/oauth2/token
grant_type=client_credentials
client_id=***
client_secret=***
resource=https://outlook.office.com
Access token was returned, but also appeared to be invalid for EWS. EWS client throws 401 error with the same value of x-ms-diagnostics
response header as described ealier in #1.
Here is code sample that I used to test EWS client with access token acquired in Postman:
var token = "...";
var client = new ExchangeService
{
Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
Credentials = new OAuthCredentials(token),
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,
"[email protected]"),
};
var folder = Folder.Bind(client, WellKnownFolderName.SentItems);
After you add the authorization profile, you need to get access token from the server. In this tutorial, we get it by using the Authorization Code grant method: Click Get Token. In the subsequent dialog, enter Client Identification and Secret, Authorization URI, Access Token URI and Redirect URI.
Use OAuth authentication in all your new or existing EWS applications to connect to Exchange Online. OAuth authentication for EWS is only available in Exchange Online as part of Microsoft 365. EWS applications that use OAuth must be registered with Azure Active Directory first.
To use OAuth with your application you will need to: Register your application with Azure Active Directory. Add code to get an authentication token to get an authentication token from a token server. Add an authentication token to EWS requests that you send.
You can protect your client application with either a certificate or a secret. The two permissions that I needed to get this to work were Calendars.ReadWrite.All
and full_access_as_app
. I never tried acquiring my token via PostMan, but use AcquireTokenAsync
in Microsoft.IdentityModel.Clients.ActiveDirectory
. In that call, the resource
parameter I use is https://outlook.office365.com/. It's pretty simple once you know all the little twists and turns. And full disclosure: I was one lost puppy until MSFT support helped me through this. The doc on the web is often outdated, conflicting, or at best, confusing.
We had a similar problem: We wanted to use a Service Account to connect to a single mailbox and just doing some stuff with the EWS API (e.g. searching in the GAL) and the full_access_as_app
seems like an overkill.
Fortunately it is possible:
Follow the normal "delegate" steps
And use this to get a token via username/password:
...
var cred = new NetworkCredential("UserName", "Password");
var authResult = await pca.AcquireTokenByUsernamePassword(new string[] { "https://outlook.office.com/EWS.AccessAsUser.All" }, cred.UserName, cred.SecurePassword).ExecuteAsync();
...
With that setup we could use a "tradional" username/password way, but using OAuth and the EWS API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With