pustaka otentikasi pengguna untuk node.js?


274

Apakah ada perpustakaan otentikasi pengguna yang ada untuk node.js? Secara khusus saya mencari sesuatu yang dapat melakukan otentikasi kata sandi untuk pengguna (menggunakan DB backend auth kustom), dan mengaitkan pengguna itu dengan sesi.

Sebelum saya menulis perpustakaan auth, saya pikir saya akan melihat apakah orang tahu perpustakaan yang ada. Tidak dapat menemukan sesuatu yang jelas melalui pencarian google.

- Shreyas


Untuk pencarian: Sesuatu yang setara dengan omniauth(rails) atau python social-auth. Pengguna PHP (dan bahasa server web umum lainnya) juga harus merasa bebas untuk menambahkan padanannya.
forivall

Jawaban:


233

Jika Anda mencari kerangka kerja otentikasi untuk Connect atau Express, Passport layak diselidiki: https://github.com/jaredhanson/passport

(Pengungkapan: Saya adalah pengembang Paspor)

Saya mengembangkan Passport setelah menyelidiki kedua connect-auth dan everyauth. Walaupun keduanya merupakan modul yang hebat, mereka tidak sesuai dengan kebutuhan saya. Saya menginginkan sesuatu yang lebih ringan dan tidak mencolok.

Paspor dipecah menjadi modul terpisah, sehingga Anda dapat memilih untuk hanya menggunakan apa yang Anda butuhkan (OAuth, hanya jika perlu). Paspor juga tidak memasang rute apa pun di aplikasi Anda, memberi Anda fleksibilitas untuk memutuskan kapan dan di mana Anda ingin otentikasi, dan menghubungkan untuk mengontrol apa yang terjadi ketika otentikasi berhasil atau gagal.

Misalnya, berikut adalah proses dua langkah untuk menyiapkan otentikasi berbasis-nama-pengguna (kata sandi dan kata sandi):

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

Strategi tambahan tersedia untuk otentikasi melalui Facebook, Twitter, dll. Strategi khusus dapat dicolokkan, jika perlu.


Di antara semua paket auth untuk simpul saya memilih paspor. Ini didokumentasikan dengan baik dan mudah digunakan, dan mendukung lebih banyak strategi.
tech-man

Saya menggunakan paspor saat ini untuk prototipe, dan saya tidak merekomendasikannya karena tampaknya tidak terawat, dan desainnya tidak terlalu bagus. Sebagai contoh, itu memaksa Anda untuk menggunakan connect-flash ketika itu hanya bisa menggunakan req.session.messages, dan paspor-google yang diiklankan di situs web sudah usang karena menggunakan Google OpenId yang sudah usang, dan tidak ada tautan ke paspor- google-oauth yang seharusnya menggantikannya. Ini juga tanda tangan dari callback setelah otentikasi: done(null,false,{ message:'Incorrect username.' })mengerikan karena kita tidak tahu semua parameter itu.
eloone

1
@eloone Saya perlu memperbarui dokumen untuk menunjuk ke metode otentikasi baru yang sekarang lebih disukai Google. Seperti yang Anda sebutkan, ada dukungan untuk itu dan mereka bekerja dengan baik. Sedangkan untuk pertanyaan desain, paspor tidak memaksa Anda untuk menggunakan flash-connect, dan argumen yang Anda sebutkan didokumentasikan pada panduan ini. Jika Anda membutuhkan bantuan untuk memahami, ada forum tempat orang dapat membantu dan menjawab pertanyaan Anda.
Jared Hanson

Bukan untuk apa-apa - tapi saya baru saja selesai memasukkan Paspor (menggunakan contoh yang disediakan). Sangat mudah! Saya menyadari sudah beberapa tahun sejak sejak komentar terbaru. Saya akan merekomendasikan siapa pun melihatnya.
terary

89

Sesi + Jika

Saya kira alasan bahwa Anda belum menemukan banyak perpustakaan yang bagus adalah bahwa menggunakan perpustakaan untuk otentikasi kebanyakan direkayasa.

Apa yang Anda cari hanyalah pengikat sesi :) Sesi dengan:

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

itu dia.


Saya tidak setuju dengan kesimpulan Anda bahwa plugin connect-auth adalah cara yang harus dilakukan.

Saya menggunakan juga terhubung tetapi saya tidak menggunakan connect-auth karena dua alasan:

  1. IMHO memecah connect-auth arsitektur onion-ring yang sangat kuat dan mudah dibaca dari connect. A no-go - pendapat saya :). Anda dapat menemukan artikel yang sangat bagus dan pendek tentang cara kerja koneksi dan ide cincin bawang di sini .

  2. Jika Anda - seperti yang ditulis - hanya ingin menggunakan login dasar atau http dengan database atau file. Connect-auth terlalu besar. Ini lebih untuk hal-hal seperti OAuth 1.0, OAuth 2.0 & Co


Otentikasi yang sangat sederhana dengan koneksi

(Sudah selesai. Cukup jalankan untuk pengujian tetapi jika Anda ingin menggunakannya dalam produksi, pastikan untuk menggunakan https) (Dan untuk menjadi REST-Principle-Compliant Anda harus menggunakan POST-Permintaan daripada GET-Permintaan b / c Anda mengubah keadaan :)

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

CATATAN

Saya menulis pernyataan ini lebih dari setahun yang lalu dan saat ini tidak memiliki proyek simpul aktif. Jadi mungkin ada Perubahan API di Express. Silakan tambahkan komentar jika saya harus mengubah apa pun.


Mengapa connect-auth merusak pola bawang / lapisan? apakah karena tidak menggunakan next ()? Mungkinkah itu
jpstrikesback

3
Iya. Ini harus menggunakan next () karena itulah ide di balik terhubung. Connect memiliki lapisan-arsitektur / bentuk struktur kode. Dan setiap lapisan memiliki kekuatan untuk menghentikan eksekusi permintaan dengan tidak memanggil next (). Jika kita berbicara tentang otentikasi: Lapisan otentikasi akan memeriksa apakah pengguna memiliki izin yang benar. Jika semuanya baik-baik saja, panggilan layer next (). Jika tidak auth-layer ini menghasilkan kesalahan dan tidak akan memanggil next ().
Matthias

***, ini persis apa yang saya cari. connect-auth memberiku sedikit gangguan pencernaan. Saya baru saja masuk ke aplikasi saya untuk pertama kalinya. Terima kasih banyak.
Andy Ray

7
Ini masih tidak membantu menjawab bagaimana menghubungkan ke backend basis data (lebih disukai dengan kata sandi terenkripsi). Saya menghargai komentar Anda bahwa perpustakaan yang satu ini over-engineered, tapi pasti ada yang tidak. Juga, jika saya ingin menulis sistem auth saya sendiri, saya akan menggunakan Struts di Jawa. seperti halnya OP, saya ingin tahu plugin mana yang akan melakukannya untuk saya dalam 1 baris kode.
hendrixski

4
jawaban bagus Nivoc. Tidak berfungsi dengan versi terbaru sambungkan. Saya harus mengubah ... cookieDecoder () -> cookieParser () dan bodyDecoder () -> bodyParser () dan menghapus panggilan berikutnya () dari fungsi helloWorldContent karena saya mendapatkan kesalahan 'Tidak dapat mengatur header setelah mereka dikirim '
Michael Dausmann

26

Sepertinya plugin connect-auth ke connect middleware persis seperti yang saya butuhkan: http://wiki.github.com/ciaranj/connect-auth/creating-a-form-based-strategy

Saya menggunakan express [ http://expressjs.com ] sehingga plugin connect sangat cocok karena express subclass (ok - prototyped) dari connect


1
hei, apakah Anda memiliki contoh dari apa yang Anda lakukan? hanya membutuhkan connect-auth dan memanggil ".authenticate" pada "req" mengembalikan "TypeError: Objek # tidak memiliki metode 'otentikasi'" untuk saya.
Misha Reyzlin

1
IMHO Plugin ini adalah cara yang berat untuk otentikasi http sederhana
Matthias

Dan plugin ini bekerja terhadap arsitektur cincin bawang terhubung
Matthias

14

Saya pada dasarnya mencari hal yang sama. Secara khusus, saya menginginkan yang berikut:

  1. Untuk menggunakan express.js, yang membungkus kemampuan middleware Connect
  2. Otentikasi "Berbasis formulir"
  3. Kontrol granular atas rute mana yang diautentikasi
  4. Database back-end untuk pengguna / kata sandi
  5. Gunakan sesi

Apa yang akhirnya saya lakukan adalah membuat fungsi middleware saya sendiri check_authyang saya sampaikan sebagai argumen untuk setiap rute yang ingin saya otentikasi. check_authhanya memeriksa sesi dan jika pengguna tidak masuk, maka arahkan mereka ke halaman login, seperti:

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

Lalu untuk setiap rute, saya memastikan fungsi ini dilewatkan sebagai middleware. Sebagai contoh:

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

Akhirnya, kita harus benar-benar menangani proses login. Ini mudah:

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

Bagaimanapun, pendekatan ini sebagian besar dirancang agar fleksibel dan sederhana. Saya yakin ada banyak cara untuk memperbaikinya. Jika Anda punya, saya sangat ingin umpan balik Anda.

EDIT: Ini adalah contoh yang disederhanakan. Dalam sistem produksi, Anda tidak akan pernah ingin menyimpan & membandingkan kata sandi dalam teks biasa. Sebagai komentator tunjukkan, ada lib yang dapat membantu mengelola keamanan kata sandi.


2
ini bagus, kecuali Anda harus menggunakan bcrypt untuk menyimpan kata sandi (bukan teks biasa dalam db). Ada posting bagus di sini tentang hal ini: devsmash.com/blog/…
chovy


7

Berikut adalah beberapa kode untuk otentikasi dasar dari salah satu proyek saya. Saya menggunakannya melawan CouchDB dengan dan cache data auth tambahan, tapi saya menghapus kode itu.

Bungkus metode otentikasi di sekitar Anda meminta penanganan, dan berikan panggilan balik kedua untuk otentikasi gagal. Callback yang berhasil akan mendapatkan nama pengguna sebagai parameter tambahan. Jangan lupa untuk menangani permintaan dengan kredensial yang salah atau tidak ada dengan benar dalam kegagalan panggilan balik:

/**
 * 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;
    }

};

Saya ingin menghindari auth dasar yang mendukung auth berbasis-form. Ini jelas merupakan solusi elegan untuk masalah auth dasar. Saya pikir saya mungkin telah menemukan kerangka kerja auth yang baik (connect-auth - duduk di atas connectjs)
shreddd

4

Cara berbeda dalam otentikasi adalah Tanpa Kata Sandi, modul otentikasi berbasis token untuk mengekspresikan yang menghindari masalah inheren kata sandi [1]. Ini cepat diimplementasikan, tidak memerlukan terlalu banyak formulir, dan menawarkan keamanan yang lebih baik untuk rata-rata pengguna (pengungkapan penuh: Saya penulis).

[1]: Kata sandi sudah usang


3

Beberapa tahun telah berlalu dan saya ingin memperkenalkan solusi otentikasi saya untuk Express. Ini disebut Lockit . Anda dapat menemukan proyeknya di GitHub dan pengantar singkat di blog saya .

Jadi apa perbedaan dengan solusi yang ada?

  • mudah digunakan: mengatur DB Anda, NPM menginstal, require('lockit'), lockit(app), dilakukan
  • rute sudah built-in (/ daftar, / login, / lupa kata sandi, dll.)
  • tampilan sudah built-in (berdasarkan Bootstrap tetapi Anda dapat dengan mudah menggunakan tampilan Anda sendiri)
  • ini mendukung komunikasi JSON untuk aplikasi halaman tunggal AngularJS / Ember.js Anda
  • itu TIDAK mendukung OAuth dan OpenID. Hanya usernamedan password.
  • ini bekerja dengan beberapa database (CouchDB, MongoDB, SQL) di luar kotak
  • ia memiliki tes (saya tidak dapat menemukan tes untuk Drywall)
  • itu dipelihara secara aktif (dibandingkan dengan everyauth)
  • verifikasi email dan proses kata sandi lupa (kirim email dengan token, tidak didukung oleh Paspor)
  • modularitas: gunakan hanya apa yang Anda butuhkan
  • fleksibilitas: sesuaikan semua hal

Lihatlah contoh - contohnya .


2

Ada sebuah proyek bernama Drywall yang mengimplementasikan sistem login pengguna dengan Passport dan juga memiliki panel admin manajemen pengguna. Jika Anda mencari otentikasi pengguna dengan fitur lengkap dan sistem manajemen yang mirip dengan apa yang dimiliki Django tetapi untuk Node.js, ini dia. Saya menemukan itu menjadi titik awal yang sangat baik untuk membangun aplikasi simpul yang memerlukan otentikasi pengguna dan sistem manajemen. Lihat jawaban Jared Hanson untuk informasi tentang cara kerja Passport.



1

Contoh sederhana cepat menggunakan mongo, untuk API yang menyediakan authsi pengguna untuk klien Angular

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

untuk rute Anda kira-kira seperti ini:

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

Kemudian di rute Anda yang memerlukan auth Anda hanya dapat memeriksa sesi pengguna:

if (!req.session.user) {
    res.send(403);
}

0

Berikut adalah perpustakaan otentikasi baru yang menggunakan token cap waktu. Token dapat diemailkan atau dikirim sms ke pengguna tanpa harus menyimpannya dalam database. Dapat digunakan untuk otentikasi tanpa kata sandi atau untuk otentikasi dua faktor.

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

Pengungkapan: Saya adalah pengembang perpustakaan ini.


0

Jika Anda memerlukan otentikasi dengan SSO (Single Sign On) dengan akun pengguna Microsoft Windows. Anda dapat mencoba https://github.com/jlguenego/node-expose-sspi .

Ini akan memberi Anda req.ssoobjek yang berisi semua informasi pengguna klien (login, nama tampilan, sid, grup).

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

Penafian: Saya penulis node-expose-sspi.


0

manis-auth

Modul otentikasi pengguna yang ringan dan tanpa konfigurasi. Itu tidak memerlukan database sperate.

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

Sederhana seperti:

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
    }
})
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.