vso-node-api
Version:
Node client for Visual Studio Online/TFS REST APIs
116 lines (115 loc) • 5.31 kB
JavaScript
;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
Object.defineProperty(exports, "__esModule", { value: true });
const http = require("http");
const https = require("https");
var _ = require("underscore");
var ntlm = require("../opensource/node-http-ntlm/ntlm");
class NtlmCredentialHandler {
constructor(username, password, workstation, domain) {
this.username = username;
this.password = password;
if (workstation !== undefined) {
this.workstation = workstation;
}
if (domain !== undefined) {
this.domain = domain;
}
}
prepareRequest(options) {
// No headers or options need to be set. We keep the credentials on the handler itself.
// If a (proxy) agent is set, remove it as we don't support proxy for NTLM at this time
if (options.agent) {
delete options.agent;
}
}
canHandleAuthentication(res) {
if (res && res.statusCode === 401) {
// Ensure that we're talking NTLM here
// Once we have the www-authenticate header, split it so we can ensure we can talk NTLM
var wwwAuthenticate = res.headers['www-authenticate'];
if (wwwAuthenticate !== undefined) {
var mechanisms = wwwAuthenticate.split(', ');
var idx = mechanisms.indexOf("NTLM");
if (idx >= 0) {
// Check specifically for 'NTLM' since www-authenticate header can also contain
// the Authorization value to use in the form of 'NTLM TlRMTVNT....AAAADw=='
if (mechanisms[idx].length == 4) {
return true;
}
}
}
}
return false;
}
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
handleAuthentication(httpClient, protocol, options, objs, finalCallback) {
// Set up the headers for NTLM authentication
var ntlmOptions = _.extend(options, {
username: this.username,
password: this.password,
domain: this.domain || '',
workstation: this.workstation || ''
});
var keepaliveAgent;
if (httpClient.isSsl === true) {
keepaliveAgent = new https.Agent({});
}
else {
keepaliveAgent = new http.Agent({ keepAlive: true });
}
let self = this;
// The following pattern of sending the type1 message following immediately (in a setImmediate) is
// critical for the NTLM exchange to happen. If we removed setImmediate (or call in a different manner)
// the NTLM exchange will always fail with a 401.
this.sendType1Message(httpClient, protocol, ntlmOptions, objs, keepaliveAgent, function (err, res) {
if (err) {
return finalCallback(err, null, null);
}
setImmediate(function () {
self.sendType3Message(httpClient, protocol, ntlmOptions, objs, keepaliveAgent, res, finalCallback);
});
});
}
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
sendType1Message(httpClient, protocol, options, objs, keepaliveAgent, callback) {
var type1msg = ntlm.createType1Message(options);
var type1options = {
headers: {
'Connection': 'keep-alive',
'Authorization': type1msg
},
timeout: options.timeout || 0,
agent: keepaliveAgent,
// don't redirect because http could change to https which means we need to change the keepaliveAgent
allowRedirects: false
};
type1options = _.extend(type1options, _.omit(options, 'headers'));
httpClient.requestInternal(protocol, type1options, objs, callback);
}
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
sendType3Message(httpClient, protocol, options, objs, keepaliveAgent, res, callback) {
if (!res.headers['www-authenticate']) {
return callback(new Error('www-authenticate not found on response of second request'));
}
// parse type2 message from server:
var type2msg = ntlm.parseType2Message(res.headers['www-authenticate']);
// create type3 message:
var type3msg = ntlm.createType3Message(type2msg, options);
// build type3 request:
var type3options = {
headers: {
'Authorization': type3msg
},
allowRedirects: false,
agent: keepaliveAgent
};
// pass along other options:
type3options.headers = _.extend(type3options.headers, options.headers);
type3options = _.extend(type3options, _.omit(options, 'headers'));
// send type3 message to server:
httpClient.requestInternal(protocol, type3options, objs, callback);
}
}
exports.NtlmCredentialHandler = NtlmCredentialHandler;