Как работает basic-авторизация в ExpressJS

    В Express.JS есть встроенный middleware для авторизации. Если очень хочется заблокировать доступ к приложению — достаточно всего лишь добавить одну строчку в сетап express-приложения:
    app.use(express.basicAuth('username', 'password'));
    

    Так же middleware поддерживает альтернативный вариант — с коллбэком:
    app.use(express.basicAuth(function(user, pass, next) {
      var result = (user === 'testUser' && pass === 'testPass');
      next(null /* error */, result);
    }));
    

    А если нужно ограничить доступ только к определенным url'ам — middleware можно использовать не глобально, а только в рамках роутера. То есть так:
    var auth = express.basicAuth(function(user, pass, next) {
      var result = (user === 'testUser' && pass === 'testPass');
      next(null, result);
    });
    
    app.get('/home', auth, function(req, res) {
      res.send('Hello World');
    });
    

    Если не забираться под капот — на этом можно и закончить. Тех же, кто на диаграмме Венна находится между кругами «Мне интересно, что внутри» и «Я не знаю как работает basic авторизация apache», приглашаю под кат.

    basicAutn достался express в наследство от connect. Устроен он достаточно просто — когда браузер запрашивает страницу у сервера, сервер сообщает браузеру о необходимости авторизации с помощью заголовка WWW-Authenticate:
      res.statusCode = 401;
      res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
      res.end('Unauthorized');
    

    В ответ на это, браузер рисует форму ввода логина и пароля, и, дождавшись, когда пользователь покончит со вводом, отправляет повторный запрос к серверу. На этот раз запрос уже будет содержать заголовок Authorization:Basic dXNlcm5hbWU6cGFzc3dvcmQ=, где dXNlcm5hbWU6cGFzc3dvcmQ= — это, ни что иное, как строка 'username:password'. В этом легко убедиться:
    var str = new Buffer('dXNlcm5hbWU6cGFzc3dvcmQ=', 'base64').toString()
    console.log(str) // выведет 'username:password'
    

    Теперь браузер будет прикреплять эту строку к каждому запросу, а basicAuth будет прилежно проверять, верные ли данные пользователя указаны в запросе. Этот механизм не отличается безопасностью, но если нужно быстро и просто ограничить доступ к проекту — это вполне подойдет.

    Исходный код того, что происходит внутри basicAuth можно прочитать в документации к connect.

    Комментарии 9

    • НЛО прилетело и опубликовало эту надпись здесь
        –1
        Только что открыл форму авторизации в одном окне, а во втором попытался подключиться к серверу. Второе окно подвисло до того момента, пока я не авторизовался в первом. Вы точно уверены, что это асинхронная функция?
          0
              // async
              if (callback.length >= 3) {
                var pause = utils.pause(req);
                callback(user, pass, function(err, user){
                  if (err || !user)  return unauthorized(res, realm);
                  req.user = req.remoteUser = user;
                  next();
                  pause.resume();
                });
              // sync
              } else {
                if (callback(user, pass)) {
                  req.user = req.remoteUser = user;
                  next();
                } else {
                  unauthorized(res, realm);
                }
              }
          

          куда уж точнее
            0
            Был не прав. Поправил статью, спасибо.
              –1
              То «блокирование», о котором я говорил, происходит на стороне браузера, с NodeJS все в полном порядке.
          0
          А если вам надо повесить авторизацию на группу путей, можете воспользоваться библиотечкой github.com/DavidKlassen/express-group-middleware

          Работает например так:

          var group = require('express-group-middleware')(app);
          
          var auth = function (req, res, next) { 
            // do some auth...
            next(); 
          };
          
          group(function () {
            app.resource('channels', channels, { load: Channel.findById.bind(Channel) });
            app.resource('streams', streams, { load: Stream.findById.bind(Stream) });
            app.resource('workers', workers);
          }).before(auth);
          


          ps надо бы найти время ридми написать, раз уж рекламирую…
            0
            Видать время не нашли)
              +1
              merged :)
                0
                Честно признаюсь, я сейчас понятия не имею что творится в мире разработки на express.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое