Skip to content

Commit

Permalink
Revert "Workaround for GitHub issue request#2807 (request#2808)" (req…
Browse files Browse the repository at this point in the history
…uest#3075)

This reverts commit 2f04c3c.
  • Loading branch information
reconbot authored Dec 1, 2018
1 parent 2f04c3c commit 3b37eb1
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 10 deletions.
167 changes: 167 additions & 0 deletions lib/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
'use strict'

var caseless = require('caseless')
var uuid = require('uuid/v4')
var helpers = require('./helpers')

var md5 = helpers.md5
var toBase64 = helpers.toBase64

function Auth (request) {
// define all public properties here
this.request = request
this.hasAuth = false
this.sentAuth = false
this.bearerToken = null
this.user = null
this.pass = null
}

Auth.prototype.basic = function (user, pass, sendImmediately) {
var self = this
if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) {
self.request.emit('error', new Error('auth() received invalid user or password'))
}
self.user = user
self.pass = pass
self.hasAuth = true
var header = user + ':' + (pass || '')
if (sendImmediately || typeof sendImmediately === 'undefined') {
var authHeader = 'Basic ' + toBase64(header)
self.sentAuth = true
return authHeader
}
}

Auth.prototype.bearer = function (bearer, sendImmediately) {
var self = this
self.bearerToken = bearer
self.hasAuth = true
if (sendImmediately || typeof sendImmediately === 'undefined') {
if (typeof bearer === 'function') {
bearer = bearer()
}
var authHeader = 'Bearer ' + (bearer || '')
self.sentAuth = true
return authHeader
}
}

Auth.prototype.digest = function (method, path, authHeader) {
// TODO: More complete implementation of RFC 2617.
// - handle challenge.domain
// - support qop="auth-int" only
// - handle Authentication-Info (not necessarily?)
// - check challenge.stale (not necessarily?)
// - increase nc (not necessarily?)
// For reference:
// http://tools.ietf.org/html/rfc2617#section-3
// https://github.com/bagder/curl/blob/master/lib/http_digest.c

var self = this

var challenge = {}
var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
for (;;) {
var match = re.exec(authHeader)
if (!match) {
break
}
challenge[match[1]] = match[2] || match[3]
}

/**
* RFC 2617: handle both MD5 and MD5-sess algorithms.
*
* If the algorithm directive's value is "MD5" or unspecified, then HA1 is
* HA1=MD5(username:realm:password)
* If the algorithm directive's value is "MD5-sess", then HA1 is
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
*/
var ha1Compute = function (algorithm, user, realm, pass, nonce, cnonce) {
var ha1 = md5(user + ':' + realm + ':' + pass)
if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
return md5(ha1 + ':' + nonce + ':' + cnonce)
} else {
return ha1
}
}

var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
var nc = qop && '00000001'
var cnonce = qop && uuid().replace(/-/g, '')
var ha1 = ha1Compute(challenge.algorithm, self.user, challenge.realm, self.pass, challenge.nonce, cnonce)
var ha2 = md5(method + ':' + path)
var digestResponse = qop
? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
: md5(ha1 + ':' + challenge.nonce + ':' + ha2)
var authValues = {
username: self.user,
realm: challenge.realm,
nonce: challenge.nonce,
uri: path,
qop: qop,
response: digestResponse,
nc: nc,
cnonce: cnonce,
algorithm: challenge.algorithm,
opaque: challenge.opaque
}

authHeader = []
for (var k in authValues) {
if (authValues[k]) {
if (k === 'qop' || k === 'nc' || k === 'algorithm') {
authHeader.push(k + '=' + authValues[k])
} else {
authHeader.push(k + '="' + authValues[k] + '"')
}
}
}
authHeader = 'Digest ' + authHeader.join(', ')
self.sentAuth = true
return authHeader
}

Auth.prototype.onRequest = function (user, pass, sendImmediately, bearer) {
var self = this
var request = self.request

var authHeader
if (bearer === undefined && user === undefined) {
self.request.emit('error', new Error('no auth mechanism defined'))
} else if (bearer !== undefined) {
authHeader = self.bearer(bearer, sendImmediately)
} else {
authHeader = self.basic(user, pass, sendImmediately)
}
if (authHeader) {
request.setHeader('authorization', authHeader)
}
}

Auth.prototype.onResponse = function (response) {
var self = this
var request = self.request

if (!self.hasAuth || self.sentAuth) { return null }

var c = caseless(response.headers)

var authHeader = c.get('www-authenticate')
var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase()
request.debug('reauth', authVerb)

switch (authVerb) {
case 'basic':
return self.basic(self.user, self.pass, true)

case 'bearer':
return self.bearer(self.bearerToken, true)

case 'digest':
return self.digest(request.method, request.path, authHeader)
}
}

exports.Auth = Auth
10 changes: 0 additions & 10 deletions request.js
Original file line number Diff line number Diff line change
Expand Up @@ -878,16 +878,6 @@ Request.prototype.onRequestError = function (error) {
clearTimeout(self.timeoutTimer)
self.timeoutTimer = null
}

// This is a workaround for a race condition caused by the way that lib/redirect.js
// calls Request.init() when processing an HTTP redirect:
// https://github.com/request/request/issues/2807
// Somehow we end up with an ECONNRESET error from a socket that has already
// been destroyed and returned to the pool.
if (self.req && self.req.socket && self.req.socket.destroyed) {
return
}

self.emit('error', error)
}

Expand Down

0 comments on commit 3b37eb1

Please sign in to comment.