Authenticating Zombie.js Browser against Windows Authentication - node.js

I am able to run my mocha tests using Zombie.js on my local project, but am getting a 401 error while attempting to run it on one of our dev servers.
The dev server this has to go on is Windows Server 2003. This is an organizational choice rather than my own.
This server has windows authentication for that domain that the app needs to sit on and is the reason I was getting the 401 error.
I tried setting the authentication as suggested by the zombie docs like so:
browser.authenticate().basic("user", "password");
This did not work.
Any suggestions?

browser.authenticate().basic() in Zombie.js carries out HTTP basic authentication. Your site is protected by Windows authentication, i.e. NTLM. These are different and incompatible protocols. You need to use something like node-http-ntlm, and either figure out how to interpose it in your tests so that Zombie.js can use the session it establishes, or extend the authentication abilities of Zombie.js to include NTLM.

You can try this code
In addition, the library also provides helper methods for encoding and decoding the headers used during NTLM HTTP authentication. This functionality should presently be considered experimental.
source
// npm install ntlm request agentkeepalive
var url = "https://.../ews/exchange.asmx"
, domain = ...
, username = ...
, password = ...
var ntlm = require('ntlm')
, ntlmrequest = require('request').defaults({
agentClass: require('agentkeepalive').HttpsAgent
});
ntlmrequest(url, {
headers: {
'Authorization': ntlm.challengeHeader(hostname, domain),
}
}, function(err, res) {
ntlmrequest(url, {
headers: {
'Authorization': ntlm.responseHeader(res, url, domain, username, password)
}
}, function (err, res, body) {
console.log(body);
});
});

Related

Microsoft login: Curl, Postman, and Dotnet work but Node (axios) request returns 404

I'm trying to access a graph api (specifically bookings) that only allows the Delegated (work or school account) permission. We need to be able to allow anonymous users to schedule bookings, so we created a fake microsoft user for our server to interact with their api.
I'm able to successfully authorize my user with Postman, Curl, and Dotnet, but I can't get Axios to work for the life of me, even after generating the code from Postman itself. I'm using the same exact URL in each method.
Note: my environment variables don't have typos and I'm running the code in Azure Functions. I know this is a hacky solution, but it seems to be the only way to do it using Microsoft Bookings.
Here's my code:
let data = new FormData();
data.append('grant_type', 'password');
data.append('username', process.env.MICROSOFT_USERNAME);
data.append('password', process.env.MICROSOFT_PASSWORD);
data.append('client_id', process.env.MICROSOFT_CLIENT_ID);
data.append('client_secret', process.env.MICROSOFT_CLIENT_SECRET);
data.append('resource', 'https://graph.microsoft.com/');
data.append('scope', 'https://graph.microsoft.com/.default');
const config = {
method: 'post',
url: `https://login.microsoftonline.com/${process.env.MICROSOFT_TENANT}/oauth2/token`,
headers:
data.getHeaders(),
data: data
};
axios(config).then(resp => {
console.log(resp);
}).catch(e => {
console.log(e);
});

How do we send post request with Kerberos authentication with axios on linux?

we are trying to call POST api to generate certificate for renewal of certification. However as per the 3rd party API reqirement, we need to validate Kerberos authentication. We have tried many thhings with no luck.
We are getting 401 unauthorized error when we are trying to run it.
Tech stack: Node.js, Javascript, Kerberos auth, Linux OS.
Code snippet:
const axios = require('axios');
const data = {
Request: "-----BEGIN CERTIFICATE REQUEST-----<csr key>-----END CERTIFICATE REQUEST-----",
CertificateTemplateName: "DBAPI1Y",
PrimaryEmail: "test#test.com"
};
axios.post('http://dummyurl.com/webapi/SubmitRequest', data, "Negotiate")
.then((res) => {
console.log(`Status: ${res.status}`);
console.log('Body: ', res.data);
}).catch((err) => {
console.error(err);
});
Tried this approach with no luck: How do I use Negotiate or Kerberos authentication with axios?
Can someone please help?
I was unable to find any traces of Negotiate support within axios. Your linked post says that in browsers it would work automatically, which is indeed true for any in-browser JavaScript (that uses Fetch or XMLHTTPRequest behind the scenes) – but the node CLI is a different world. When run through Node CLI, axios will use the HTTP client provided by Node, which doesn't do Kerberos.
It seems that you will need to implement this manually, by using the krb5 module to get a token, then telling axios to send it in the Authorization header – similar to Bearer tokens.
The following example seems to kind of work, although not very pretty – it cannot cope with '30x' redirects:
const axios = require("axios");
const krb5 = require("krb5");
// The service is "HTTP" (uppercase), regardless of the URL being http or https.
token = await krb5.spnego({hostbased_service: "HTTP#dummyurl.com"});
resp = await axios.post("https://dummyurl.com/webapi/SubmitRequest",
data,
{
headers: {
"Authorization": `Negotiate ${token}`,
},
// SPNEGO tokens are single-use, so if Axios tries to
// follow redirects, the 2nd request will be rejected
// as a "replay". So tell it to not even try.
maxRedirects: 0,
});
(I have no experience with writing "proper" Node.js code, especially async-based code, and while in theory it should be possible to use axios' transformRequest to dynamically get a new token for each request, I was unable to figure out how to do it within a sync function.)
In general, I would probably do the task in Python instead (which has well-maintained Kerberos integration using requests-gssapi or httpx-gssapi for Requests or httpx respectively).
Note that Kerberos doesn't ensure data integrity for HTTP, so you must still use HTTPS with it, otherwise someone could still simply MitM the requests.
Apart from the above approach suggested by #user1686, I have solved the problem using NTLM authentication. Specifically, I have used the httpntlm module.
By using the httpntlm package, we don't need to deal with kerberos packages as all the kerberos packages have many dependencies on node-gyp.
Thus, it's better to use some other solution apart from the kerberos related packages.
Note: This solution worked for me, but it will vary from use case to use case.

How to store authentication token with node and express

I have two nodejs app for client and server. Client side doesn't rely on any libraries for building interfaces. It uses pure html and express routes to the requested page.
router.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/index.html'));
});
Client sends an auth request to the server via AJAX call and receive the auth token back. I need to store it somewhere and make sure express checks if token is available or not to reroute user to login page if unauthenticated (token is not available)
I was thinking to use localStorage but it is not available/accessible under express.
$.ajax({
url: 'http://server:8080/login',
type: "POST",
data: {username, password},
dataType: "JSON",
contentType: 'application/json',
success: function (data) {
localStorage.setItem("jwtToken", data.jwtToken);
},
});
Maybe your should consider store it like a cookie and all the new requests to your server will contain the necessary information to verify the auth of the client.
So that developers don't have to deal with tokens and other aspects of an authentication system, I created an npm package. You can check if the user is logged in or not with this package as well. Here is the link:
https://www.npmjs.com/package/authenticatejs

Not able to set cookie from the express app hosted on Heroku

I have hosted both frontend and backend in Heroku.
Frontend - xxxxxx.herokuapp.com (react app)
Backend - yyyyyy.herokuapp.com (express)
I'm trying to implement Google authentication. After getting the token from Google OAuth2, I'm trying to set the id_token and user details in the cookie through the express app.
Below is the piece of code that I have in the backend,
authRouter.get('/token', async (req, res) => {
try {
const result = await getToken(String(req.query.code))
const { id_token, userId, name, exp } = result;
const cookieConfig = { domain: '.herokuapp.com', expires: new Date(exp * 1000), secure: true }
res.status(201)
.cookie('auth_token', id_token, {
httpOnly: true,
...cookieConfig
})
.cookie('user_id', userId, cookieConfig)
.cookie('user_name', name, cookieConfig)
.send("Login succeeded")
} catch (err) {
res.status(401).send("Login failed");
}
});
It is working perfectly for me on my local but it is not working on heroku.
These are the domains I tried out already - .herokuapp.com herokuapp.com. Also, I tried out without specifying the domain field itself.
I can see the Set-Cookie details on the response headers but the /token endpoint is failing without returning any status code and I can't see the cookies set on the application tab.
Please see the below images,
I can't see any status code here but it says it is failed.
These are cookie information that I can see but it is not available if I check via application tab.
What am I missing here? Could someone help me?
May you should try secure as:
secure: req.secure || req.headers['x-forwarded-proto'] === 'https'
You are right, this should technically work.
Except that if it did work, this could lead to a massive security breach since anyone able to create a Heroku subdomain could generate a session cookie for all other subdomains.
It's not only a security issue for Heroku but also for any other service that lets you have a subdomain.
This is why a list of domains has been created and been maintained since then to list public domains where cookies should not be shared amongst the subdomains. This list is usually used by browsers.
As you can imagine, the domain heroku.com is part of this list.
If you want to know more, this list is known as the Mozilla Foundation’s Public Suffix List.

Retrieving data from Pocket API (oAuth)

I need to retrieve my saved reading list from my Pocket account
and it seems that I need to acquire access token through their oAuth to make a request.
I've got consumer key for the access token and as per Pocket API documentation, the request will resemble something like this.
POST /v3/oauth/request HTTP/1.1
Host: getpocket.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Accept: application/x-www-form-urlencoded
consumer_key=1234-abcd1234abcd1234abcd1234&
redirect_uri=pocketapp1234:authorizationFinished
My question is... isn't oAuth for 3rd party apps to enable authentication via Google, Facebook account? I don't see how this idea is relevant for my website that will only require access to my own data from Pocket to share on my site.
I understand I will need to authenticate somehow so I can access my data but is oAuth the process I will need to go through to get what I need?
It seems that they support only 3 legged OAuth flow. You can use Grant in your NodeJS app, or just get access token from here.
Grant
save the following example to a file
set your key here: key:'...'
install the needed dependencies
run the file with node.js
navigate to http://localhost:3000/connect/getpocket
follow the instructions on screen
At the end you'll see your access_token.
var express = require('express')
, session = require('express-session')
var options = {
server: {protocol:'http', host:'localhost:3000'},
getpocket: {key:'...', callback:'/getpocket_callback'}
}
var Grant = require('grant-express')
, grant = new Grant(options)
var app = express()
app.use(session({secret:'very secret'}))
app.use(grant)
app.get('/getpocket_callback', function (req, res) {
console.log(req.query)
res.end(JSON.stringify(req.query, null, 2))
})
app.listen(3000, function () {
console.log('Express server listening on port ' + 3000)
})
}
Purest
Then you can use Purest to make requests to the Pocket's REST API.
var getpocket = new Purest({provider: 'getpocket'})
getpocket.query()
.post('get')
.auth('[API_KEY]', '[ACCESS_TOKEN]')
.request(function (err, res, body) {
// body is the parsed JSON response
})
For anyone reading this in 2021 or later, wanting to make a simple script to add articles to their pocket, I came up with this:
1: get your consumer key, via pocket's site.
2: get you access token, using this tool it's very simple. If you want to make an app or something that'll work without it, I guess the above (old) answer might work, didn't test it.
3: Use the following code to add an article:
var request = require('request');
request.post({
url: 'https://getpocket.com/v3/add',
form: {
url: 'https://articleToAdd.com',
consumer_key: '123456-12abcd1234a1ab12a12abc12',
access_token: '12345a1a-1ab1-1a12-12a1-1a1234'
}
},
function(err, httpResponse, body) { console.log(httpResponse.body) }
)
Hope it helps someone that is looking to do the same. Retrieving/modifying articles is similar, look here for the specifics.

Resources