node.jsのユーザー認証ライブラリ


274

node.js用の既存のユーザー認証ライブラリはありますか?特に、(カスタムバックエンド認証DBを使用して)ユーザーのパスワード認証を実行し、そのユーザーをセッションに関連付けることができるものを探しています。

認証ライブラリを作成する前に、既存のライブラリを知っているかどうかを確認しました。グーグル検索で明らかなものを見つけることができませんでした。

-シュレヤス


検索可能性:omniauth(rails)またはpythonに相当するものsocial-auth。PHP(およびその他の一般的なWebサーバー言語)ユーザーも、同等のものを自由に追加できます。
forivall 2013年

回答:


233

ConnectまたはExpressの認証フレームワークを探している場合は、Passportを調査する価値があります。https//github.com/jaredhanson/passport

(開示:私はパスポートの開発者です)

connect-authとeveryauthの両方を調査した後、Passportを開発しました。どちらも素晴らしいモジュールですが、私のニーズには合いませんでした。もっと軽量で邪魔にならないものが欲しかった。

Passportは個別のモジュールに分割されているため、必要なものだけを使用するように選択できます(OAuth、必要な場合のみ)。Passportはまた、アプリケーションにルートをマウントしないため、認証をいつ、どこで行うかを柔軟に決定し、認証が成功または失敗したときに何が起こるかを制御するフックを備えています。

たとえば、フォームベース(ユーザー名とパスワード)の認証を設定する2段階のプロセスを次に示します。

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などを介した認証に追加の戦略を利用できます。必要に応じて、カスタム戦略をプラグインできます。


ノードのすべての認証パッケージの中で、パスポートを選択しました。十分に文書化されており、使いやすく、より多くの戦略をサポートしています。
tech-man

現時点ではプロトタイプにパスポートを使用していますが、維持されていないようで、デザインがあまり良くないためお勧めしません。たとえば、単にreq.session.messagesを使用できる場合は、connect-flashを使用する必要があり、非推奨のGoogle OpenIdを使用しているため、ウェブサイトに宣伝されているpassport-googleは古く、passport-それに代わるgoogle-oauth。また、これは認証後のコールバックのシグネチャですdone(null,false,{ message:'Incorrect username.' })。これらのパラメーターが何であるかがわからないので、ひどいです。
eloone 2014

1
@eloone Googleが現在好む新しい認証方法を指すようにドキュメントを更新する必要があります。あなたが言うように、サポートはそれらのために存在し、彼らはうまく働きます。設計に関する質問については、passportはconnect-flashの使用を強制するものではなく、言及する引数はガイドに記載されています。理解を助ける必要がある場合は、人々があなたの質問を助けて答えることができるフォーラムがあります。
Jared Hanson、

無料ではありませんが、パスポートのプラグインが完了しました(付属の例を使用)。超簡単!最新のコメントから数年が経ちました。どなたかご覧になることをお勧めします。
terary

89

セッション+場合

あなたが多くの良いライブラリーを見つけられなかった理由は、認証にライブラリーを使用することはほとんど過剰に設計されているためだと思います。

あなたが探しているのは、単にセッションバインダーです:)以下とのセッション:

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

それでおしまい。


connect-authプラグインが進むべき道であるというあなたの結論に同意しません。

私も接続を使用していますが、2つの理由で接続認証を使用していません。

  1. IMHOは、connect-authを破壊します。非常に強力で読みやすい、connectのオニオンリングアーキテクチャです。ノーゴー-私の意見:)。コネクトの仕組みとオニオンリングのアイデアについては、こちらの非常に優れた短い記事をご覧ください

  2. あなたが書いたように-データベースまたはファイルで基本的なまたはhttpログインを使用したい場合。接続認証は大きすぎます。OAuth 1.0、OAuth 2.0、Coなどに適しています


接続による非常に単純な認証

(完了です。テストのために実行するだけですが、本番環境で使用する場合は、必ずhttpsを使用してください)(そしてREST-Principle-Compliantになるには、GET-Request b / cの代わりにPOST-Requestを使用する必要がありますあなたは状態を変更します:)

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

注意

私は1年以上前にこの声明を書きましたが、現在アクティブなノードプロジェクトはありません。したがって、ExpressにAPIの変更がある可能性があります。何かを変更する必要がある場合は、コメントを追加してください。


connect-authが玉ねぎ/レイヤーのパターンを壊すのはなぜですか?それはnext()を使用しないためですか?できますか?
jpstrikesback

3
はい。それは接続の背後にあるアイデアなので、それはnext()を使用する必要があります。Connectには、レイヤーアーキテクチャ/コード構造の形式があります。そして、すべてのレイヤーは、next()を呼び出さないことでリクエストの実行を停止することができます。認証について話している場合:認証レイヤーは、ユーザーが適切な権限を持っているかどうかをチェックします。すべてが問題なければ、レイヤーはnext()を呼び出します。そうでない場合、この認証層はエラーを生成し、next()を呼び出しません。
Matthias

男、これはまさに私が探していたものです。connect-authは私に少し消化不良を与えていました。初めてアプリにログインしました。本当にありがとう。
Andy Ray

7
これは、データベースバックエンドへの接続方法(できれば暗号化されたパスワードを使用)に答えるのに役立ちません。この1つのライブラリは過剰に設計されているとのコメントに感謝しますが、実際にはそうでないものもあります。また、独自の認証システムを作成したい場合は、JavaでStrutsを使用します。OPのように、どのプラグインが1行のコードでそれを行うかを知りたいです。
hendrixski

4
素晴らしい答えニヴォック。connect thoの最新バージョンでは動作しません。私は変更する必要がありました... cookieDecoder()-> cookieParser()and bodyDecoder()-> bodyParser()とhelloWorldContent関数からのnext()呼び出しを削除しました。送信されます」
Michael Dausmann、

26

接続ミドルウェアへの接続認証プラグインがまさに私が必要とするものであるように見えます:http : //wiki.github.com/ciaranj/connect-auth/creating-a-form-b​​ased-strategy

私はエクスプレス[ http://expressjs.com ] を使用しているため、エクスプレスはコネクトからサブクラス化(ok-プロトタイプ)されているので、コネクトプラグインは非常にうまく適合します。


1
ねえ、あなたがしたことの例はありますか?単にconnect-authを要求し、「req」で「.authenticate」を呼び出すと、「TypeError:Object#has no method 'authenticate'」が返されます。
Misha Reyzlin

1
私見このプラグインは、単純なhttp認証のために重い方法です
Matthias

このプラグインは、接続オニオンリングアーキテクチャに対して機能します
Matthias

14

基本的に同じものを探していました。具体的には、次のものが必要でした。

  1. Connectのミドルウェア機能をラップするexpress.jsを使用するには
  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を使用する必要があります(dbのプレーンテキストではありません)。良いポストはそれについてここにあります:devsmash.com/blog/...は
chovy


7

これは、私のプロジェクトの1つからの基本認証のコードです。追加の認証データキャッシュを使用してCouchDBに対して使用しましたが、そのコードを削除しました。

リクエスト処理の周りに認証メソッドをラップし、失敗した認証のための2番目のコールバックを提供します。成功コールバックは、追加パラメーターとしてユーザー名を取得します。失敗コールバックで資格情報が間違っているか欠落しているリクエストを正しく処理することを忘れないでください:

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

};

フォームベースの認証を支持して、基本的な認証を避けたかったのです。これは間違いなく、基本的な認証問題に対するエレガントなソリューションです。私は良い認証フレームワークを見つけたかもしれません(connect-auth-connectjsの上にあります)
shreddd


3

数年が経ちましたが、Expressの認証ソリューションを紹介します。それはLockitと呼ばれます。プロジェクトはGitHubにあり、短い紹介は私のブログにあります。

では、既存のソリューションとの違いは何ですか?

  • 使いやすい:あなたのDBをセットアップし、NPMインストール、require('lockit')lockit(app)、行われ
  • すでに組み込まれているルート(/ signup、/ login、/ forgot-passwordなど)
  • 組み込み済みのビュー(Bootstrapに基づいていますが、独自のビューを簡単に使用できます)
  • AngularJS / Ember.jsシングルページアプリのJSON通信をサポートします
  • OAuthおよびOpenIDはサポートされていません。のみusernamepassword
  • 複数のデータベース(CouchDB、MongoDB、SQL)をそのまま使用できます
  • テストがあります(ドライウォールのテストは見つかりませんでした)
  • アクティブに維持されている(everyauthと比較して)
  • 電子メールの検証とパスワードを忘れたプロセス(パスポートではサポートされていない、トークン付きの電子メールを送信)
  • モジュール性:必要なものだけを使用
  • 柔軟性:すべてのものをカスタマイズ

例を見てください。


2

Passportを使用したユーザーログインシステムを実装し、ユーザー管理の管理パネルを備えた、Drywallというプロジェクトがあります。Djangoが持っているもののようなNode.jsに似た、完全な機能を備えたユーザー認証および管理システムを探しているなら、これがまさにそれです。ユーザー認証と管理システムを必要とするノードアプリを構築するための非常に良い出発点であることがわかりました。Passportのしくみについては、Jared Hansonの回答を参照してください。



1

Angularクライアントにユーザー認証を提供するAPIの、mongoを使用した簡単な例

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

次に、タイムスタンプ付きのトークンを使用する新しい認証ライブラリを示します。トークンは、データベースに保存する必要なく、ユーザーにメールまたはテキストで送信できます。パスワードなしの認証または2要素認証に使用できます。

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

開示:私はこのライブラリの開発者です。


0

Microsoft WindowsユーザーアカウントでのSSO(シングルサインオン)による認証が必要な場合。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の作者です。


0

sweet-auth

軽量のゼロ構成ユーザー認証モジュール。それは別のデータベースを必要としません。

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
    }
})
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.