docs: function(req, options) {
    var username;
    var password;
    var ldap;
    var errorHandler;
    options || (options = {}); // eslint-disable-line no-param-reassign
  
    if (typeof this.options.credentialsLookup === 'function') {
      var credentials = this.options.credentialsLookup(req);
      if (credentials != null) {
        // name and pass as a courtesy for those who use basic-auth directly as
        // they're likely the main user group.
        username = credentials.username || credentials.name;
        password = credentials.password || credentials.pass;
      }
    } else {
      username = lookup(req.body, this.options.usernameField) || lookup(req.query, this.options.usernameField);
      password = lookup(req.body, this.options.passwordField) || lookup(req.query, this.options.passwordField);
    }
  
    if (!username || !password) {
      return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, this.options.missingCredentialsStatus || 400);
    }
  
    errorHandler = this.options.handleErrorsAsFailures === true ? handleErrorAsFailure.bind(this) : this.error.bind(this);
  
    /**
     * AD possible messages
     * http://www-01.ibm.com/support/docview.wss?uid=swg21290631
     */
    var messages = {
      '530': options.invalidLogonHours || 'Not Permitted to login at this time',
      '531': options.invalidWorkstation || 'Not permited to logon at this workstation',
      '532': options.passwordExpired || 'Password expired',
      '533': options.accountDisabled || 'Account disabled',
      '534': options.accountDisabled || 'Account disabled',
      '701': options.accountExpired || 'Account expired',
      '773': options.passwordMustChange || 'User must reset password',
      '775': options.accountLockedOut || 'User account locked',
      default: options.invalidCredentials || 'Invalid username/password'
    };
  
    ldap = new LdapAuth(this.options.server);
    ldap.once('error', errorHandler);
    ldap.on('error', function() { /* Ignored */ });
    ldap.authenticate(username, password, function(err, user) {
      ldap.close(function(){
        // We don't care about the closing
      });
  
      if (err) {
        // Invalid credentials / user not found are not errors but login failures
        if (err.name === 'InvalidCredentialsError' || err.name === 'NoSuchObjectError' || (typeof err === 'string' && err.match(/no such user/i))) {
          var message;
          if (err.name === 'NoSuchObjectError') {
            message = options.noSuchObject || 'Bad search base';
          } else {
            message = options.invalidCredentials || 'Invalid username/password';
          }
  
          if (err.message) {
            var ldapComment = err.message.match(/data ([0-9a-fA-F]*), v[0-9a-fA-F]*/);
            if (ldapComment && ldapComment[1]){
              message = messages[ldapComment[1]] || messages['default'];
            }
          }
          return this.fail({ message: message }, 401);
        }
        if (err.name === 'ConstraintViolationError'){
          return this.fail({ message: options.constraintViolation || 'Exceeded password retry limit, account locked' }, 401);
        }
  
        // Other errors are (most likely) real errors
        return errorHandler(err);
      }
  
      if (!user) {
        return this.fail({ message: options.userNotFound || 'Invalid username/password' }, 401);
      }
  
      // Execute given verify function
      if (this.verify) {
        if (this.options.passReqToCallback) {
          return this.verify(req, user, verify.call(this));
        } else {
          return this.verify(user, verify.call(this));
        }
      } else {
        return this.success(user);
      }
    }.bind(this));
  }