web-dev-qa-db-ja.com

PassportによるNode.js認証:フィールドが欠落している場合にメッセージをフラッシュする方法は?

Passport.jsを使用していますが、フォームのフィールドが空の場合はメッセージを表示したいと思います。しかし、それらが欠落している場合、パスポートは戦略コールバックをトリガーしないので、それを行う方法がわかりません。このユースケースをより明確にしたいのですが、パスポートを変更したくありません。私にはそうする方法があるように感じますが、どこにあるのかわかりません!ルートのコールバックを使用しようとしました(app.post)しかし、私が試した方法ではうまくいかないようです。

これが認証関数のプロトタイプです:

Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
  var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);
  // here is my problem
  if (!username || !password) {
    return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
  }

  var self = this;

  function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
  }

  try {
    if (self._passReqToCallback) {
      this._verify(req, username, password, verified);
    } else {
      this._verify(username, password, verified);
    }
  } catch (ex) {
    return self.error(ex);
  }
};

これが私の戦略です:

 passport.use('local-login', new LocalStrategy({
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true 
    },
    function(req, email, password, done) { 
        // ...
        console.log("Hello");
        User.findOne({ 'local.email' :  email }, function(err, user) {
            if (err)
                return done(err);

            // if no user is found, return the message
            if (!user)
                return done(null, false, req.flash('loginMessage', 'Pas d\'utilisateur avec ce login.')); // req.flash is the way to set flashdata using connect-flash

            // if the user is found but the password is wrong
            if (!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Oops! Mauvais password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            return done(null, user);
        });

    }));

そして最後に私のルート:

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

    // render the page and pass in any flash data if it exists
    res.render('login', { title: "Connexion", message: req.flash('loginMessage') }); 
});

// process the login form
    app.post('/login', passport.authenticate('local-login', {
        successRedirect : '/profile', // redirect to the secure profile section
        failureRedirect : '/login', // redirect back to the signup page if there is an error
        failureFlash : true // allow flash messages
    }, function(err, user, info) {
         // Was trying this callback, does'nt work, post callback maybe ?
         console.log("Hello");
    }));
13
Mael Jarnole

確認コールバックでreq.flashを呼び出さないでください。代わりに、メッセージを返す必要があります ドキュメントに示されているように 。 Passportは、failureFlash: trueの場合、フラッシュメッセージに返されたメッセージを書き込みます。

変更された検証コールバック:

passport.use('local-login', new LocalStrategy({...},
    function(email, password, done) { 
        User.findOne({ 'local.email' :  email }, function(err, user) {
            if (err)
                return done(err);
            if (!user)
                return done(null, false, {message: 'Pas d\'utilisateur avec ce login.'});
            if (!user.validPassword(password))
                return done(null, false, {message: 'Oops! Mauvais password.'});
            return done(null, user);
        });

    }));

そしてルート:

app.get('/login', function(req, res) {
    console.log(req.flash('error'));
    res.send();
});

app.post('/login', passport.authenticate('local-login', {
    successRedirect : '/profile',
    failureRedirect : '/login',
    failureFlash : true
}));

編集:

これが完全に機能する例です: https://Gist.github.com/vesse/9e23ff1810089bed4426

17
vesse

古い質問ですが、答えを見つけるのに苦労しました。うまくいけば、これは他の人を助けます。


_connect-flash_の使用に関しては ドキュメント は少し不完全だと思います。彼らが言う:

注:フラッシュメッセージを使用するには、req.flash()関数が必要です。 Express 2.xはこの機能を提供していましたが、Express 3.xから削除されました。 Express 3.xを使用するときにこの機能を提供するには、接続フラッシュミドルウェアの使用をお勧めします。

しかし、done()コールバックでreq.flashを使用することについての言及はありません。 scotch.ioチュートリアル に基づいて、実際にはコールバックでreq.flash()を呼び出す必要があります。わたしにはできる。

_// In your strategy
...
if (user) {
    return done( null, false, req.flash('loginMessage','Pas d\'utilisateur avec ce login.') );
...
_

もちろん、passReqToCallbackを使用する必要があります。また、failureFlashtrueに設定されていることを確認してください。 OPはすでにこれらを正しく行っています。

これで、ルートのフラッシュメッセージを確認できます。 _connect-flash_はメッセージの配列を送信することに注意してください。テンプレートが文字列を期待している場合、それはOPの問題である可能性があります。

_// In your routes
app.get('/login', function(req, res) {

    // Test flash messages in the console
    console.log( req.flash('loginMessage') ); // This returns an array
    console.log( req.flash('loginMessage')[0] ); // This returns a string

    // render the page and pass in any flash data if it exists
    res.render('login', {
        title: "Connexion",
        message: req.flash('loginMessage')[0] // Don't forget the index! 
    });

});
_

1つのページに複数のログインメッセージが表示される可能性がある場合は、req.flash('loginMessage')配列全体を渡して、テンプレート内で繰り返し処理します。以下は nunjucks の使用例です。


Protip:

フラッシュメッセージを含む多くのルートがある場合、ミドルウェアルートで常にそれらを _res.locals_ に設定できます。これはtitleのような他のローカルに干渉しません。これが bootstrap alerts を使用した私の実装です。

私の戦略では:

_...
if (!user){
    return done( null, false, req.flash('danger','No account exists for that email.') );
}
...
_

私のroutes.jsで:

_// Set flash messages
router.get('*', function(req,res,next){
    res.locals.successes = req.flash('success');
    res.locals.dangers = req.flash('danger');
    res.locals.warnings = req.flash('warning');
    next();
});

// Login route
app.get('/login', function(req, res) {
    res.render('login', { title: 'Login'}); 
});
_

私の修道女の基本テンプレートでは:

_<!--Messages-->
{% for danger in dangers %}
    <div class='header alert alert-danger alert-dismissible'>
        <strong><i class="fa fa-exclamation-circle"></i> ERROR:</strong> {{ danger | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for warning in warnings %}
    <div class='header alert alert-warning alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Warning:</strong> {{ warning | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for success in successes %}
    <div class='header alert alert-success alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Success!</strong> {{ success | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
_
5
Keith

badRequestMessagefailureFlash: trueを設定する必要があります。

このような:

passport.authenticate('login', {
    successRedirect : '/',
    failureRedirect : '/login',
    badRequestMessage : 'Missing username or password.',
    failureFlash: true
})
4
MrR0FL0LMA0MG

何ヶ月もオンとオフを繰り返して失敗フラッシュを機能させようとした後、ようやく失敗フラッシュ機能を使用しないソリューションを見つけました。基本的には新しいルートを作成し、フラッシュメッセージを送信しました。

app.post('/login',
  passport.authenticate('local', {failureRedirect: "/loginfailed"}),
  function(req, res) {
    if (!req.user.isActive){
      req.flash("success","Your account needs to be verified. Please check your email to verify your account");
      req.logout();
      res.redirect("back")
    }else{
      res.redirect("/");
    }
  });

  //Route to login page if user failed to login. I created this to allow flash messages and not interfere with regular login route
  app.get("/loginfailed", function(req, res){
    if (!req.user){
      req.flash("success", "Username or password is incorrect.");
      res.redirect("/login");
    }
  });
2
Joel

認証に必要なフィールドが欠落している場合、OPが指摘するように、_passport.authenticate_は戦略コールバックをトリガーしません
これは、infoパラメータを使用して、認証関数の カスタムコールバック (ページを下にスクロール)内で処理する必要があります。
OPのコードが次のような場合:

_app.post('/login', function (req, res, next) { 
    passport.authenticate('local-login',
    {
      successRedirect: '/profile',
      failureRedirect: '/login',
      failureFlash: true,
    },
    function (error, user, info) {
      //This will print: 'Missing credentials'
      console.log(info.message);
      //So then OP could do something like 
      req.flash(info.message);
      //or in my case I didn't use flash and just did 
      if (info)
        res.status(400).send(info.message);
      ...
    })(req, res, next);
  });
_

私はこの質問が古いことを知っていますが、私はこの問題に私自身で偶然遭遇しました、そして私はまだ受け入れられた答えがないことを知っています。さらに、すべての回答は、OPが実際に求めているもの、つまりbadRequestMessageへのアクセス方法を誤って解釈したと思います。
PassportJSドキュメントもあまり役に立ちません:

認証が失敗した場合、ユーザーはfalseに設定されます。例外が発生した場合、errが設定されます。オプションのinfo引数が渡され、戦略の検証コールバックによって提供される追加の詳細が含まれます。

これが実際に意味することは、infoパラメーターは次のように戦略から3番目のパラメーターとして渡すことができるということです:done(error,user,info)、ただし、資格情報が欠落している場合にこのパラメーターがデフォルトで使用されることについては言及していません。全体的に、PassportJSのドキュメントは詳細と 存在しない例へのリンク がないため、いくつかのオーバーホールで対処できると思います。

この回答 は、不足している資格情報メッセージがinfoパラメータで渡されることを理解するのに役立ちました。

1
Dawid Stróżak

私の解決策

app.jsコード:

const flash = require('connect-flash');
app.use(flash());
require('./src/config/passport.js')(app);

local.strategy.jsコード

const passport = require('passport');
const { Strategy } = require('passport-local');
const userModel = require('./../../../models/userModel');

module.exports = function localStrategy() {
passport.use(new Strategy(
    {
        usernameField: "username",
        passwordField: "password"
    }, (username, password, done) => {
        userModel.findOne({ username }, (err, user) => {
            if (err) {
                res.send(err);
            }
            if (user && (user.username == username && user.password == password)) {
                done(null, user, { message: "Success" });
            } else {
                done(null, false, { message: "Invalid credentials!" });
            }
        });
      }
       ));
  }

authController.jsコード

function signIn(req, res) {
    res.render('signin', {
        nav,
        title: "Sign in",
        message: req.flash()
    });
};

authRouter.jsコード

authRouter.route('/signin').get(signIn).post(passport.authenticate('local', {
    successRedirect: '/admin',
    failureRedirect: '/auth/signin',
    failureFlash: true
}));

signin.jsテンプレートコード(私のビューエンジンはejsです)

<% if (message) { %>
  <p style="color: red;" class="text-center"><%= message.error %></p>
<% } %>
0
Nijat Aliyev

私は同じ問題を抱えていて解決しました。
成功メッセージと失敗メッセージの変数は、JSが使用しているパスポートと一致する必要があります。遊んでみたところ、パスポートJSが変数successを使用して成功のフラッシュを表示し、errorを使用して失敗のフラッシュを表示していることがわかりました。

したがって、最初に、app.jsで次のようなスーパーグローバル変数を作成できます。

app.use(function(req, res, next) {
    res.locals.error = req.flash("error");
    res.locals.success = req.flash("success");
    next();
});

次に、これらの変数を寺院で使用します。私はejsを使用しているので、次のようになります。

<%if(error && error.length > 0){%>
    <div class="alert alert-danger"><%=error%></div>
<%}%>
    <%if(success && success.length > 0){%>
    <div class="alert alert-success"><%=success%></div>
<%}%>

最後に、パスポートのJSコードは次のようになります。

router.post("/login",passport.authenticate("local", {
    successFlash : "Hey, Welcome back",
    successRedirect : "/mountains",
    failureFlash : true,
    failureRedirect :"/login"
    }), function(req, res){
});
0
youssouf diarra