Using OAuth2 to access Calendar, Contact and Mail API in Office 365 Exchange Online

I am excited to announce that finally EWS in Exchange Online as part of Office 365 exposes a RESTful API for Calendar, Contact and Mail that uses OAuth2 for authorization. In this blog I want to briefly describe Exchange's underlying OAuth2 implementation and how to use OAuth2 to access the new Calendar, Contact and Mail APIs.

First and foremost Office 365 fully integrates with Microsoft Azure Active Directory (AAD) to implement the OAuth2 protocol. There is an exciting announcement available here from Alex Simons, Director of AAD that outlines the new AAD capabilities around this protocol. At the end of this blog I will refer to more documentation. Don't hesitate to ask questions on Stack Overflow about accessing the new APIs using the OAuth2 protocol or about the APIs themselves. Please tag your question with [ms-office] and [ews] to get the fastest response possible.

I will focus in this post on the basic concepts and will highlight some of the differences compared to other OAuth2 implementations to get you started as fast as possible. 

Step One: Registering your application

Before you can use OAuth2 to access the Calendar, Contact and Mail APIs, your application needs to be registered in AAD. When you sign up for Office 365 for business or Office 365 developer, don’t worry, you already get AAD. If you are using Visual Studio, the app registration and permission management in AAD is done automatically for you! Outside of Visual Studio, you should visit the Microsoft Azure Portal and do the application registration there. For a step by step walkthrough, go here. During the application registration process a client ID for your application is created, client secrets for web apps are managed, and the permissions your application needs for accessing the APIs are defined.

Important:  Permissions must be defined up-front in AAD for the APIs as part of the registration process. While you might be used to specifying the permissions with the authorization request in other OAuth2 implementations by using the "scope" parameter, this is not possible at this point. In order for your application to configure these permissions in AAD, the AAD tenant where your application is registered must be linked to an Office 365 subscription with Exchange Online.

Step Two: Triggering user consent (aka authorization)

The goal of your application is to access the Calendar, Contact and Mail APIs. For this you need to get an access token that is passed along with the API request to Office 365. An OAuth2 authorization request is the first step for your application to get an access token. As part of the authorization process, user consent is involved. User consent is the act of displaying a dialog that clearly lists the permissions that your application is requesting. The user can decide if the permissions your application is requesting should be granted. The permissions that appear in this consent dialog are the same permissions you configured in Step One in AAD.

A typical authorization request looks like this (make sure you use https):






Required. Value is always code


Required. Value of your client_Id given when you registered your application with AAD


Required. The Office 365 resource your app wants to access. As you want to access Office 365 Calendar, Contact, and Mail APIs this is "".


Required. A long unique string value of your choice that is hard to guess. Used to prevent CSRF. For example, this can be a Guid generated by your app with each authorization request.


Required. URI in your app where users will be sent after authorization. Must be registered in AAD.

Important: Redirect URIs must be registered in AAD as "Reply URLs" as part of the initial app registration, or at any point in time later in the process of developing your app. Failure to specify the correct redirect_uri will cause the authorization request to fail. So check your Reply URLs in AAD with your app registration.

If the user authorizes your application they will be redirected to the redirect_uri that you specified in your request above along with a temporary authorization code and the same state that you passed in the request above.

Upon successful authorization, the redirected URL should look like:

{your redirect uri}/?code={authorization code issued by AAD}&state={same state Guid passed as in the authorize request}

Ensure that the state parameter in this response matches the one you passed in the authorization request above. If the state does not match, that means the request may be a result of CSRF and must be rejected. 

If the user does not allow authorization to your application, redirection to the redirect_uri still occurs. Additional query parameters indicate the user canceled authorization and a short description of the error: 

{your redirect uri}/?error=access_denied&error_description=AADSTS65004%3a+The+resource+owner+or+authorization+server+denied+the+request.%0d&state={same state Guid passed as in the authorize request}

Before we continue with the next step on how to trade in the authorization code to an actual access token that you can use to access the Office 365 Calendar, Contact and Mail APIs we need to talk about a very nice variation of authorization you can request for a web application. Note that following doesn't apply for a native application. In particular AAD offers for a web application the capability for an administrator of an Office 365 Organization to consent on-behalf of all users in this organization. Once an administrator does this, users will not see any additional consent dialog with authorization requests. Essentially what this provides you is that your web application can have a sign-up experience for an organization in addition to an individual sign-up for a user.

You can accomplish this kind of organization sign-up in your app by simply modifying the authorize request to add an additional parameter called "prompt". For the example above this would be:


Triggering admin consent for an organization sign-up:




Value: "admin_consent". Indicates that the authorization request is for all users in an Office 365 Organization.

Important: Only an administrator of an Office 365 Organization can consent to such an authorization request. If an end-user tries to do this, AAD will redirect back to your application with an specific error indicating that consent could not be given.

Step Three: Trade-In for an access/refresh token

We're almost there. At this point we have an authorization code from a user or an administrator dependent on the authorization request your application sent. This code can be used to request an access token. Together with the access token your application will also receive a refresh token and an ID token. For the API access only the access token is important. The refresh token is for your app to keep in a safe place in an encrypted persistent temporary or permanent storage dependent on your app's requirements. You use the refresh token to request additional access tokens for either the same Calendar, Contact and Mail APIs or other Office 365 APIs such as OneDrive Pro. The ID token can be used to authenticate the user to your application. It contains the users ID and the organizations ID as known by AAD. You can use those IDs for your app's own memory or profile store on who signed up. Both access token and refresh token are JSON encoded web tokens that can be easily parsed. The specific properties to use for building an app-specific profile store in the ID token are "oid" for the user identifier and "tid" for the organization identifier.

Let's look at how such a token request looks like using the code.

Code Request:

The base token URL is:


The body is form encoded:

grant_type=authorization_code&code={code from the authorize request}&redirect_uri={reply url for your application}&client_id={your application's client id in AAD}&client_secret={your application's client secret}

Important: The redirect_uri in the token request must be the same as the one used with the original authorize request.

A successful response to this request contains the following fields in a JSON object:




Base64 url encoded JSON web token for API access.


Always "Bearer"


The time period (in seconds) after which the access token will become invalid. For now this value is 60 minutes.


Time in Unix epoch when the access token expires.


The Office 365 resource or API that this access token applies to.


Opaque refresh token for your app to keep and get additional access tokens.


The permissions that this access token contains.


Base64 url encoded JSON web token for user identification by the client application.

After 60 minutes the access token becomes invalid. A good pattern to follow is to cache this access token and use it to call the Calendar, Contact and Mail APIs until the API returns a 401 access denied with an error code of "invalid_token". In that 401 event your app should use the refresh token to get a new access token and retry the request.

Note: In case you are curious about what an access token or ID Token look like, there is a great web app available at that allows you to simply cut and paste the token received from a token request and peek into the fields.

Request additional access tokens with a refresh token

Below is the request your app uses to get a new access token. The actual response is the same as in the code request, thus together with the access token your app receives a new refresh token. You should take the new refresh token to replace the old one as refresh tokens will expire in some period of time too.

The base token URL is:


 The body is form encoded:


grant_type=refresh_token&refresh_token={the current RefreshToken in your applications cache}&client_id={your application's client id in AAD}&client_secret={your application's client secret}&resource={Uri of the API}


Important: The resource parameter indicates what API endpoint your application wants access to within the Office 365 APIs. For the Calendar, Contact and Mail API this is "".

As briefly mentioned above, the refresh token, while long living, becomes invalid at some point too. One example is if the user changes their password, refresh tokens become invalid. Your app can react to this gracefully by handling an error response from the token request that returns "invalid_grant". If this happens your app should go back to the authorization request as outlined in Step One to get a new authorization code and use this to get a new access token/refresh token pair.


Step Four: Accessing the API with the access token

We are almost there. Your application has an access token at this point for the Calendar, Contact and Mail API. Using this access token is fairly simple. When sending a RESTful API https request the access token is attached in the standard authorization header with the Bearer auth scheme. An example is listed below: 


User-Agent: My-Cool-WebApp/1.0
client-request-id: 9aa1c740-25e2-4841-9146-ef7018f7bf37
return-client-request-id: true
authorization: Bearer {access token your app received in Step Three}

Important: Always use https with your API request.

Unrelated to OAuth2, there are three http request headers the Office365 APIs would want your application to specify. This is the "User-Agent" the "client-request-id" and "Date". For User Agent you can follow RFC 2616 which basically describes it as {ProductName}/{ProductVersion} [{comment}]. For "client-request-id" your app should create a new Guid for each request. When the request fails "return-client-request-id" returns the Guid that was submitted as "client-request-id" with the request. It is highly recommended that you persist the failed request together with client-request-id and all http response headers in some application log. The "Date" requst header signals the date and time that the message was sent. It should follow the "HTTP-date" format as defined by RFC 2616)}. If you ever need help troubleshooting your application with the Office 365 APIs, this will be pave the route to a fast and successful resolution of the problem.


Client libraries are your Friend - use them!

Above I described how your application can apply the OAuth2 protocol for Office 365 APIs on the example of the Calendar, Contact and Mail API. While the OAuth2 protocol is a fairly straight forward and well understood pattern there are many things such as caching of access tokens/refresh tokens that are more complicated to handle within an application. The Azure Active Directory Authentication Library (ADAL) will help you abstracting not only the OAuth2 protocol, but also provide caching and mechanisms to get new access tokens. Whenever possible you should use this client library for your web or native app. ADAL comes for a variety of platforms including .NET, Windows Store, iOS and Android. Check them out!


Hopefully this blog provided you with enough detail to get started developing an exciting application for the Calendar, Contact and Mail API in Office 365. We would love to hear from you at this forum and are eager to know about your apps and feedback on how we can improve on our APIs and authentication to them.

Note that there will be many more functionalities coming in the next month and I will try to keep the blog updated whenever a new capability lights up. For example, the current blog describes the OAuth2 code flow, and AAD in the near future will provide a OAuth2 implicit flow that is optimized for JavaScript-based applications such as Office 365 mail apps. For sure we will post about this pretty soon. We are also working on having OpenID Connect available for single sign-on into your web app and Office 365. All more exciting functionality that will make application development more easy and hopefully worry-free regards the way your application authenticates with Office 365 APIs.

Additional documentation of interest

Finally, as promised, some pointers to documentation. Lots of time went into these and they are well worth reading while sipping your morning or afternoon coffee:

OAuth Sandbox:

API Sandbox:

Platform Overview:

Apps for Office:

Office on Github:

OAuth2 Authorization Grant:

OAuth2 client libraries:

AAD on GitHub:

Comments (72)
  1. Andreas Helland says:

    I started playing with the Exchange API a couple of days ago, and I'm liking it so far.

    So, I put together a two-parter on how to get started coding:…/microsoft-provides-a-restful-api-for-exchange-part-1…/microsoft-provides-a-restful-api-for-exchange-part-2

    I also created a repo on GitHub for the purpose of having a proof of concept for labs:…/CustomOffice365OWA

    (The initial commit does not have all the bells and whistles of a proper OWA or snazzy UI, but it's something to get started with.)

  2. Nice! Thanks for doing this Andreas.

  3. Saurabh says:


    Does anyone know what is the resource url to access the files and folders for OneDrive for Business.

    Many Thanks

  4. Sheldon says:

    In step two, can we send the users email address as a param to auto-populate this login request?

    In my application, once entering the email here, it redirects to their adfs install, so I am trying to automate this step.

  5. Yes, simply add "login_hint" to the URL



  6. Narciso says:

    Making a GET request to "…/Messages" sending

    Headers {

       client-request-id: 'b81b2074-c34e…',

       Authorization: 'Bearer eyJ0eXAiOiJK…',

       Accept: 'application/json;odata.metadata=full',

       return-client-request-id: true


    Where client-request-id is a unique Guid and  Authorization has the base64 AccessToken

    I'm always having


    statusCode:  401

    headers:  { server: 'Microsoft-IIS/8.0',

     'request-id': 'e97ba485-b….',

     'client-request-id': 'b81b2074-c34e….',

     'x-ms-diagnostics': '2000001;reason="No applicable user context claims found.";error_category="invalid_token"',

     'x-powered-by': 'ASP.NET',

     'x-feserver': 'CO1PR06CA033',

     'www-authenticate': 'Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trusted_issuers="00000001-0000-0000-c000-000000000000@*", error="invalid_token",Basic Realm=""',

     date: 'Tue, 03 Jun 2014 23:44:57 GMT',

     'content-length': '0' }

  7. Please try the following: In the request capture the base64encoded AccessToken and copy&paste this into to get the content of the token.

    Please respond with the *content* of the token and also with the complete client-request-id and/or request-id and the time of the request in UTC.

    Thank you!


  8. fmj says:


    I have registered my app using "_layouts/15/appregnew.aspx". I haven't got an account in Windows Azure.

    I try to connect using oauth but I get an error.…/jj687469%28v=office.15%29.aspx



       [error] => access_denied

       [error_description] => AADSTS65005: Resource 'myCLIENTID' does not exist or one of its queried reference-property objects are not present.

    Trace ID: 923debb6-0391-4d17-93bd-b400bb79e5cb

    Correlation ID: 7816531f-776e-4347-9749-cd0fa3dc8eee

    Timestamp: 2014-06-05 12:40:40Z

       [state] => 5fdfd60b-8457-4536-b20f-wrerr58d19458


    Thank you.

  9. Narciso says:


    "typ": "JWT",

    "alg": "RS256",

    "x5t": "kriMPdmBvx68skT8-mPAB3BseeA"



    "aud": "",

    "iss": "…/",

    "iat": 1402349572,

    "nbf": 1402349572,

    "exp": 1402353472,

    "ver": "1.0",

    "tid": "22c06f24-ee3c-4566-aca5-c905b92cc25c",

    "amr": [



    "altsecid": "",

    "idp": "",

    "email": "",

    "given_name": "NARCISO",

    "family_name": "GUILLEN",

    "unique_name": "",

    "appid": "486248f0-f6d5-41da-97b8-eaee90bd69db",

    "appidacr": "1",

    "scp": "Contacts.Write Calendars.Write user_impersonation",

    "acr": "1"



  10. Ankit Sharma says:

    How to get Client Secret?

    Azure Management Portal only shows Client Id.

  11. Ankit Sharma says:

    Ok, got it, Need to generate keys in azure management portal. Keys = Client Secret


  12. Thushara says:

    Is it possible to send an authorization request to an API (with the username /password) and get the authentication code without the user having to go in to…/wsfederation ?

    We got an in house background application which syncs data between mailbox contacts.

    We are using a service account and we know the password for that account.    

  13. There is no way in the code flow to avoid username/password. We're working on a client credential flow for later this fall that will give you the functionality required to run background services. For this you will not need a username/password, but the application will directly assert its identity and authenticate as itself.

  14. Thushara says:

    Hi Matthias,

    Thanks for your comment. Do you know if this feature will be available sometime soon?

    My problem with the Token system is that for some reason (network issues etc) if we cannot refresh the token within the time frame, the background application will fail and needs to wait till the user enters the password again.

    Will the new method be a trusted certificate system between the application and office365?


  15. Exchange will support the client credential flow in Office 365 as documented here:…/dn645543.aspx. We're looking to have this ready in the 4th quarter of this year. The code flow [that we currently support] works well for web apps that implement some form of user interaction. Then you can build some form of experience for the user to signal that something is wrong with the refreshtoken to fix the situation. For unattended background apps such as service accounts the code flow is not the best approach. Client credential flow will cover this scenario better. Hope this helps. Matthias

  16. Thushara says:

    Thanks for this, this will help us.

  17. Thushara says:

    Is there a way to get the Tokens etc in XML format rather than the JSON format in POST requests to…/token ?


  18. Charles says:

    I'm having trouble with step 2…

    First problem, its returning "session_state" instead of "state". I think this breaks the spec?

    Second problem, it seems to ignore the state I do pass and return a random new one.

  19. In the redirect that comes back from AAD after your app does the authorize call, it should look similar to: https://***********omitted for brevity***5Ni9IAA&state=8b2a6bbb-69c1-40c5-ba4d-ad3e393a2a48&session_state=160f2840-8763-4324-9e2d-74d1e7f4db17

    It has "state" that the app submits and session_state. Use only "state" to verify. I just tried with my web app and it seems to do exactly this.

    Is this not what you see?

  20. All AccessTokens to Exchange Office 365 APIs are JWT format. Even if you manage to get a XML encoded token in response, our services would not be able to parse it and will 401 with "invalid_token".

  21. Thushara says:

    I’m using EWS REST APIs with OAuth2 to create contact and calendar event etc in office 365 mail box. They all works fine but Push Notifications not working.

    I have setup a Push Notification and it returns a subscription ID, but I'm not receiving any events to my call back URL.

    Is there any way to troubleshoot or see if the event notification was sent successfully or failed in the office 365 exchange online?

    In the past I have used the "Event Viewer" in the in_house exchange server VM to see these errors, do we have a similar thing with exchange online?

  22. Thushara says:

    Just to add more info on the above post "Thushara 17 Jul 2014 3:43 PM",

    I have checked the network traffic on my call back URL Port and it shows some unsuccessful HandShakes going on. I have uploaded a self signed certificate to the Azure Management Certificates section.

    Is this the correct place to set the certificate for push notifications ?

    Will a self signed certificate work for push notifications ?


  23. Gopika says:

    Hi Matthias

    Thank you for this tutorial. I followed each of your step and I was able to authentication and also get the calendar events for the user from o365.

    I have a doubt. Is there anyway to get the events of all the users that are there in the organization and also can I get the events without using oauth token…using something like app token? Is it there for O365?



  24. App Tokens is something we want to support in the next few month. Right now we only supported delegation flows where an application acts-on behalf of a specific user for a set of permissions. As part of this flow we limit access to the mailbox of the authenticated user (as expressed in the token).

    App Tokens will directly authenticate the app and when available will allow your app to get to all events of all users (if an administrator consented so).

    Hope this helps,


  25. Gopika says:

    Thanks Matthias.

    I am not sure if I understood you properly.

    My need was to get calendar events while a cron runs. Here we are not able to use user token right.

    So I saw something called grant type client credentials. Does that help me in this context? But when I tried to get the access token in POSTMAN using client_credentials, it is giving me Request method not allowed error.

    Any ideas?



  26. Jude Aloysius says:

    Hello there,

                   Is there a way I can get list of all the users in an Office365 domain using administrative credentials?

  27. Hi Jude, yes. You can use the AAD Graph API to get a list of users. You need to configure your application for Directory.Read permissions. More info see:…/jj126255.aspx.

    Thank you!


  28. Hi Gopika, we are working on supporting the client credential flow as described in "…/dn645545.aspx" for later this year. This is essentially an app-only call to our Rest services.

    Thank you!


  29. Gopika says:

    Thank You Matthias. Waiting for it

  30. Al says:

    Hi Matthias,

    Is there any update regarding the client credential flow support?

    And will it be available for the Files REST API?



  31. No update yet regards Exchange support for client credential flow. Still working on it. I plan to post another blog as soon as this is complete.

  32. Kushan Randima says:

    Hi Matthias,

    Thank you so much for posting this article.. This is really important for the project that I am currently working on.


    K.K.Kushan Randima Bsc .(Hons).

    Software Engineer

    Davton Ltd

  33. Kirthiraj says:

    Where to get the Client Secrete for my app. I Have create a web based app. I am able to see only Client-id.

  34. Chris Walters says:

    Matthias has what you reference below been completed as yet ? Thanks.

    "Hi Gopika, we are working on supporting the client credential flow as described in "…/dn645545.aspx" for later this year. This is essentially an app-only call to our Rest services."

  35. Jamaican Chris says:

    I am having trouble retrieving data using the Office365 API. I have a Java service application which has no user interface which needs to retrieve data from Office365. I registered the app in Azure and received a Client ID and Client Key. I am able to successfully invoke the "<tenant ID>/oauth2/token" URL and receive a valid access token. However, when I then take that access token and add a "Authorization: Bearer <access token>" header and make a call to "…/" , I get the following error:

    "x-ms-diagnostics →3001000;reason="There has been an error authenticating the request.";category="invalid_client""

    Any idea how I can resolve this ?

  36. Client credential flow aka "App-Only" for service apps is not yet available on the Office 365 Rest APIs. We're targeting about end of the year to have this deployed in production.

  37. Andrew Joll says:

    Just adding my voice to the request for client credential flow – one of my clients has asked me for a solution with a "weeks" not "months" timeframe! It looks like I'll need to just write a COM class using EWS to interface to their line of business application, but REST would have been far cleaner. Is there a more specific timeframe than "about end of year" – because mid November is about the end of the year!

  38. Paolo says:

    I am trying to create an app to allow any user to connect to their Office365 account and browse their contacts with the Graph API's.

    After struggling a lot, I found your article and tried to add some of the information you suggest to add, like state and resource in the authorization request, but the result is always the same: when I try to access a resource on the Graph API, I get a 401 error.

    I wonder if I am trying to do something that it is not possible to do.

  39. Georg Brandemann says:

    i'm currently working on an application that uses the older SOAP/XML Exchange webservices and the autodiscovery mechanism to access data.

    Is the possibility of using the oAuth "client credetial flow" also planned for these APIs? And is there a timeframe when this will be possible?

    Currently i can request an oAuth access token and add it in the "Authorization: Bearer" HTTP Header but i still receive 401 response codes, so it seems that the token is ( not yet ? ) accepted as authentication option.

  40. @Georg: Yes, this should work. You can actually do something like: "exchangeService.Credentials = new OAuthCredentials(authenticationResult.AccessToken);" where authenticationResult is the result of using ADAL to aquire the token. Can you capture the x-ms-diagnostics response header for more details why it fails please. Also when you decode the token at if you could check what is in the "scp" claim. EWS only works when full mailbox permissions are requested. EWS Soap does not support granular permissions like the Rest APIs do.

  41. @Paolo: That is possible. The app needs to request Directory read rights, and then you do a request against "string api = String.Format("{0}{1}/users?api-version=2013-04-05", appConfig.GraphResourceUri, tenantId);", where appConfig.GraphResourceUri is "" and tenantId is the ID of the tenant of the directory you like to browse. Note that directory read permissions can only be given by an admin, so your app needs to pass "prompt=admin_consent" with the authorize request.

  42. @Andrew: Client Credential flow is available now. I am currently working on a blog that describes how-to. Hopefully I have this done soon. Stay tuned 🙂

  43. ZeusT says:

    @Matthias: I have been a quiet lurker, greatly anticipating the release of the above mentioned blog. I feel like a cat on its ninth life with curiosity waiting to finish me off! 🙂

    Any idea when the magical beast of Client Credential Flow Blog Post will be unleashed?

    Thanks for all the hard work on these APIs for us!

  44. Konstantin isaev says:


    I was able to obtain a token via client_credentials flow but this token s not a correct one – according the x-ms-diagnostics header, it has too low protection level, 1 instead of 2.

    Registered my app via direct access to Office365 AD account by Visual Studio "Add connected service" feature.

    Can't use acess code flow because it does not work in sandboxed frame for me… (windowopen returns undefined (and even totally replaced by MS). Guess it related to some error messages in chrome cosole abount PostMessage failures.

    In sandboxed iframe request that works well in normal browser window, with itemId in request url, are result to 404.11 (double escaping) (were able to test it in only in chrome).

    Thanks for help.

  45. @Zeus, than you! It is finally there. Sorry it took a bit longer. I also wrote a sample web app using this on git. The git link is in the post. Have fun!…/10587970.aspx

    @Konstantin. You cannot currently register certificates with your app in other way than directly through the manifest in the app registration in Windows Azure portal (see my blog on step-by-step how to do this). We hope we can improve on this in the future.

  46. Aishwarya G says:

    Hi is there a way to get the calendar mail and contact details of a user by using the access token of the admin when i give that prompt= admin_consent ?

  47. Rohit says:


    I am trying to use the JS client library sample code to access a logged in user's contacts and email and fill in a table for him to select.

    Could you point me to a sample code / existing implementation where I can have a look ?

    Or if you can help me in the current thing where I am stuck –

    This below function is what I have added so far. This function gets called when a user clicks on 'Outlook' on our site.

    But the console here displays an error – O365Auth is not defined which is understandable. But I am not able to get how and where to define the auth. And since mine is a simple website, would the user get redirected to microsoft for loggin in or can that be done in the form of a popup?

         // Authenticate with Outlook

         function authOutlook() {

    $button = $("#outlook-login-button");


    var organizerContact = [];

    var authContext;

    var authToken; // for use with creating an outlookClient later.

    authContext = new O365Auth.Context();


     .then((function (token) {

     authToken = token;

     // The auth token also carries additional information. For example:    

     userName = token.givenName + " " + token.familyName;

     }).bind(this), function (reason) {

     console.log('Failed to login. Error = ' + reason.message);



    var outlookURL = "…/contacts";




    dataType: 'jsonp',


    console.log('Get request response – '+response);




    var outlookClient = new Microsoft.OutlookServices.Client('…/v1.0&, authToken.getAccessTokenFn(';));'DisplayName asc').fetch().then(

    function (result) {

    result.currentPage.forEach(function (contact) {


    contact.emailAddresses.forEach(function(emailAddress) {

    console.log('*  ' + emailAddress.address);



    }, function(error) {





  48. Yuri B. says:


    I'm trying to use this mechanism to authenticate in IMAP client. I get an access token on google and and using it on IMAP client and it works fine, but in office 365 outlook it doesn't work. This auth mechanism can be used only on API or it can be used in IMAP? And MS supports office 365 oauth2 in imap?

  49. Yuri B. says:

    I found issue. Office 365 Outlook not support OAuth authentication using IMAP. OAuth using only for API.

  50. Karol Jochelson says:

    Hi Matthias,

    Thank you for the article. Could you please confirm something for me. I understand that all of there solutions require a web browser and a user to authenticate in the browser? Is there a authentication solution where now browser interaction is required? I want to simply have a back office service that creates and deletes events in users calendars without them having to grant me access.

    Thank you.

  51. Hi Karol, client credential flow does not require a web app for the runtime of your service. The part that requires a web experience right now is the installation of the service app into a tenant in case your service is a multi-tenant service that other companies can acquire. This acquisition of the service app into another tenancy is web driven so an admin of the other organization must "consent" that this app is allowed as a service app in the organization. Note in case you build a service for only one single tenant, this is not required. Meaning if the service runs in the same organization/tenant that the service is registered as an app (by the admin of the org) then no additional steps are necessary.

    Does this help?

    Thanks, Matthias

  52. Nigel Dewar says:

    Hi, I have followed your advice exactly. Got auth token, then access token for exchange apis.

    But when tying to do a get to a resource, say contacts, or mail or anything, I keep getting a 401 unauthorized.

    I have double, triple, quadruple checked the form of the GET making sure that everything is correct but getting nowhere. I have researched heaps of info online but to no avail.

    I'm absolutely stumped as to why I cannot use my Good access token to access to API endpoints. Not sure if anyone else is facing this, but this has pretty much taken up a whole day and still going no-where.

    Any help would be greatly appreciated.

  53. Bryan O'Malley says:

    I've gotten all of this to work for one specific users.  What I'm trying to do it give one super-user access to everyone's email.  No matter what permissions I give the user in the Azure configurations or in their unique Office365 profile I cannot get past the 403 error.

    Any tips?  Thanks!

  54. Anton says:


    I'm using next scopes for requesting `access_token`: [u"openid", u""]. But in response from u'; I don't see `refresh_token`! Should I add another scope for retrieving it?

  55. Prabin says:


    can i limit login with specified domain email only.

    I want to give only access to certain email with my domain name. Please sugges

  56. Konstantin says:

    Hello Matthias,

    Im having the same problem as Anton above me, that is that I dont get a refresh token, only a access token, can you help me out?



  57. Sorry, it seems like I missed some posts not getting notification e-mail but for the last one from Konstantin. Here are some steps that would help to understand better whats' going on. Please send the info below to me at my work address if you like me to take a closer look.

    1. What authorization endpoint do you use, and the request: if you use the v2 endpoint scopes are requested dynamically and a refresh token must be requested using "offline_access" scope. This is much different than in the v1 model, where scopes are pre-registered with the app registration and a refresh token is always returned w/o explicit scope.

    2. What is the API endpoint you try to address: was this API endpoint requested in the authorize request – esp. important for next step

    3. What is the audience in the token: Use tools such as to decode the content of the access token and verify that the "aud" claim is indeed the hostname of the API you call. Check that when you request the token you have the correct resource parameter specified (v1 model) or encoded in the scope (v2 model, e.g.

    4. What are the details of the error (401 or 403) returned by the API (if you make it so far): Mail APis return a x-ms-diagnostics response header that has more details why esp. a 401 occurred. Other APIs will return a JSON payload with more details.

    Hope that helps for next steps.



  58. Kian says:

    Hello Matthias,

    Can you please provide any help/links for implementing App Tokens….is it relased in new version ;

     i can see the Last updates[response on 7 Aug 2014 3:39 PM to Gopika ] from you that , it will be released soon..

  59. Daniel says:

    Can you read events from Shared Mailboxes using this? I keep getting Access Denied errors when trying to access a shared mailbox

  60. Peter F says:

    Like Daniel I also would really appreciate any advice on how to access a Shared Mailbox as I get 403 Forbidden

  61. Sharath says:

    Same is the case with me. Can any one please let me know is there a possibility to access shared calendar events using outlook rest api.

  62. The “here” link in “Step One: Registering your application” is broken.

    1. Thanks Andrew! I’ll update it with Azure’s new link.

  63. Santosh Khavekar says:

    Hi , I was looking to integrate Outlook calendar API in my Project,Just I want to check Create Event , Delete Event and Update Event with Developer Console first Like Google Have There Google Developer Console.We can check Google Calendar API using It,like same way Can we have console for test Outlook Calendar API?

    Thanks in Advance.

    1. Currently our OAuth Sandbox allows you to test GET requests. We’re updating it to support POST, PATCH and DELETE so that you can test the other operations, so this should be available to you soon.

  64. Yasir Farooqui says:

    We are developing an app that syncs user’s emails and calendar events through subscription mechanism. We are facing a strange issue that revokes the user’s refresh token after 14 days. We are also using the updated refresh token every time we receive it. It only happens for few users and works for others. We are also kind of sure that passwords were not changed by those users for whom we lost refresh tokens. In short the refresh token expiration seems to be a bit inconsistent.

    1. Sorry to hear that! If you’re sure that you’re updating your stored refresh token every time you get a new token, could you please post details on Stack Overflow and tag it with the “azure” tag?

  65. Doron says:

    Hi Matthias,

    I manage to authenticate properly to O365 when the user is a regular user. However, when the user is a federated user the authentication fails. What has to be done differently?


  66. Travis says:

    Working in Java, trying to connect to SMTP to see inbox using OAUTH tokens. I have obtained the access_token, refresh_token, and email. From what I understand I should be able to connect.

    I have obtained the email, refresh_token, and access_token using the following code:


    public String getOAuthDialog(Http.Request request) {
    + “?client_id=” + config.getClientId()
    + “&redirect_uri=” + getOutlookLoginRedirect(request)
    + “&response_type=code”
    + “&” +
    “” +
    “” +
    + “&state=” + stateGenerator.generate();

    So the scopes I am using are mail.send, mail.readwrite,, offline_access, openid, email, and profile (although I am fairly confident I do not need all of these -> goal is to read inbox and send emails, while also getting email and name if they exist).

    I am then connecting to SMTP server with the following code:

    private SMTPTransport getSMTPConnection(User user) {
    try {
    String accessToken = user.getAccesstoken();
    for (int tries = 1; true; tries++) {
    try {
    return OAuth2Authenticator.connectToSmtp(“”,
    } catch (AuthenticationFailedException e) {
    // Try refreshing once, and then fail if we get this exception again
    if (tries > 1) {“{}”, e);
    throw new RuntimeException(“Failed to connect to Outlook SMTP for ” + user.getOutlookUid() + “. ” + e.getMessage(), e);

    // Token expired; we need a new one“Exception connecting to Outlook SMTP for ” + user.getOutlookUid() + “: ” + e);

    accessToken = updateAccessToken(user);
    } catch (Exception e) {
    log.error(“Email send failure through Outlook SMTP. ” + e.getMessage());
    throw Throwables.propagate(e);

    As you can see, I make two attempts just to account for the access_token expiring. Essentially I call that function, then call

    smtpTransport.sendMessage(message, message.getAllRecipients());
    on a message I have created.

    The code that actually connects to the server is here:

    public static SMTPTransport connectToSmtp(String host, int port, String userEmail, String oauthToken, boolean debug)
    throws Exception {

    Properties props = new Properties();
    props.put(“mail.smtp.starttls.enable”, “true”);
    props.put(“mail.smtp.starttls.required”, “true”);
    props.put(“mail.smtp.sasl.enable”, “true”);
    props.put(“mail.smtp.sasl.mechanisms”, “XOAUTH2”);
    props.put(“mail.smtp.sasl.mechanisms.oauth2.oauthToken”, oauthToken);
    Session session = Session.getInstance(props);

    URLName unusedUrlName = null;
    SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
    // If the password is non-null, SMTP tries to do AUTH LOGIN.
    String password = “”;
    transport.connect(host, port, userEmail, password);

    return transport;

    Okay, now I can get to the most frustrating part… I have used the “connectToSMTP” method to connect to Gmail and it worked perfectly.


    So ultimately my question is “what am I doing wrong?” or “what can I update to be able to send emails through Outlook”? I have seen that Outlook has a REST API, but that is plan B. Is there something different about Outlook vs Gmail?

    Some things I have considered:

    1. Scope did not request enough access (so I probably am asking for too much now)
    2. access_token was stored incorrectly or encoded in some way (tried decoding it from base_64 which provided nothing). I am able to use my refresh_token to update the access_token so that tells me I am probably storing them correctly.
    3. I tried passing null for the password. Also passed in the actual password and that WORKED, but I have the access_token and refresh_token so I shouldn’t need to ask for their explicit password. Also this would be dangerous and sketchy to ask of users.
    4. I tried manually connecting to the smtp server using “openssl s_client -crlf -starttls smtp -connect”, but it seemed to think my access_token was wrong “535 5.0.0 OAuth failed: OAuth authentication failed due to Invalid token. Code -2147184118” That number when taken two’s complement and converted to hex is 0x8004920a. Helped in searches but was to no avail.
    5. I have done a lot of searching for this and will continue now to post this everywhere. A lot of resources for it working with Gmail, but as previously stated I already have it working for Gmail. Something seems different for Outlook. Also I have encountered lots of posts regarding email forwarding on an email client… I am semi-creating an email client so going through settings doesn’t help me.

    Another concern that a buddy of mine had was that my access token was really long, contributing to what the manual smtp server claimed. It is 1188 characters long. It’s something like ‘EwB4Aul3BAAUo4xeBIbHjhBxWOFekj4Xy2…x9stHxi2K/VFggE=’ (obviously I hid most of the characters).

    Preemptive THANK YOU for anyone who offers advice or finds my issue. Especially why I can pass in the email password and that fails, but using the oauth access_token fails.

  67. Hello I´m Max software engineer at Avantica. I´m develop a java web app and I need use our domain as login authentication and then read some emails from our inbox. How can I use the EWS with java I saw some examples using tokens but I need more details. Thanks in advance…!

  68. Mrinal says:

    I’ve registered one app. But on triggering the authorization flow, I receive this error – Additional technical information:
    Correlation ID: d5a2b2ac-9624-49a0-b630-3eb0bdd5dd57
    Timestamp: 2017-04-11 11:15:55Z
    AADSTS90093: This application requires application permissions to another application. Consent for application permissions can only be performed by an administrator. Sign out and sign in as an administrator or contact one of your organization’s administrators.

    Can you please help in resolving this?

    1. It sounds like you registered for app permissions rather than delegated permissions. You’ll need to fix up your registration or create a new one. If you can’t figure it out please post on Stack Overflow.

Comments are closed.

Skip to main content