библиотеки аутентификации пользователя для node.js?

274

Существуют ли какие-либо библиотеки аутентификации пользователей для node.js? В частности, я ищу что-то, что может выполнить аутентификацию по паролю для пользователя (с использованием пользовательской базы данных с аутентификацией на основе сервера) и связать этого пользователя с сеансом.

Перед тем, как написать библиотеку аутентификации, я подумал, что я узнаю, знают ли люди о существующих библиотеках. Не удалось найти ничего очевидного через поиск в Google.

-Shreyas

shreddd
источник
Для поиска: что-то эквивалентное omniauth(rails) или python social-auth. Пользователи PHP (и других распространенных языков веб-сервера) также могут свободно добавлять свои эквиваленты.
13

Ответы:

233

Если вы ищете систему аутентификации для Connect или Express, стоит изучить Passport: https://github.com/jaredhanson/passport

(Раскрытие: я разработчик Passport)

Я разработал Passport после изучения подключения и аутентификации. Хотя они оба являются отличными модулями, они не удовлетворяли моим потребностям. Я хотел что-то более легкое и ненавязчивое.

Паспорт разбит на отдельные модули, поэтому вы можете использовать только то, что вам нужно (OAuth, только при необходимости). Паспорт также не монтирует какие-либо маршруты в вашем приложении, предоставляя вам гибкость в определении того, когда и где вы хотите аутентификацию, и перехватывает контроль того, что происходит, когда аутентификация проходит успешно или не удается.

Например, вот двухэтапный процесс настройки аутентификации на основе форм (имя пользователя и пароль):

passport.use(new LocalStrategy(
  function(username, password, done) {
    // Find the user from your DB (MongoDB, CouchDB, other...)
    User.findOne({ username: username, password: password }, function (err, user) {
      done(err, user);
    });
  }
));

app.post('/login', 
  passport.authenticate('local', { failureRedirect: '/login' }),
  function(req, res) {
    // Authentication successful. Redirect home.
    res.redirect('/');
  });

Дополнительные стратегии доступны для аутентификации через Facebook, Twitter и т. Д. При необходимости можно подключить пользовательские стратегии.

Джаред Хансон
источник
Среди всех аутентификационных пакетов для узла я выбрал аттестат. Он хорошо документирован, прост в использовании и поддерживает больше стратегий.
техник
В настоящее время я использую паспорт для прототипа, и я не рекомендую его, потому что он, кажется, не поддерживается, а дизайн не очень хорош. Например, он заставляет вас использовать connect-flash, когда он может просто использовать req.session.messages, а рекламируемый на сайте паспорт-Google устарел, поскольку он использует Google OpenId, который устарел, и нет ссылки на паспорт- Google-OAUT, что должно заменить его. Также это подпись обратного вызова после аутентификации: done(null,false,{ message:'Incorrect username.' })это ужасно, так как мы не знаем, каковы все эти параметры.
Eloone
1
@eloone Мне нужно обновить документы, чтобы указать новые методы аутентификации, которые Google сейчас предпочитает. Как вы упоминаете, поддержка существует для тех, и они работают хорошо. Что касается вопросов проектирования, то паспорт не заставляет вас использовать connect-flash, а приведенные вами аргументы документированы в руководстве. Если вам нужна помощь в понимании, существуют форумы, на которых люди могут помочь и ответить на ваши вопросы.
Джаред Хансон,
Не зря - но я только что закончил подключать Passport (использовал предоставленный пример). Супер просто! Я понимаю, что прошло несколько лет с момента самых последних комментариев. Я бы порекомендовал любому взглянуть.
terary
89

Сессия + Если

Я полагаю, причина того, что вы не нашли много хороших библиотек, заключается в том, что использование библиотеки для аутентификации в большинстве случаев слишком сложное.

То, что вы ищете, это просто привязка сессии :) Сессия с:

if login and user == xxx and pwd == xxx 
   then store an authenticated=true into the session 
if logout destroy session

Это оно.


Я не согласен с вашим выводом, что плагин connect-auth - это путь.

Я также использую Connect, но я не использую Connect-Auth по двум причинам:

  1. ИМХО ломает connect-auth очень мощную и легкую для чтения архитектуру луковичных колец connect. А не ходи - моё мнение :). Вы можете найти очень хорошую и короткую статью о том, как работает connect, и об идее лукового кольца здесь .

  2. Если вы - как написано - просто хотите использовать простой или http логин с базой данных или файлом. Connect-auth слишком велик. Это больше для таких вещей, как OAuth 1.0, OAuth 2.0 и Co


Очень простая аутентификация с подключением

(Это завершено. Просто запустите его для тестирования, но если вы хотите использовать его в рабочей среде, обязательно используйте https) (И чтобы быть совместимым с REST-принципом, вы должны использовать POST-запрос вместо GET-запроса b / c вы меняете состояние :)

var connect = require('connect');
var urlparser = require('url');

var authCheck = function (req, res, next) {
    url = req.urlp = urlparser.parse(req.url, true);

    // ####
    // Logout
    if ( url.pathname == "/logout" ) {
      req.session.destroy();
    }

    // ####
    // Is User already validated?
    if (req.session && req.session.auth == true) {
      next(); // stop here and pass to the next onion ring of connect
      return;
    }

    // ########
    // Auth - Replace this example with your Database, Auth-File or other things
    // If Database, you need a Async callback...
    if ( url.pathname == "/login" && 
         url.query.name == "max" && 
         url.query.pwd == "herewego"  ) {
      req.session.auth = true;
      next();
      return;
    }

    // ####
    // This user is not authorized. Stop talking to him.
    res.writeHead(403);
    res.end('Sorry you are not authorized.\n\nFor a login use: /login?name=max&pwd=herewego');
    return;
}

var helloWorldContent = function (req, res, next) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('authorized. Walk around :) or use /logout to leave\n\nYou are currently at '+req.urlp.pathname);
}

var server = connect.createServer(
      connect.logger({ format: ':method :url' }),
      connect.cookieParser(),
      connect.session({ secret: 'foobar' }),
      connect.bodyParser(),
      authCheck,
      helloWorldContent
);

server.listen(3000);

НОТА

Я написал это заявление более года назад, и в настоящее время у меня нет активных проектов узлов. Так что в Express могут быть изменения API. Пожалуйста, добавьте комментарий, если я должен что-то изменить.

nivoc
источник
Почему connect-auth нарушает структуру лука / слоев? потому что он не использует next ()? Может ли это?
jpstrikesback
3
Да. Он должен использовать next (), потому что это идея подключения. Connect имеет слой-архитектуру / форму структуры кода. И каждый уровень может остановить выполнение запроса, не вызывая next (). Если мы говорим об аутентификации: слой аутентификации проверит, имеет ли пользователь правильные разрешения. Если все в порядке, слой вызывает next (). Если нет, этот слой авторизации генерирует ошибку и не будет вызывать next ().
Матиас
чувак, это именно то, что я искал. Connect-Auth давал мне немного расстройства желудка. Я только что вошел в свое приложение в первый раз. Огромное спасибо.
Энди Рэй
7
Это по-прежнему не помогает ответить на вопрос, как подключиться к базе данных (желательно с зашифрованными паролями). Я ценю ваш комментарий о том, что эта библиотека чрезмерно спроектирована, но, безусловно, есть такая, которая ее не имеет. Кроме того, если бы я хотел написать свою собственную систему аутентификации, я бы использовал Struts в Java. так же, как OP, я хочу знать, какие плагины будут делать это для меня в 1 строке кода.
Хендрикски
4
отличный ответ нивок. Не работает с последними версиями Connect Tho. Мне пришлось изменить… отправлены »
Майкл Даусманн
26

Похоже, что плагин connect-auth для промежуточного программного обеспечения подключения - это как раз то, что мне нужно: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Я использую экспресс [ http://expressjs.com ], так что подключаемый модуль очень хорошо вписывается, так как экспресс находится в подклассе (хорошо - прототип) из connect

shreddd
источник
1
эй, у тебя есть пример того, что ты сделал? простое требование connect-auth и вызов «.authenticate» для «req» возвращает «TypeError: Object # не имеет метода« authenticate »« для меня.
Миша Рейзлин
1
ИМХО Этот плагин очень тяжёл для простой http-аутентификации
Matthias
И этот плагин работает против архитектуры подключения кольца лука
Matthias
14

Я в основном искал то же самое. В частности, я хотел следующее:

  1. Использовать express.js, который включает в себя возможности промежуточного программного обеспечения Connect
  2. Проверка подлинности на основе форм
  3. Детальный контроль над тем, какие маршруты аутентифицированы
  4. База данных для пользователей / паролей
  5. Используйте сеансы

В итоге я создал свою собственную функцию промежуточного программного обеспечения, check_authкоторую я передаю в качестве аргумента каждому маршруту, который я хочу аутентифицировать. check_authпросто проверяет сеанс и, если пользователь не вошел в систему, затем перенаправляет их на страницу входа, например так:

function check_auth(req, res, next) {

  //  if the user isn't logged in, redirect them to a login page
  if(!req.session.login) {
    res.redirect("/login");
    return; // the buck stops here... we do not call next(), because
            // we don't want to proceed; instead we want to show a login page
  }

  //  the user is logged in, so call next()
  next();
}

Затем для каждого маршрута я гарантирую, что эта функция передается как промежуточное ПО. Например:

app.get('/tasks', check_auth, function(req, res) {
    // snip
});

Наконец, нам нужно обработать процесс входа в систему. Это просто:

app.get('/login', function(req, res) {
  res.render("login", {layout:false});
});

app.post('/login', function(req, res) {

  // here, I'm using mongoose.js to search for the user in mongodb
  var user_query = UserModel.findOne({email:req.body.email}, function(err, user){
    if(err) {
      res.render("login", {layout:false, locals:{ error:err } });
      return;
    }

    if(!user || user.password != req.body.password) {
      res.render("login",
        {layout:false,
          locals:{ error:"Invalid login!", email:req.body.email }
        }
      );
    } else {
      // successful login; store the session info
      req.session.login = req.body.email;
      res.redirect("/");
    }
  });
});

Во всяком случае, этот подход был в основном разработан, чтобы быть гибким и простым. Я уверен, что есть множество способов улучшить это. Если у вас есть, я очень хотел бы получить ваши отзывы.

РЕДАКТИРОВАТЬ: это упрощенный пример. В производственной системе вы никогда не захотите хранить и сравнивать пароли в виде простого текста. Как указывает комментатор, есть библиотеки, которые могут помочь в управлении паролем.

Том
источник
2
это хорошо, за исключением того, что вы должны использовать bcrypt для хранения пароля (не простой текст в БД). Здесь есть хороший пост об этом: devsmash.com/blog/…
chovy
13

Также посмотрите на все, если вы хотите, чтобы сторонние / социальные сети вход в систему интеграции.

Питер Лайонс
источник
7

Вот код для базовой аутентификации из одного из моих проектов. Я использую его против CouchDB с дополнительным кешем данных аутентификации, но я удалил этот код.

Оберните метод аутентификации вокруг вашей обработки запроса и предоставьте второй обратный вызов для неудачной аутентификации. Обратный вызов успеха получит имя пользователя в качестве дополнительного параметра. Не забудьте правильно обрабатывать запросы с неправильными или отсутствующими учетными данными при сбое обратного вызова:

/**
 * Authenticate a request against this authentication instance.
 * 
 * @param request
 * @param failureCallback
 * @param successCallback
 * @return
 */
Auth.prototype.authenticate = function(request, failureCallback, successCallback)
{
    var requestUsername = "";
    var requestPassword = "";
    if (!request.headers['authorization'])
    {
        failureCallback();
    }
    else
    {
        var auth = this._decodeBase64(request.headers['authorization']);
        if (auth)
        {
            requestUsername = auth.username;
            requestPassword = auth.password;
        }
        else
        {
            failureCallback();
        }
    }


    //TODO: Query your database (don't forget to do so async)


    db.query( function(result)
    {
        if (result.username == requestUsername && result.password == requestPassword)
        {
            successCallback(requestUsername);
        }
        else
        {
            failureCallback();
        }
    });

};


/**
 * Internal method for extracting username and password out of a Basic
 * Authentication header field.
 * 
 * @param headerValue
 * @return
 */
Auth.prototype._decodeBase64 = function(headerValue)
{
    var value;
    if (value = headerValue.match("^Basic\\s([A-Za-z0-9+/=]+)$"))
    {
        var auth = (new Buffer(value[1] || "", "base64")).toString("ascii");
        return {
            username : auth.slice(0, auth.indexOf(':')),
            password : auth.slice(auth.indexOf(':') + 1, auth.length)
        };
    }
    else
    {
        return null;
    }

};
b_erb
источник
Я хотел избежать базовой аутентификации в пользу аутентификации на основе форм. Это определенно элегантное решение основной проблемы с аутентификацией. Я думаю, что, возможно, я нашел хорошую платформу аутентификации (connect-auth - сидит поверх connectjs)
shreddd
4

Другой подход к аутентификации - это Passwordless, модуль аутентификации на основе токенов для экспресс, который обходит внутреннюю проблему паролей [1]. Это быстро реализуется, не требует слишком много форм и предлагает лучшую безопасность для среднего пользователя (полное раскрытие: я автор).

[1]: пароли устарели

Флориан
источник
3

Прошло несколько лет, и я хотел бы представить свое решение для аутентификации для Express. Это называется Lockit . Вы можете найти проект на GitHub и краткое введение в моем блоге .

Так в чем же отличие существующих решений?

  • простой в использовании: настройка вашей БД, НПМ установить, require('lockit'), lockit(app), сделано
  • маршруты уже встроены (/ регистрация, / логин, / забыли пароль и т. д.)
  • представления уже встроены (на основе Bootstrap, но вы можете легко использовать свои собственные представления)
  • он поддерживает связь JSON для ваших одностраничных приложений AngularJS / Ember.js
  • он не поддерживает OAuth и OpenID. Только usernameи password.
  • он работает с несколькими базами данных (CouchDB, MongoDB, SQL) из коробки
  • у него есть тесты (я не смог найти тесты для гипсокартона)
  • он активно поддерживается (по сравнению с каждым)
  • подтверждение по электронной почте и обработка забытого пароля (отправка электронной почты с токеном, не поддерживается Passport)
  • модульность: используйте только то, что вам нужно
  • гибкость: настроить все вещи

Посмотрите на примеры .

zemirco
источник
2

Существует проект, который называется « Гипсокартон» и реализует систему входа пользователей с помощью Passport, а также имеет панель управления пользователями. Если вы ищете полнофункциональную систему аутентификации и управления пользователями, похожую на ту, что есть в Django, но для Node.js, то это она. Я обнаружил, что это действительно хорошая отправная точка для создания приложения для узла, для которого требовалась система аутентификации и управления пользователями. См . Ответ Джареда Хансона о том, как работает Паспорт.

lk145
источник
1

Простой простой пример использования mongo для API, который обеспечивает аутентификацию пользователя для клиента Angular

в app.js

var express = require('express');
var MongoStore = require('connect-mongo')(express);

// ...

app.use(express.cookieParser());
// obviously change db settings to suit
app.use(express.session({
    secret: 'blah1234',
    store: new MongoStore({
        db: 'dbname',
        host: 'localhost',
        port: 27017
    })
}));

app.use(app.router);

для вашего маршрута что-то вроде этого:

// (mongo connection stuff)

exports.login = function(req, res) {

    var email = req.body.email;
    // use bcrypt in production for password hashing
    var password = req.body.password;

    db.collection('users', function(err, collection) {
        collection.findOne({'email': email, 'password': password}, function(err, user) {
            if (err) {
                res.send(500);
            } else {
                if(user !== null) {
                    req.session.user = user;
                    res.send(200);
                } else {
                    res.send(401);
                }
            }
        });
    });
};

Затем в ваших маршрутах, требующих аутентификации, вы можете просто проверить сеанс пользователя:

if (!req.session.user) {
    res.send(403);
}
Мистер П
источник
0

Вот новая библиотека аутентификации, которая использует метки времени. Токены могут быть отправлены пользователям по электронной почте или в виде текстовых сообщений без необходимости сохранять их в базе данных. Он может использоваться для аутентификации без пароля или для двухфакторной аутентификации.

https://github.com/vote539/easy-no-password

Раскрытие информации: я разработчик этой библиотеки.

sffc
источник
0

Если вам нужна аутентификация с помощью единого входа (SSO) с учетной записью пользователя Microsoft Windows. Вы можете попробовать https://github.com/jlguenego/node-expose-sspi .

Это даст вам req.ssoобъект, который содержит всю информацию о клиенте (логин, отображаемое имя, sid, группы).

const express = require("express");
const { sso, sspi } = require("node-expose-sspi");

sso.config.debug = false;

const app = express();

app.use(sso.auth());

app.use((req, res, next) => {
  res.json({
    sso: req.sso
  });
});

app.listen(3000, () => console.log("Server started on port 3000"));

Отказ от ответственности: я автор node-expose-sspi.

jlguenego
источник
0

кисло-авт

Легкий модуль аутентификации пользователя с нулевой конфигурацией. Для этого не нужна отдельная база данных.

https://www.npmjs.com/package/sweet-auth

Это просто как:

app.get('/private-page', (req, res) => {

    if (req.user.isAuthorized) {
        // user is logged in! send the requested page
        // you can access req.user.email
    }
    else {
        // user not logged in. redirect to login page
    }
})
Нэвин
источник