Skip to content

Instantly share code, notes, and snippets.

@sampathsris
Last active September 18, 2018 03:44
Show Gist options
  • Save sampathsris/0f4be7166879b8c3131fc31382ff00b4 to your computer and use it in GitHub Desktop.
Save sampathsris/0f4be7166879b8c3131fc31382ff00b4 to your computer and use it in GitHub Desktop.
Authenticate an IFS Aurena endpoint with DB Provider in Node.js and retrieve a projection
/**
* This is a crude hack to connect to an IFS Aurena endpoint using IFS Database
* Identity Provider, and then query for projections from Node.js. Not sure the
* purpose of this, but feeling it would come handy for something. You can do
* the same in Postman, but I'm feeling the stateful client (read: with cookies)
* will be helpful in chaining requests.
*
* Tested with:
* "dom-parser": "^0.1.5",
* "request": "^2.87.0",
* "request-promise-native": "^1.0.5"
*/
const request = require('request-promise-native');
const url = require('url');
const DomParser = require('dom-parser');
const parser = new DomParser();
const WEB_ROOT = '/main/ifsapplications/web/';
const WEB_LOGIN = '/openid-connect-provider/login';
const OPENID_AUTH = '/openid-connect-provider/idp/authorization';
const APP_INDEX = '/main/ifsapplications/web/index.html';
// gets 'action' attribute from an HTML form element
const getFormAction = form => form.attributes.reduce((acc, curr) => {
if (curr.name === 'action')
return curr.value;
return acc;
}, null);
// gets 'action' attribute of the first form in a DOM
const getFormActionFromDOM =
dom => getFormAction(dom.getElementsByTagName('form')[0]);
// gets 'action' attribute of the first form in an HTML source
const getFormActionFromText =
text => getFormActionFromDOM(parser.parseFromString(text));
// returns a function that checks the HTTP response status and a given final redirect URI path
const checkRequestSuccess = expectedPath => res => {
if (res.statusCode === 200 && res.request.uri.pathname === expectedPath) {
return res.body;
}
throw new Error({
target: expectedPath,
statusCode: res.statusCode,
uri: res.request.uri
});
};
// authenticates with an Aurena endpoint and returns promise that resolves to an stateful client
const auth = (HOST, username, password) => {
const client = request.defaults({
jar: true,
followAllRedirects: true,
resolveWithFullResponse: true
});
return client
// Initial request:
.get(url.resolve(HOST, WEB_ROOT))
// Client should eventually redirect to the login form.
.then(checkRequestSuccess(WEB_LOGIN))
// Grab the login form's redirect URL,
.then(getFormActionFromText)
// Prepare request options for next step, with form action URL from previous step.
.then(loginUrl => ({
url: url.resolve(HOST, loginUrl),
form: {
username,
password
}
})
)
// Use the options object created in the last step for a POST request.
.then(client.post)
// Last POST request should redirect to OpenID Auth page
.then(checkRequestSuccess(OPENID_AUTH))
// ...which contains an scripted redirect using a form. Grab the form data
// and prepare the options for next request.
.then(body => {
let dom = parser.parseFromString(body);
let redirectUrl = getFormActionFromDOM(dom);
let params = dom.getElementsByTagName('input').reduce((paramsObj, item) => {
let paramName, paramValue;
item.attributes.forEach(attr => {
switch (attr.name) {
case 'name':
paramName = attr.value;
break;
case 'value':
paramValue = attr.value;
break;
default:
break;
}
});
paramsObj[paramName] = paramValue;
return paramsObj;
}, {});
return {
url: redirectUrl,
form: params
};
})
// Do a POST request with options constructed in the last step.
.then(client.post)
// Check the success of the last step.
.then(checkRequestSuccess(APP_INDEX))
// This should be the index page. return a promise that resolves
// to the client we just built.
.then(() => client);
};
module.exports = auth;
/**
*
* Needs following environment variables to be set:
* IFS_HOST=https://<host>:<port>
* IFS_USERNAME=<username>
* IFS_PASSWORD=<password>
*
* Also need following set if the endpoint uses a self-signed certificate:
* NODE_TLS_REJECT_UNAUTHORIZED=0
*
*/
const auth = require('./aurena');
const HOST = process.env.IFS_HOST;
const USERNAME = process.env.IFS_USERNAME;
const PASSWORD = process.env.IFS_PASSWORD;
const PROJECTIONS = url.resolve(HOST, '/main/ifsapplications/projection/v1/');
const NAVIGATOR_ENTRIES = 'ClientNavigator.svc/ClientNavigatorMain?$orderby=ParentId,SortOrder,Label';
// let's try authenticating with an endpoint and try to call a projection: in this
// case, ClientNavigator.svc/ClientNavigatorMain.
auth(HOST, USERNAME, PASSWORD)
.then(client =>
client
.get(url.resolve(PROJECTIONS, NAVIGATOR_ENTRIES))
.then(res => res.body)
.then(JSON.parse)
.then(data => data.value)
.then(navigator => 'Retrieved ' + navigator.length + ' navigator entries')
.then(console.log)
// If all goes well, this should print something like:
// Retrieved 357 navigator entries
)
.catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment