biblioteki uwierzytelniania użytkowników dla node.js?

274

Czy istnieją jakieś biblioteki uwierzytelniania użytkowników dla node.js? W szczególności szukam czegoś, co może wykonać uwierzytelnianie hasła dla użytkownika (przy użyciu niestandardowej bazy danych uwierzytelniania bazy danych) i powiązać tego użytkownika z sesją.

Zanim napisałem bibliotekę autorów, pomyślałem, że sprawdzę, czy ludzie wiedzą o istniejących bibliotekach. Nie można znaleźć niczego oczywistego za pomocą wyszukiwarki Google.

-Shreyas

shreddd
źródło
W celu przeszukiwania: Coś równoważnego omniauth(szyny) lub python social-auth. Użytkownicy PHP (i innych popularnych języków serwerów WWW) powinni również dodać swoje odpowiedniki.
forivall

Odpowiedzi:

233

Jeśli szukasz struktury uwierzytelniania dla Connect lub Express, warto sprawdzić paszport: https://github.com/jaredhanson/passport

(Ujawnienie: Jestem programistą Passport)

Opracowałem paszport po zbadaniu zarówno funkcji connect-auth, jak i everyauth. Choć oba są świetnymi modułami, nie spełniły moich potrzeb. Chciałem czegoś bardziej lekkiego i dyskretnego.

Paszport jest podzielony na osobne moduły, więc możesz użyć tylko tego, czego potrzebujesz (OAuth, tylko w razie potrzeby). Usługa Passport nie instaluje także żadnych tras w aplikacji, co zapewnia elastyczność w decydowaniu o tym, kiedy i gdzie chcesz uwierzytelnić, a także kontroluje, co dzieje się, gdy uwierzytelnienie się powiedzie lub nie.

Na przykład oto dwuetapowy proces konfiguracji uwierzytelniania opartego na formularzu (nazwa użytkownika i hasło):

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('/');
  });

Dostępne są dodatkowe strategie uwierzytelniania za pośrednictwem Facebooka, Twittera itp. W razie potrzeby można podłączyć niestandardowe strategie.

Jared Hanson
źródło
Spośród wszystkich paczek uwierzytelniania dla węzła I wybrałem paszport. Jest dobrze udokumentowany i łatwy w użyciu oraz obsługuje więcej strategii.
tech-man
Obecnie używam paszportu do prototypu i nie polecam go, ponieważ wydaje się, że nie jest utrzymany, a projekt nie jest zbyt dobry. Na przykład zmusza Cię do korzystania z Connect-Flash, gdy można po prostu użyć req.session.messages, a reklama paszportowa Google reklamowana w witrynie jest nieaktualna, ponieważ używa przestarzałego Google OpenId i nie ma linku do paszportu google-oauth, które powinny go zastąpić. Jest to także sygnatura wywołania zwrotnego po uwierzytelnieniu: done(null,false,{ message:'Incorrect username.' })jest to okropne, ponieważ nie wiemy, jakie są wszystkie te parametry.
eloone
1
@eloone Muszę zaktualizować dokumenty, aby wskazać nowe metody uwierzytelniania, które teraz preferuje Google. Jak wspomniałeś, istnieje wsparcie dla tych i działają one dobrze. Jeśli chodzi o pytania projektowe, paszport nie zmusza Cię do korzystania z Connect-Flash, a argumenty, o których wspominasz, są udokumentowane w przewodniku. Jeśli potrzebujesz pomocy w zrozumieniu, istnieją fora, na których ludzie mogą pomóc i odpowiedzieć na twoje pytania.
Jared Hanson
Nie na darmo - ale właśnie skończyłem podłączanie paszportu (skorzystałem z dostarczonego przykładu). Super łatwe! Zdaję sobie sprawę, że minęło kilka lat od ostatniego komentarza. Poleciłbym każdemu rzucić okiem.
terary
89

Sesja + If

Sądzę, że powodem, dla którego nie znalazłeś wielu dobrych bibliotek, jest to, że korzystanie z biblioteki do uwierzytelniania jest w większości przypadków nadmiernie zaawansowane.

To, czego szukasz, to tylko segregator sesji :) Sesja z:

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

Otóż ​​to.


Nie zgadzam się z twoim wnioskiem, że wtyczka connect-auth jest właściwą drogą.

Używam również connect, ale nie używam connect-auth z dwóch powodów:

  1. IMHO łamie connect-auth bardzo potężną i łatwą do odczytania architekturę connect-ringową. Nie ma mowy - moja opinia :). Możesz znaleźć bardzo dobry i krótki artykuł o tym, jak działa connect i pomysł na krążek cebulowy tutaj .

  2. Jeśli - jak napisano - po prostu chcesz użyć podstawowego lub http logowania do bazy danych lub pliku. Connect-auth jest zdecydowanie za duży. To coś więcej dla takich rzeczy jak OAuth 1.0, OAuth 2.0 & Co


Bardzo proste uwierzytelnianie za pomocą Connect

(Jest zakończony. Wystarczy go przetestować, ale jeśli chcesz go użyć w środowisku produkcyjnym, upewnij się, że używasz protokołu https) (Aby zachować zgodność z zasadą REST, powinieneś użyć żądania POST zamiast żądania GET b / c zmieniasz stan :)

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);

UWAGA

Napisałem to oświadczenie ponad rok temu i obecnie nie mam aktywnych projektów węzłów. Mogą więc wystąpić zmiany API w Express. Dodaj komentarz, jeśli mam coś zmienić.

nivoc
źródło
Dlaczego connect-auth przerywa wzór cebuli / warstw? czy to dlatego, że nie używa next ()? Czy to możliwe?
jpstrikesback
3
Tak. Musi użyć next (), ponieważ taka jest idea połączenia. Connect ma architekturę warstwową / formę struktury kodu. I każda warstwa ma moc, aby zatrzymać wykonywanie żądania, nie wywołując next (). Jeśli mówimy o uwierzytelnianiu: warstwa uwierzytelniania sprawdzi, czy użytkownik ma odpowiednie uprawnienia. Jeśli wszystko jest w porządku, warstwa wywołuje next (). Jeśli nie, ta warstwa uwierzytelniania generuje błąd i nie wywoła funkcji next ().
Matthias
człowieku, właśnie tego szukałem. connect-auth wywoływało u mnie niestrawność. Właśnie zalogowałem się do mojej aplikacji po raz pierwszy. Dzięki wielkie.
Andy Ray
7
To nadal nie pomaga odpowiedzieć na pytanie, jak połączyć się z zapleczem bazy danych (najlepiej za pomocą zaszyfrowanych haseł). Doceniam twój komentarz, że ta biblioteka jest przeprojektowana, ale na pewno jest taka, która nie jest. Ponadto, gdybym chciał napisać własny system uwierzytelniania, użyłbym Struts w Javie. tak jak OP, chcę wiedzieć, które wtyczki zrobią to dla mnie w 1 linii kodu.
hendrixski
4
świetna odpowiedź Nivoc. Nie działa z najnowszymi wersjami Connect Tho. Musiałem zmienić ... cookieDecoder () -> cookieParser () i bodyDecoder () -> bodyParser () i usunąć następne wywołanie () z funkcji helloWorldContent, ponieważ pojawiał się błąd „Nie można ustawić nagłówków po są wysyłane ”
Michael Dausmann
26

Wygląda na to, że wtyczka connect-auth do oprogramowania pośredniego Connect jest dokładnie tym, czego potrzebuję: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Korzystam z express [ http://expressjs.com ], więc wtyczka connect pasuje bardzo ładnie, ponieważ express jest podklasowany (ok - prototypowany) z connect

shreddd
źródło
1
hej, masz przykład tego, co zrobiłeś? po prostu wymaganie connect-auth i wywołanie „.authenticate” na „req” zwraca „TypeError: Object # nie ma dla mnie metody„ uwierzytelnienia ””.
Misha Reyzlin
1
IMHO Ta wtyczka jest zbyt wymagająca dla prostego uwierzytelniania HTTP
Matthias
I ta wtyczka działa przeciwko architekturze Connect Cebul Ring
Matthias
14

Zasadniczo szukałem tego samego. W szczególności chciałem:

  1. Aby użyć express.js, który otacza możliwości oprogramowania pośredniego Connect
  2. Uwierzytelnianie „na podstawie formularza”
  3. Szczegółowa kontrola nad tym, które trasy są uwierzytelniane
  4. Back-end bazy danych dla użytkowników / haseł
  5. Użyj sesji

Skończyło się na tym, że stworzyłem własną funkcję oprogramowania pośredniego check_auth, którą przekazuję jako argument do każdej trasy, którą chcę uwierzytelnić. check_authpo prostu sprawdza sesję, a jeśli użytkownik nie jest zalogowany, następnie przekierowuje go na stronę logowania, w ten sposób:

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();
}

Następnie dla każdej trasy zapewniam, że ta funkcja jest przekazywana jako oprogramowanie pośrednie. Na przykład:

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

Wreszcie, musimy faktycznie obsługiwać proces logowania. Jest to proste:

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("/");
    }
  });
});

W każdym razie takie podejście zostało zaprojektowane głównie jako elastyczne i proste. Jestem pewien, że istnieje wiele sposobów, aby to poprawić. Jeśli masz jakieś, bardzo chciałbym poznać Twoją opinię.

EDYCJA: To jest uproszczony przykład. W systemie produkcyjnym nigdy nie chcesz przechowywać i porównywać haseł w postaci zwykłego tekstu. Jak zauważa komentator, istnieją biblioteki, które mogą pomóc w zarządzaniu bezpieczeństwem haseł.

Tomek
źródło
2
jest to dobre, z wyjątkiem tego, że powinieneś używać bcrypt do przechowywania hasła (nie zwykłego tekstu w db). Jest tutaj dobry post: devsmash.com/blog/…
chovy
13

Spójrz także na allauth, jeśli chcesz zintegrować logowanie innych firm / sieci społecznościowe.

Peter Lyons
źródło
7

Oto kod do podstawowego uwierzytelnienia z jednego z moich projektów. Używam go przeciwko CouchDB zi dodatkowej pamięci podręcznej danych uwierzytelniania, ale usunąłem ten kod.

Owiń metodę uwierzytelnienia wokół obsługi żądań i zapewnij drugie wywołanie zwrotne w przypadku nieudanego uwierzytelnienia. Odwołanie powodzenia otrzyma nazwę użytkownika jako dodatkowy parametr. Nie zapomnij poprawnie obsługiwać żądań z błędnymi lub brakującymi poświadczeniami w wywołaniu zwrotnym niepowodzenia:

/**
 * 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
źródło
Chciałem uniknąć podstawowego uwierzytelniania na rzecz uwierzytelniania opartego na formularzu. Jest to zdecydowanie eleganckie rozwiązanie podstawowego problemu z uwierzytelnianiem. Myślę, że mogłem znaleźć dobrą platformę uwierzytelniania (connect-auth - siedzi na szczycie connectjs)
shreddd
4

Innym podejściem do uwierzytelnienia jest uwierzytelnianie bez hasła, oparte na tokenach moduł dla express, który omija nieodłączny problem haseł [1]. Jest szybki do wdrożenia, nie wymaga zbyt wielu formularzy i oferuje większe bezpieczeństwo przeciętnego użytkownika (pełne ujawnienie: jestem autorem).

[1]: Hasła są przestarzałe

florian
źródło
3

Minęło kilka lat i chciałbym przedstawić moje rozwiązanie uwierzytelniające dla Express. Nazywa się Lockit . Możesz znaleźć projekt na GitHub i krótkie wprowadzenie na moim blogu .

Jakie są zatem różnice w stosunku do istniejących rozwiązań?

  • łatwy w użyciu: skonfiguruj DB, zainstaluj npm require('lockit'),lockit(app) , sporządzonej
  • trasy już wbudowane (/ signup, / login, / forgot-password itp.)
  • widoki już wbudowane (oparte na Bootstrap, ale możesz łatwo używać własnych widoków)
  • obsługuje komunikację JSON dla aplikacji jednostronicowych AngularJS / Ember.js
  • NIE obsługuje OAuth i OpenID. Tylko usernameipassword .
  • działa z kilkoma bazami danych (CouchDB, MongoDB, SQL) od razu po wyjęciu z pudełka
  • ma testy (nie mogłem znaleźć żadnych testów dla płyt kartonowo-gipsowych)
  • jest aktywnie utrzymywany (w porównaniu do wszystkich)
  • weryfikacja adresu e-mail i proces zapomnianego hasła (wyślij wiadomość e-mail z tokenem, nieobsługiwanym przez paszport)
  • modułowość: używaj tylko tego, czego potrzebujesz
  • elastyczność: dostosuj wszystkie rzeczy

Spójrz na przykłady .

zemirco
źródło
2

Istnieje projekt o nazwie Drywall, który implementuje system logowania użytkownika za pomocą Passport, a także ma panel administracyjny zarządzania użytkownikami. Jeśli szukasz w pełni funkcjonalnego systemu uwierzytelniania i zarządzania użytkownikami podobnego do tego, co ma Django, ale dla Node.js, to jest to. Uważam, że jest to naprawdę dobry punkt wyjścia do budowy aplikacji węzłowej, która wymagała uwierzytelnienia użytkownika i systemu zarządzania. Zobacz odpowiedź Jareda Hansona, aby uzyskać informacje na temat działania paszportu.

Łk145
źródło
1

Szybki prosty przykład użycia mongo, dla interfejsu API, który zapewnia autoryzację użytkownika, np. Klientowi Angular

w 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);

na trasie coś takiego:

// (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);
                }
            }
        });
    });
};

Następnie na trasach wymagających uwierzytelnienia możesz po prostu sprawdzić sesję użytkownika:

if (!req.session.user) {
    res.send(403);
}
Pan P.
źródło
0

Oto nowa biblioteka uwierzytelniania, która wykorzystuje tokeny ze znacznikami czasu. Tokeny można przesyłać użytkownikom pocztą e-mail lub SMS-em bez konieczności przechowywania ich w bazie danych. Może być używany do uwierzytelniania bez hasła lub do uwierzytelniania dwuskładnikowego.

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

Ujawnienie: Jestem deweloperem tej biblioteki.

sffc
źródło
0

Jeśli potrzebujesz uwierzytelnienia przy użyciu jednokrotnego logowania (Single Sign On) przy użyciu konta użytkownika Microsoft Windows. Możesz spróbować https://github.com/jlguenego/node-expose-sspi .

Otrzymasz req.ssoobiekt, który zawiera wszystkie informacje o użytkowniku klienta (login, nazwa wyświetlana, sid, grupy).

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"));

Uwaga: Jestem autorem node-expose-sspi.

jlguenego
źródło
0

kochanie

Lekki moduł uwierzytelniania użytkownika o zerowej konfiguracji. Nie potrzebuje sperowanej bazy danych.

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

To proste jak:

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
    }
})
Naveen
źródło