web-dev-qa-db-fra.com

Passeport Facebook avec JWT

J'utilise Passport sur mon serveur pour l'authentification de l'utilisateur. Lorsqu'un utilisateur se connecte localement (à l'aide d'un nom d'utilisateur et d'un mot de passe), le serveur lui envoie unJWTqui est stocké dans le stockage local et est renvoyé au serveur pour chaque appel de l'API nécessitant Authentification d'utilisateur.

Maintenant, je veux soutenir Facebook et Google login aussi. Depuis que j'ai commencé avec Passport, j'ai pensé qu'il serait préférable de continuer avec les stratégies de passeport en utilisant passport-facebook et passport-google-oauth .

Je ferai référence à Facebook, mais les deux stratégies se comportent de la même manière. Ils nécessitent tous deux une redirection vers une route de serveur ( '/ auth/facebook' et '/ auth/facebook/callback' d'ailleurs). Le processus a réussi à sauver les utilisateurs, y compris leurs identifiants facebook\google et leurs jetons sur la base de données.

Lorsque l'utilisateur est créé sur le serveur, un JWT est créé (sans aucune dépendance du jeton reçu de facebook\google). 

     ... // Passport facebook startegy
     var newUser = new User();
     newUser.facebook = {};
     newUser.facebook.id = profile.id; 
     newUser.facebook.token = token; // token received from facebook
     newUser.facebook.name  = profile.displayName;   
     newUser.save(function(err) {
          if (err)
               throw err;
          // if successful, return the new user
          newUser.jwtoken = newUser.generateJwt(); // JWT CREATION!
          return done(null, newUser);
     });

Le problème est qu’après sa création, je ne trouve pas un moyen approprié d’envoyer le fichier JWT au client , car je devrais également rediriger vers mon application.

app.get('/auth/facebook/callback',
    passport.authenticate('facebook', {
        session: false,
        successRedirect : '/',
        failureRedirect : '/'
    }), (req, res) => {
        var token = req.user.jwtoken;
        res.json({token: token});
    });

Le code ci-dessus me redirige vers la page principale de mon application, mais je ne reçois pas le jeton . Si je supprime le successRedirect, je récupère le jeton, mais je ne suis pas redirigé vers mon application .

Une solution pour ça? Est-ce que mon approche est incorrecte? Toutes les suggestions feront. 

16
Bar Kedem

La meilleure solution que j'ai trouvée pour ce problème serait de rediriger vers la page attendue avec un cookie qui contient le JWT.

Utiliser res.json n'enverrait qu'une réponse json et ne redirigerait pas. C'est pourquoi l'autre réponse suggérée ici ne résoudrait pas le problème rencontré.

Donc, ma solution serait:

app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
    session: false,
    successRedirect : '/',
    failureRedirect : '/'
}), (req, res) => {
    var token = req.user.jwtoken;
    res.cookie('auth', token); // Choose whatever name you'd like for that cookie, 
    res.redirect('http://localhost:3000'); // OR whatever page you want to redirect to with that cookie
});

Après la redirection, vous pouvez lire le cookie en toute sécurité et utiliser ce JWT comme prévu. (vous pouvez réellement lire le cookie à chaque chargement de page pour vérifier si un utilisateur est connecté)

Comme je l'ai mentionné précédemment, il est possible de rediriger avec le JWT en tant que paramètre de requête, mais c'est très dangereux. L'utilisation d'un cookie est plus sûre et il existe encore des solutions de sécurité que vous pouvez utiliser pour le rendre encore plus sûr. un paramètre de requête qui est clairement non sécurisé.

8
Bar Kedem

Ajoutant à la réponse de Bar.

J'ai préparé un composant de destination pour extraire le cookie, l'enregistrer dans un stockage local, le supprimer, puis le rediriger vers une page autorisée.

class SocialAuthRedirect extends Component {
  componentWillMount() {
    this.props.dispatch(
      fbAuthUser(getCookie("auth"), () => {
        document.cookie =
          "auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
        this.props.history.Push("/profile");
      })
    );
  }

  render() {
    return <div />;
  }
}
3
Tetsuya3850

Une solution propre consisterait à implémenter la redirection côté client.

Utilisez simplement:

app.get('/auth/facebook/callback',
  passport.authenticate('facebook', {
    session: false,
    failureRedirect: '/login'
  }), (req, res) => {
    res.json({
      token: req.user.jwtoken
    })
  }
)

Si votre côté client reçoit le jeton, redirigez-le ensuite vers la page d'accueil et, dans le cas où la connexion n'aurait pas abouti, le serveur le redirigerait directement.

Ou vous pouvez opter pour la gestion complète du côté client, comme je le ferais:

app.get('/auth/facebook/callback',
  passport.authenticate('facebook', {
    session: false
  }), (req, res) => {
    if (req.user.jwtoken) {
      res.json({
        success: true,
        token: req.user.jwtoken
      })
    } else {
      res.json({
        success: false
      })
    }
  }
)

Si success === true, stockez JWT dans LocalStorage, sinon redirigez-vous vers la page de connexion.

1
SherloxFR