Authentification NodeJS REST à l’aide de Passport et du réseau social OAuth2 +

Je travaille sur api REST utilisant NodeJS . Pour l’authentification, j’ai décidé d’utiliser Passport . Je veux vraiment api RESTful. Cela signifie donc que je dois utiliser des jetons au lieu de sessions.

Je souhaite permettre aux utilisateurs de se connecter à l’aide d’un nom d’utilisateur et d’un mot de passe ou des réseaux sociaux tels que Facebook, Google et Twitter.

Je OAuth2.0 mon propre serveur OAuth2.0 pour l’émission de Refresh tokens Access et de Refresh tokens aide oauth2orize module oauth2orize . Alors maintenant, je peux enregistrer un nouvel utilisateur, puis lui atsortingbuer des jetons. J’ai suivi ce tutoriel:

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

Vérification de l’utilisateur pour l’itinéraire:

 // 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 assez bien. Malheureusement, je ne sais pas comment implémenter 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 véritablement RESTful. J’ai déjà implémenté le schéma d’utilisateur selon ce tutoriel où les jetons 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: Ssortingng, unique: true, required: true }, hashedPassword: { type: Ssortingng, required: true }, created: { type: Date, default: Date.now } }, facebook: { id: Ssortingng, token: Ssortingng, email: Ssortingng, name: Ssortingng }, twitter: { id: Ssortingng, token: Ssortingng, displayName: Ssortingng, username: Ssortingng }, google: { id: Ssortingng, token: Ssortingng, email: Ssortingng, name: Ssortingng } }); 

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

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

c’est vérifier seulement le jeton porteur contre ma firebase database, mais comment puis-je vérifier les jetons sociaux? Est-ce que je manque quelque chose?

J’utilise la connexion Facebook pour ma propre API RESTful pour mon application Notepads ici . J’ai démarré l’application sous la forme d’une page Web, mais la communication après la connexion se fera via l’API.

Ensuite, j’ai décidé de créer une version mobile de la même application qui utilisera l’API. J’ai décidé de le faire ainsi: l’application mobile se connecte via Facebook et envoie l’ID utilisateur Facebook et le jeton d’access FB à l’API, l’API appelle l’API de Facebook pour vérifier ces parameters et enregistre avec succès un nouvel utilisateur (ou existant) dans la firebase database de mon application, crée un jeton personnalisé pour cet utilisateur et le renvoie à l’application mobile. À partir de là, l’application mobile envoie ce jeton personnalisé pour authentifier l’application avec l’API.

Voici un code:

L’authentification dans l’API (utilise le module fbgraph npm):

 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-sortingfonov/notepads-nodejs-angularjs-mongodb-bootstrap/blob/6617a5cb418ba8acd6351ef9a9f69228f1047154/src/routes/users.js#L46 .

Le modèle d’utilisateur:

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

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

L’authentification 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-sortingfonov/notepads-ionic/blob/master/www/js/services.js#L33 .

L’application mobile envoie le jeton avec la demande:

  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 / angular / Cordova. La connexion Facebook de l’application mobile démarre l’application Facebook installée sur votre téléphone ou ouvre une fenêtre contextuelle pour vous connecter à Facebook. Ensuite, un rappel renvoie l’identifiant de l’utilisateur Facebook et le jeton d’access à mon application mobile.

Le module fbgraph npm: https://github.com/criso/fbgraph

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

 var userSchema = mongoose.Schema({ local: { username: Ssortingng, password: Ssortingng }, facebook: { id: Ssortingng, token: Ssortingng, email: Ssortingng, name: Ssortingng }, google: { id: Ssortingng, token: Ssortingng, email: Ssortingng, name: Ssortingng }, token: { type: Schema.Types.ObjectId, ref: 'Token', default: null } }); var tokenSchema = mongoose.Schema({ value: Ssortingng, user: { type: Schema.Types.ObjectId, ref: 'User' }, expireAt: { type: Date, expires: 60, default: Date.now } }); 

Lorsque je me connecte à mon application Web, j’utilise un plug-in de PassportJS Social tel que 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, lorsque je veux naviguer dans mon application Web, j’utilise l’authentification de session mise à ma disposition via le plug-in Facebook. Lorsqu’un utilisateur souhaite demander un jeton d’API, il doit être connecté pour pouvoir associer ce jeton à l’utilisateur.

Alors maintenant, un utilisateur est associé à un jeton qu’il peut utiliser pour l’authentification de jeton pour mon API.

Mes routes API ne s’intéressent pas aux sessions, elles ne s’intéressent qu’aux jetons. C’est ce que j’ai fait en créant un routeur express et en utilisant la stratégie de passeport en tant que middleware pour tous les itinéraires menant à 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' }); }); 

Alors maintenant, je n’utilise que l’authentification par jeton pour mes API (ne regarde jamais les données de session) et l’authentification par session pour naviguer facilement dans mon application Web. Voici un exemple d’utilisation de sessions pour naviguer dans mon application:

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

J’espère que cela vous aidera!

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

J’exécute le même problème. Je voulais que mon serveur REST soit sans état.

À mon avis, il y a 2 options.

  1. Ajouter des sessions à votre serveur de repos
  2. Ajoutez un nouveau serveur d’authentification chargé de créer et de dissortingbuer le jeton.

PS: Vous devriez le marquer comme passeport pour que les développeurs de ports de passage puissent le voir.

Si vous utilisez un jeton porteur, il vous suffit de transmettre l’identificateur unique à l’utilisateur de l’API. Cela se ferait très probablement en un seul appel, peut-être sous la forme d’un identifiant. Chaque appel à l’API nécessite ensuite ce jeton qui vérifie que le jeton existe dans la firebase database et met à jour sa valeur Time To Live. Vous n’avez pas besoin de jetons individuels dans votre schéma pour chaque connexion; vous avez besoin d’un schéma individuel pour les jetons qui a une durée de vie (TTL).