NodeJS rest authentification utilisant passeport et OAuth2 + réseau social

je suis en train de travailler sur REST api à l'aide de NodeJS. Pour l'authentification, j'ai décidé d'utiliser Passport. Je veux une api vraiment reposante. Donc ça veut dire que je dois utiliser des jetons plutôt que des sessions.

je veux laisser les utilisateurs se connecter en utilisant leur nom d'utilisateur et leur mot de passe, ou en utilisant des réseaux sociaux comme Facebook, Google et Twitter.

je fais mes propres OAuth2.0 serveur pour la délivrance d' Access et Refresh tokens en utilisant oauth2orize module. Donc maintenant je peux enregistrer de nouveaux utilisateurs et ensuite leur délivrer des tokens. J'ai suivi ce tutoriel:

http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/

Vérification de l'utilisateur pour la route:

// api ------------------------------------------------------------------------------------
    app.get('/api/userInfo',
        passport.authenticate('bearer', { session: false }),
        function(req, res) {
            // req.authInfo is set using the `info` argument supplied by
            // `BearerStrategy`.  It is typically used to indicate scope of the token,
            // and used in access control checks.  For illustrative purposes, this
            // example simply returns the scope in the response.
            res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope })
        }
    );

Tout cela fonctionne très bien. Malheureusement, je ne sais pas comment mettre en œuvre l'authentification sociale.

je lisais ce tutoriel:

http://scotch.io/tutorials/javascript/easy-node-authentication-facebook 

mais dans ce tutoriel, ils ne font pas une api vraiment reposante. J'ai déjà implémenté le schéma utilisateur selon ce tutoriel où les tokens pour l'utilisateur local sont stockés dans des modèles séparés.

// define the schema for our user model
var userSchema = mongoose.Schema({
    local: {
        username: {
            type: String,
            unique: true,
            required: true
        },
        hashedPassword: {
            type: String,
            required: true
        },
        created: {
            type: Date,
            default: Date.now
        }
    },
    facebook: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    twitter: {
        id: String,
        token: String,
        displayName: String,
        username: String
    },
    google: {
        id: String,
        token: String,
        email: String,
        name: String
    }
});

Mais maintenant, comment puis-je vérifier l'utilisateur?

passport.authenticate('bearer', { session: false }),

ceci ne vérifie que les jetons au porteur par rapport à ma db, mais comment puis-je vérifier les jetons sociaux? Ai-je raté quelque chose?

20
demandé sur Ankan-Zerob 2014-10-09 22:34:40

4 réponses

J'utilise Facebook login pour ma propre API RESTful pour mon application Bloc-notes ici. J'ai commencé l'application comme une qui sera utilisée comme une page web, mais la communication après la connexion sera toujours via L'API.

Alors j'ai décidé de créer un version mobile de la même application qui utilisera L'API. J'ai décidé de le faire de cette façon: l'application mobile se connecte via Facebook et envoie l'identifiant de l'utilisateur facebook et le jeton D'accès FB à L'API, les appels API API Facebooks pour vérifier ces paramétrages et si le succès enregistre un nouvel utilisateur(ou se connecte à un utilisateur existant) dans la base de données de mon application, crée un token personnalisé pour cet utilisateur et le renvoie à l'application mobile. De là, l'application mobile envoie cette coutume jeton pour authentifier l'application avec l'API.

Voici le code:

l'auth dans L'API (utilise le module npm de fbgraph):

var graph = require('fbgraph'),
Promise = require('bluebird')
...
Promise.promisify(graph.get);
...
var postAuthHandler = function (req, res) {
    var fbUserId = req.body.fbId,
    fbAccessToken = req.body.fbAccessToken,
    accessToken = req.body.accessToken;
    ...
    graph.setAppSecret(config.facebook.app.secret);
    graph.setAccessToken(fbAccessToken);

    var graphUser;
    var p = graph.getAsync('me?fields=id,name,picture')
        .then(function (fbGraphUser) {
            //when the given fb id and token mismatch:
            if (!fbGraphUser || fbGraphUser.id !== fbUserId) {
                console.error("Invalid user from fbAccessToken!");
                res.status(HttpStatus.FORBIDDEN).json({});
                return p.cancel();
            }

            graphUser = fbGraphUser;

            return User.fb(fbUserId);
        })
        .then(function (user) {
            if (user) {
                //user found by his FB access token
                res.status(HttpStatus.OK).json({accessToken: user.accessToken});
                //stop the promises chain here
                return p.cancel();
            }
            ...create the user, generate a custom token and return it as above...

https://github.com/iliyan-trifonov/notepads-nodejs-angularjs-mongodb-bootstrap/blob/6617a5cb418ba8acd6351ef9a9f69228f1047154/src/routes/users.js#L46 .

Le modèle de l'Utilisateur:

var userSchema = new mongoose.Schema({
    facebookId: { type: String, required: true, unique: true },
    accessToken: { type: String, required: true, unique: true },
    name: { type: String, required: true },
    photo: { type: String, required: true },
    categories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Category' }],
    notepads: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Notepad' }]
});

https://github.com/iliyan-trifonov/notepads-nodejs-angularjs-mongodb-bootstrap/blob/master/src/models/user.js#L9 .

l'auteur Facebook dans l'application mobile:

               auth: function(fbId, fbAccessToken) {
                return $http({
                    url: apiBase + '/users/auth',
                    data: {
                        fbId: fbId,
                        fbAccessToken: fbAccessToken
                    },
                    method: 'POST',
                    cache: false
                });
            },
            ...

https://github.com/iliyan-trifonov/notepads-ionic/blob/master/www/js/services.js#L33 .

l'application mobile envoie le token avec la requête:

  notepads: {
            list: function() {
                return $http({
                    url: apiBase + '/notepads?insidecats=1' + '&token=' + User.get().accessToken/*gets the token from the local storage*/,
                    method: 'GET',
                    cache: false
                });
            },

c'est une application ionique/angulaire/Cordova. La connexion Facebook à partir de l'application mobile démarre L'application Facebook installée sur votre téléphone ou ouvre une fenêtre popup pour se connecter sur Facebook. Ensuite, un rappel renvoie l'id de L'utilisateur Facebook et le jeton d'accès à mon application mobile.

le fbgraphe module npm:https://github.com/criso/fbgraph

6
répondu Iliyan Trifonov 2015-06-21 00:08:31

Iv'e a créé les schémas suivants:

var userSchema = mongoose.Schema({
    local: {
        username: String,
        password: String
    },
    facebook: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    google: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    token: {
        type: Schema.Types.ObjectId,
        ref: 'Token',
        default: null
    }
});

var tokenSchema = mongoose.Schema({
    value: String,
    user: {
        type: Schema.Types.ObjectId,
        ref: 'User'
    },
    expireAt: {
        type: Date,
        expires: 60,
        default: Date.now
    }
});

lors de la connexion à mon application web, j'utilise un plugin social PassportJS comme facebook/google. Exemple:

//auth.js(router)
router.get('/facebook', passport.authenticate('facebook', {scope: ['email']}));

router.get('/facebook/callback', 
  passport.authenticate('facebook', { successRedirect: '/profile',
                                      failureRedirect: '/' }));

maintenant, quand je veux naviguer dans mon application web, j'utilise l'authentification de session qui m'a été rendue disponible via le plugin Facebook. Quand un utilisateur veut demander un token API, il doit être connecté pour que je puisse associer ce token à l'utilisateur.

donc maintenant un utilisateur a un token associé avec eux, ils peuvent utiliser pour l'authentification des tokens pour mon API.

mes routes API ne se soucient pas ou ne regardent pas les sessions, Tout ce qui les intéresse c'est le token. Iv'e l'a fait en créant un routeur express, et en utilisant la stratégie de porteur de passeport comme un middleware pour toutes les routes se dirigeant vers mon API.

//api.js (router)
//router middleware to use TOKEN authentication on every API request
router.use(passport.authenticate('bearer', { session: false }));

router.get('/testAPI', function(req, res){
        res.json({ SecretData: 'abc123' });
    });

donc maintenant j'utilise seulement l'authentification de token pour mon API( ne regarde jamais les données de session), et l'authentification de Session pour naviguer facilement sur mon webapp. Un exemple de moi utilisant les sessions pour naviguer dans mon application sont indiquées ci-dessous:

//secure.js(router) - Access private but NON-API routes.
//router middleware, uses session authentication for every request
router.use(function(req, res, next){
        if(req.isAuthenticated()){
            return next();
        }
        res.redirect('/auth');
    });
//example router
router.get('/profile', function(req, res){
        res.send("Private Profile data");
    });

espérons que cela vous aidera!

2
répondu Brent Aureli 2015-04-16 02:30:21

Les stratégies des médias sociaux pour passeport dépendent de la session. Ils ne fonctionneront pas sans.

je suis dans le même numéro que je voulais que mon serveur de repos soit apatride.

À mon avis il y a 2 options.

  1. ajouter des sessions à votre serveur rest
  2. Ajouter un nouveau serveur auth qui est responsable de la création et de la distribution des tokens.

PS: vous devriez étiqueter ceci comme passeport afin que le port de passage devs pourrait voir.

0
répondu Douglas Ferguson 2015-01-02 05:03:36

si vous utilisez un token porteur, vous n'avez qu'à transmettre l'identifiant unique à l'utilisateur de l'API. Cela serait très probablement fait dans un appel singulier peut-être sous la forme d'un login. Ensuite, chaque appel à L'API nécessite ce token qui vérifie que le token existe dans la base de données et met à jour sa valeur Time To Live. Vous n'avez pas besoin de tokens individuels dans votre schéma pour chaque connexion vous avez besoin d'un schéma individuel pour les tokens qui a une valeur de temps à vivre (TTL)

0
répondu chacliff 2015-05-15 17:19:03