3
votes

I am trying to create a NodeJS daemon/service application for accessing Office 365 mail/contacts using node-outlook library. I was able to create an Office 365 trial subscription and register my application. So now I have access endpoint URL, Client ID and Client Secret for my app. Here is my code:

var outlook = require("node-outlook");
var token;
process.env.DEBUG = true;
var fs = require('fs');
var credentials = {
    clientID: "<id>",
    clientSecret: "<secret>",
    site: "https://login.microsoftonline.com/<my-tenant-id>",
    authorizationPath: "/oauth2/authorize",
    tokenPath: "/oauth2/token",
    useBasicAuthorizationHeader: false,
    rejectUnauthorized: false,
    ca: fs.readFileSync('pki/some.pem', { encoding: 'ascii' }),
};

var oauth2 = require('simple-oauth2')(credentials);
oauth2.client.getToken({}, saveToken);

function saveToken(error, result) {
    if (error) {
        console.log('Access Token Error: ', error);
        return;
    }
    token = oauth2.accessToken.create(result);
    var outlookClient = new outlook.Microsoft.OutlookServices.Client(
        'https://outlook.office365.com/api/v1.0',
        getAccessToken);
    outlookClient.me.folders.getFolder('Inbox').messages.getMessages().fetchAll(10).then(
        function (result) {
            console.log('Success: ', result);
        },
        function (error) {
            console.log('Error: ', error);
            console.log('Headers: ', error.getAllResponseHeaders());

        });
}


function getAccessToken() {
    var deferred = new outlook.Microsoft.Utility.Deferred();
    if (token.expired()) {
        token.refresh(function (error, result) {
            if (error) {
                console.log("Refresh token error: ", error.message);
            }
            token = result;
            deferred.resolve(token.token.access_token);
        });
    }
    else {
        deferred.resolve(token.token.access_token);
    }
    return deferred;
}

And this is the result:

Error:  { UNSENT: 0,
  OPENED: 1,
  HEADERS_RECEIVED: 2,
  LOADING: 3,
  DONE: 4,
  readyState: 4,
  onreadystatechange: [Function],
  responseText: '',
  responseXML: '',
  status: 401,
  statusText: null,
  open: [Function],
  setDisableHeaderCheck: [Function],
  setRequestHeader: [Function],
  getResponseHeader: [Function],
  getAllResponseHeaders: [Function],
  getRequestHeader: [Function],
  send: [Function],
  handleError: [Function],
  abort: [Function],
  addEventListener: [Function],
  removeEventListener: [Function],
  dispatchEvent: [Function] }
Headers:  content-length: 0
server: Microsoft-IIS/8.0
request-id: 07931460-4fbf-4028-bc7b-fe350c240a1b
x-calculatedbetarget: BLUPR10MB0594.namprd10.prod.outlook.com
x-backendhttpstatus: 401
x-ms-diagnostics: 2000010;reason="The access token is acquired using an authenti
cation method that is too weak to allow access for this application. Presented a
uth strength was 1, required is 2.";error_category="insufficient_auth_strength"
x-diaginfo: BLUPR10MB0594
x-beserver: BLUPR10MB0594
x-powered-by: ASP.NET
x-feserver: CY1PR01CA0008
www-authenticate: Bearer client_id="00000002-0000-0ff1-ce00-000000000000", trust
ed_issuers="00000001-0000-0000-c000-000000000000@*", token_types="app_asserted_u
ser_v1", authorization_uri="https://login.windows.net/common/oauth2/authorize",
error="invalid_token",Basic Realm="",Basic Realm=""
date: Mon, 29 Jun 2015 18:34:32 GMT
connection: close

I already tried different certificates in PEM format with "ca" attribute of "credentials". The error is the same.

So first of all, can I use self-issued PKI certificates? What are the requirements for the PKI certificates so they would work with Azure AD? I use SHA1 algorithm and 2048-bit encryption. Is it enough of those?

This is what I used as a manual: http://blogs.msdn.com/b/exchangedev/archive/2015/01/21/building-demon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow.aspx

Also I looked at the source code of simple-oauth2 library and found that "ca" is the only option the one can use for the PKI settings. It is explicitly checked. All the other PKI related nodejs https options (cert,key,passphrase,...) are just ignored and never get to the actual request code.

Am I the only one who ran into the problem?

1

1 Answers

4
votes

The error is because the use of a secret instead of a cert in the auth request. In your case, you don't want to use a secret at all. It sounds like the real issue here is if the simple-oauth2 library will handle getting the auth request into the format that Azure expects. The gory details of the format are here: Office 365 Rest API - Daemon week authentication.

I looked at README for simple-oauth2 and their examples of client credential flow use a secret instead of a cert-based assertion. Looking at the config code I don't see an ability to do any cert-based auth there, so this library may not work for this scenario (unless I missed it).

UPDATE: The good news is that the adal-node library does support using a certificate, and it's fairly easy to use. The tricky part is getting the certificates ironed out.

I've started a sample Node.js script, which you can find here: https://github.com/jasonjoh/node-service. So far it only retrieves the token using the certificate from Azure. The README has all of the steps I went through to get the certificates sorted out.