19

Sto lavorando su api REST utilizzando NodeJS. Per l'autenticazione ho deciso di utilizzare Passport. Voglio davvero api RESTful. Quindi significa che devo usare i token invece delle sessioni.Autenticazione REST NodeJS utilizzando Passport e OAuth2 + social network

Desidero consentire agli utenti di accedere utilizzando nome utente e password o utilizzando i social network come Facebook, Google e Twitter.

Realizzo il mio server OAuth2.0 per l'emissione di Access e Refresh tokens utilizzando il modulo oauth2orize. Così ora posso registrare un nuovo utente e poi rilasciargli token. Ho seguito questo tutorial:

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

Verifica utente per percorso:

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

Tutto questo funziona abbastanza bene. Sfortunatamente non so come implementare l'autenticazione sociale.

Stavo leggendo questo tutorial:

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

Ma in questo tutorial non stanno facendo un'API RESTful veramente. Ho già implementato lo schema utente secondo questo tutorial in cui i token per l'utente locale sono memorizzati in modelli separati.

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

Ma ora, come posso verificare l'utente?

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

questo sta verificando unico segno portatore contro al mio db, ma come posso verificare gettoni sociali? Mi sto perdendo qualcosa?

+0

si può elaborare come in ultima analisi, ha risolto questo? Puoi anche lasciare il tuo account Twitter dietro per ulteriore corrispondenza. Grazie :) –

+0

e invece del tutorial di aleksandrov, perché non è possibile utilizzare il passport-oauth? http://passportjs.org/guide/oauth/ –

+0

Anche perché implementare oAuth per il tuo server? perché non usare passaporto-locale e poi emettere token con una strategia al portatore? –

risposta

6

Sto utilizzando il login di Facebook per la mia API RESTful per my Notepads app here. Ho avviato l'applicazione come una che verrà utilizzata come una pagina Web ma ancora la comunicazione dopo l'accesso tramite l'API.

Quindi ho deciso di creare un mobile version of the same app che utilizzerà l'API. Ho deciso di farlo in questo modo: l'app mobile si collega tramite Facebook e invia l'ID utente di Facebook e il token di accesso FB all'API, l'API chiama l'API di Facebook per verificare questi parametri e se registra correttamente un nuovo utente (o registra un esistente) nel DB della mia app, crea un token personalizzato per questo utente e lo restituisce all'app mobile. Da qui l'app mobile invia questo token personalizzato per autenticare l'app con l'API.

Ecco alcuni codice:

L'autenticazione nel API (utilizza il modulo NPM 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.

Il modello User:

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'autenticazione di Facebook nel mobile app:

   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'applicazione mobile invia il token con la richiesta:

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

Si tratta di un/angolare/Cordova app ionico. Il login di Facebook dall'app mobile avvia l'app di Facebook installata sul telefono o apre un popup per accedere a Facebook. Quindi un callback restituisce l'ID dell'utente di Facebook e accede al token alla mia app mobile.

Il modulo NPM fbgraph: https://github.com/criso/fbgraph

+0

Basta formattare gli URL e saranno attivi. Nessuna autorizzazione necessaria per questo. –

+0

Grazie, @NickVolynkin! Ieri sono stato in grado di rendere attivi solo i primi 2 URL e ricevevo un avviso che mi serviva almeno 10 rappresentanti. per il resto. Ma oggi ce l'ho e ho formattato con successo gli URL. –

0

Le strategie di social media per passaporto dipendono dalla sessione. Non funzioneranno senza uno.

Sto correndo nello stesso problema volevo che il mio server REST fosse senza stato.

Secondo me ci sono 2 opzioni.

  1. Aggiungi sessioni al server di riposo
  2. aggiungere un nuovo server di autenticazione, che è responsabile della creazione e distribuendo token.

PS: è necessario contrassegnarlo come passaporto in modo che gli sviluppatori di porte passino possano visualizzarlo.

+0

Invece del tutorial di aleksandrov (http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/) perché non si può usare il passport-oauth? http://passportjs.org/guide/oauth/ –

+0

Questo è il modulo che il tutorial utilizza sotto il cofano. È quello che usa la sessione. I token vengono memorizzati nella sessione in modo che quando il browser lo reindirizza possa associare l'utente/token restituito. Il mio servizio di autenticazione è statico per questo motivo e la mia API è senza stato. –

2

Iv'e creato il seguente schema di:

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

Quando si accede al mio web app, io uso un plugin PassportJS sociali come Facebook/google. Esempio:

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

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

Ora quando voglio navigare la mia applicazione web che utilizzano l'autenticazione di sessione che è stato messo a disposizione tramite il plugin di Facebook. Quando un utente vuole richiedere un token API, è necessario che abbia effettuato l'accesso per poter associare quel token all'utente.

Quindi ora un utente ha un token ad esso associato che possono utilizzare per l'autenticazione token per la mia API.

I miei percorsi API non si preoccupano o guardano le sessioni, tutto quello che interessa è il token. Ho fatto questo creando un router express e utilizzando la strategia del portatore del passaporto come middleware per tutti i percorsi diretti verso la mia 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' }); 
    }); 

Così ora uso solo token di autenticazione per, e l'autenticazione sessione di (Non sempre guardare i dati di sessione) la mia API di navigare facilmente il mio webapp. Un esempio di me che usa le sessioni per navigare nella mia app è il seguente:

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

Speriamo che questo ti possa aiutare!

+2

Grazie ancora! Sì, ho capito il tuo concetto. Tuttavia questo non rende il backend puro RESTful - non esiste un punto di ingresso per l'autenticazione di facebook da parte di un dispositivo Android. Ci dovrebbe essere un meccanismo RESTful per fare questo. Dopo questo la tua soluzione è perfetta, perché i token bearer prendono il sopravvento per generare token specifici dell'app. quindi ho lasciato questo pensiero nel tuo video https://www.youtube.com/watch?v=VVFedQZgUgw –

0

se si utilizza un token al portatore tutto ciò che si deve fare è passare l'identificatore univoco all'utente dell'API. Questo molto probabilmente verrebbe fatto in un'unica chiamata, magari sotto forma di login. Quindi ogni chiamata all'API richiede quel token che verifica il token esistente nel database e aggiorna il suo valore Time To Live. Non hai bisogno di token individuali nel tuo schema per ogni accesso hai bisogno di uno schema individuale per token che ha un valore TTL (Time to Live Value)