I'm asking this question on the back of a previous question I raised, as the scope of the question has changed somewhat but that might be worth reading first for background info.
I'm trying to programatically obtain data out of our Dynamics CRM instance, using a single set of admin credentials within a Node powered Express app. This Express app is hosted on a separate server outside of our network where CRM is hosted. The app will then request, process and serve CRM data back to any logged in user who has access (controlled by roles/permissions within the app), meaning an end user only has to login into the Express app.
From my web browser, if I visit our on-premise CRM endpoint: https://my.crm.endpoint
, I get prompted for a username and password.
If I provide correct credentials, I am authenticated and have full access to the CRM, allowing me to query the API.
Example https://my.crm.endpoint/api/data/v8.2/contacts?$select=fullname,contactid
This returns a lovely JSON object containing all the data I want :)
NOW! Under the covers, I can see that it is using NTLM to authenticate, of which I have little knowledge :/ Having read up a little and watched a few YouTube videos, I have a basic understanding of the challenge/response mechanism but I'm still unsure as to how to proceed.
NB: I have read this from Microsoft which describes the mechanism, but doesn't give any specific examples. I don't even know what hashing algorithm should be used, or what headers to set etc.
Question Can anyone provide any sort of detail as to how I can authenticate with our CRM using NTLM from a Web App (Express in my case)?
Steps I can see the browser making...
- Visit
https://my.crm.endpoint
- 302 Redirected to:
https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wctx=rm%3d1%26id%3dfaf0791c-6a3a-4c4e-9e69-9dfa8fd4c2e8%26ru%3d%252fdefault.aspx&wct=2018-04-20T10%3a12%3a37Z&wauth=urn%3afederation%3aauthentication%3awindows
- Prompted for user credentials
- ** enter credentials**
- A whole bunch of stuff happens here and I get a little lost but looks like it gets a couple of 401's then a POST is made to the
https://my.crm.endpoint
. Another 302 is shown, then finally a GET to the actualdefault.aspx
page. - I then have access to CRM.
NB: Once authenticated, I can see three cookies that have been set and which are sent when querying the api example above. These cookies are MSISAuth, MSISAuth1 and ReClientId.
If I'm missing any crucial info, please let me know and I'll provide what I can!
UPDATE
I have just installed httpntlm module and attempted to authenticate using this...
let httpntlm = require('httpntlm');
httpntlm.get({
url: 'https://my.crm.endpoint',
username: '<my.email@address.com>',
password: '<mypassword>',
workstation: '', // unsure what to put here if anything?
domain: '' // unsure what to put here if anything?
}, function (err, res){
if(err) return err;
console.log(res.headers);
console.log(res.body);
});
The response I get is this...
{ location: 'https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wctx=rm%3d1%26id%3d93a4c6fd-5b17-4a2b-965f-07af5e96b08f%26ru%3d%252fdefault.aspx&wct=2018-04-20T14%3a08%3a00Z&wauth=urn%3afederation%3aauthentication%3awindows',
server: 'Microsoft-IIS/8.5',
req_id: '298acefc-53aa-46fa-96c4-e5d8762b1fd2',
'x-powered-by': 'ASP.NET',
date: 'Fri, 20 Apr 2018 14:08:00 GMT',
connection: 'close',
'content-length': '397' }
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="https://fs.our.domain/adfs/ls/?wa=wsignin1.0&wtrealm=https%3a%2f%2fmy.crm.endpoint%2f&wctx=rm%3d1%26id%3d93a4c6fd-5b17-4a2b-965f-07af5e96b08f%26ru%3d%252fdefault.aspx&wct=2018-04-20T14%3a08%3a00Z&wauth=urn%3afederation%3aauthentication%3awindows">here</a>.</h2>
</body></html>
Anyone able to shed any light on what I actually need to be doing?! :-/
UPDATE 2
Following @markgamache comment, and having read the suggested docs, we are indeed using WS-Fed! As the Wa=signin1.0
parameter informs the browser to pop up a login box, does this make it impossible to achieve this programmatically, without additional user interaction?