Office 365 APIs and Node.js
UPDATE: The node-outlook library that is discussed in this post has been updated with a new interface that does not use the Cordova library and is much simpler to use. It's recommended that new apps use the newer interface. The tutorial linked at the end of the article has been updated to use the newer interface.
When we launched the Office 365 APIs, the Visual Studio folks released some very helpful client-side implementations of these APIs, making it much easier to get started. One of these implementations, the Microsoft Office 365 APIs Client Libraries for Cordova Applications, got a lot of attention from JavaScript developers. However, there was some confusion about the library and what it was designed to do. As the name suggests, this library was created to be used in applications built on the Cordova platform, and more specifically, for Multi-Device Hybrid App projects in Visual Studio. Developers who tried using it in a web app soon ran into issues. It just wouldn't work.
With all of the interest in using this library from a web app, when I was asked if we could modify the library to make it work in Node.js, I said "why not?"
Finding the problem
As it turns out, the big stumbling block is that the library uses the AJAX XMLHttpRequest object to send API requests. AJAX is all client-side, and not available to server-side platforms like Node.js. So it makes sense that it doesn't work in web apps.
The solution
Luckily, someone clever already provided a solution for this. The XMLHttpRequest for node.js module sounds like exactly what we need! If we can use this, we can avoid having to change the Cordova library much at all.
With that out of the way, the question left is how to load this non-Node module. As before, someone clever has already found the solution to that. With this trick in my toolbox, I decided to write a Node module to wrap the Cordova libraries (to be specific, just the Mail, Calendar, and Contacts APIs).
The node-outlook module
The code for the module was incredibly simple. All I needed to do was create an index.js
file and put the following in it:
var fs = require("fs");
var path = require("path");
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var currentDirectory = path.dirname(fs.realpathSync(__filename));
exchangefile = fs.readFileSync(path.join(currentDirectory,
'./exchange-lib/exchange.js'), 'utf8');
eval(exchangefile);
utilityfile = fs.readFileSync(path.join(currentDirectory,
'./exchange-lib/utility.js'), 'utf8');
eval(utilityfile);
exports.Microsoft = Microsoft;
The exchange.js
and utility.js
files were copied straight from the Cordova library NuGet package. Pretty simple right? Well, it's even simpler now! I published node-outlook
on the NPM registry, so you can save yourself the hassle and install via NPM. Or if you prefer, get the code on GitHub.
Using node-outlook
For a full sample that uses this library, check out node-mail. I'll cover the basic requirements here.
In order to use the library, you need to implement a callback function that returns an OAuth2 access token. The OutlookServices.Client
class takes this callback as a parameter to its constructor. Whenever the client class makes a call to the Office 365 service, it calls this function to get the token. How you implement the callback will depend on how you're storing and managing tokens. As an example, let's look at how node-mail
does it.
function getAccessToken(resource, session) {
console.log("getAccessToken called for " + session);
var deferred = new outlook.Microsoft.Utility.Deferred();
if (session.token.expired()) {
session.token.refresh(function(error, result) {
if (error) {
console.log("Refresh token error: ", error.message);
}
session.token = result;
console.log("NEW ACCESS TOKEN: ", session.token.token.access_token);
deferred.resolve(session.token.token.access_token);
});
}
else {
// Return the token right away
console.log("EXISTING ACCESS TOKEN: ", session.token.token.access_token);
deferred.resolve(session.token.token.access_token);
}
return deferred;
}
function getAccessTokenFn(resource, session) {
return function() {
return getAccessToken(resource, session);
}
}
The node-mail
app caches tokens in the session. The getAccessToken
function returns the cached token if it's not expired. Otherwise, it refreshes the token and returns the new token. The getAccessTokenFn
function exists to wrap getAccessToken
as a function with no parameters, which is what the OutlookServices.Client
class expects.
With authorization taken care of, using the library works just like it does in a Cordova app. For example, you could get messages in the inbox with the following code:
var outlookClient = new outlook.Microsoft.OutlookServices.Client(
'https://outlook.office365.com/api/v1.0',
authHelper.getAccessTokenFn('https://outlook.office365.com/', session));
outlookClient.me.messages.getMessages()
.orderBy('DateTimeReceived desc').fetchAll(10).then(function (result) {
result.forEach(function (message) {
console.log(message.subject);
});
}, function (error) {
console.log(error);
});
For a step-by-step tutorial, see https://dev.outlook.com/RestGettingStarted/Tutorial/node.
As always, I'd love to hear your feedback in the comments or on Twitter (@JasonJohMSFT). Feel free to report issues or submit pull requests on GitHub!
Comments
- Anonymous
March 20, 2015
Thanks for the wonderful post Jason!!! I got the setup working for my Office 365 account; but it didn't work for other users. Below are the steps I did,
- Created Office 365 Developer account
- Thought Azure AD account will be automatically created; but the system said I need to have "Azure Subscription"
- I purchased trial version of "Azure Subscription"
- Followed all the steps you have mentioned in "github.com/.../RegisterAnAppInAzure.md"
- App is registered and I am able to access Office 365 REST API's (calendar,events) using my user account
- Using OAuth2 Auth flow, I tried accessing as a different user (who has Office 365 Home account) , token and refresh token was returned. But, with this token if I access the Office 365 REST API, I am getting "401 Unauthorized Error".
- I went to my Active Directory portal; added this user explicitly and gave "User" role
- Now when I accessed Office 365 REST API, 401 error is gone. But, getting "ErrorMissingEmailAddress" error Do I need to explicitly add all the users who wants to access their Office 365 data via "App" that I registered in "Active Directory"? I am accessing "Office 365 REST API" via OAuth2 in my NodeJS application. My NodeJS application is not multi-tenant application and so I cannot have user organization Active Directory synced to my AD portal. Any users registered with our NodeJS application, needs "Sign-In with Azure/Office" option, so they can view their Office 365 data. Please let me know if I am missing any configuration.
Anonymous
March 22, 2015
Ah. Office 365 Home Accounts are currently not supported by the APIs. This is on the roadmap, but we don't have a firm date to share yet.Anonymous
March 26, 2015
Thanks for the update Jason. Is Office 365 Business Account (Business/Business Essentials/Business Premium) supported by the APIs?Anonymous
March 26, 2015
It should be. You can always try an account by going to apisandbox.msdn.microsoft.com and clicking on "My Office 365". It will let you sign in and try simple GETs against your account.Anonymous
May 05, 2015
Hey, Jason your update was really helpful & informative. Thanks for this amazing update & looking forward for your next post.Anonymous
June 25, 2015
Hi Jason. The scenario you described here works, but it doesn't match my requirements for an application. I am talking about Daemon type of application. The one without user interaction. Have you ever built those using NodeJS/node-outlook?Anonymous
June 25, 2015
No, I haven't done it personally with Node. It should work, you will just have a slightly different auth flow, and you would use the .users accessor instead of .me.Anonymous
July 29, 2015
Hey Jason, Appreciate your effort. I am trying to send mail using the outlookClientObject. outlookClient.me.sendMail(message) My code never sends off an email. I am still able to do the follwoing things, Get Mail Delete Mail Create a Calendar Event Read Contacts Add Contacts.. I am not able to send an email to someone from the code. Any assistance would be greatly Appreciated!!Anonymous
July 29, 2015
Ravi: Sorry to hear that. Make sure you have the "Send mail" permission specified in your app registration. If you do and you still have trouble, post details on Stack Overflow and tag your post with the "outlook-restapi" tag so my colleagues and I will see it.Anonymous
August 26, 2015
Hi Jason, Thanks for the post, appreciate your effort. Are you aware of any official JS client for EWS Managed APIs and not just Office365- Anonymous
April 04, 2016
@ Kaushal Not an official one, but I am author of ews-javascript-api at https://github.com/gautamsi/ews-javascript-api working on posting ews-managed-api from GitHub/officedev
- Anonymous
Anonymous
December 08, 2015
Hey Jason, Do I need to submit my app for approval others to access?Anonymous
December 08, 2015
@Anil: Nope!Anonymous
January 26, 2016
Jason, I was super-pumped to find your blog article. I want to try automating some regular calendar tasks, and this looks like the perfect place to get started! One problem - the links at the bottom of your article ('Common calendar tasks using an Office 365 client library' in particular) don't work. Where can I find this content now?Anonymous
January 26, 2016
@Tim: Oh yeah, sorry about that. The original version of the Node library used an old Cordova Exchange library that got retired. That link was pointing at the old docs for that library, but they pulled that when they retired it. So I basically redesigned a v2 interface for the Node library (node-outlook: www.npmjs.com/.../node-outlook) that doesn't use the Cordova stuff and is a little more straightforward to use. Check this tutorial: dev.outlook.com/.../nodeAnonymous
April 10, 2017
Hello Jason,I'm creating new NodeJs project. Customer wants to use Office365 mail service.All works fine but I have a question. We can login with Office 365 or Outlook.com account and grant access to our app.Can I get token (authorize) automatically without redirecting? Thank you- Anonymous
April 11, 2017
Have a look at the different OAuth flows supported by Azure. Implicit works without a redirect, but you should consider if it is the right fit for your scenario.
- Anonymous