| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 | 'use strict'var caseless = require('caseless')var uuid = require('uuid/v4')var helpers = require('./helpers')var md5 = helpers.md5var toBase64 = helpers.toBase64function 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  while (true) {    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
 |