3

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?

Jason Johnston
  • 17,194
  • 2
  • 20
  • 34
Dmitriy
  • 31
  • 1
  • 3

1 Answers1

5

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.

Community
  • 1
  • 1
Jason Johnston
  • 17,194
  • 2
  • 20
  • 34
  • Thank you for the prompt response, Jason. This is going to be an integration script running in the cloud (not Azure) on the regular basis. It is supposed to synchronize Office 365 data with our system. So if I understand correctly the application type is daemon/service. There is no web component. Are you sure that clientSecret is not required? The Azure AD issued it to me along with the clientID and access endpoint. – Dmitriy Jun 29 '15 at 20:52
  • Is there a way to use clientSecret instead of certificates. Azure AD is creating the secured channel anyway so logically why would I need some additional certificate to identify myself if I already have clientID, clientSecret and unique endpoint? – Dmitriy Jun 29 '15 at 21:01
  • No, Exchange requires a certificate assertion when you are using application permissions/client credential flow. This is to prevent the scenario where someone obtains your client ID and secret and gains global access to all organizations that consent to your app. You can use a self-signed cert for this. I'll see if I can come up with a sample script. – Jason Johnston Jun 30 '15 at 03:24
  • I just updated my answer with a link to the sample I'm working on. Hopefully this will help with token acquisition. – Jason Johnston Jun 30 '15 at 18:08
  • Thanks again Jason. You are being very helpful. I managed to make it working using node-outlook, jws and simple-oath2 libraries. I would definitely like to see your approach in the official Microsoft documentation. – Dmitriy Jul 01 '15 at 04:03