| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 | 
							- '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
 
-   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
 
 
  |