2017-07-23 5 views
0

Je crée un serveur REST au nodejs en utilisant express.Autorisation dans un serveur REST NodeJS Express

Je voudrais autoriser certains utilisateurs à effectuer certains appels. C'est-à-dire d'avoir un administrateur qui peut éditer d'autres utilisateurs et voir des rapports, où un utilisateur peut seulement effectuer des actions simples. J'essayais d'utiliser passport.js et passport-ldapauth, et je voudrais également effectuer différentes requêtes pour l'authentification (vérifier les informations d'identification) et l'autorisation (vérifier si l'utilisateur fait partie d'un groupe).

var fs = require('fs'); 
var express = require('express'); 
var path = require('path'); 
var cookieParser = require('cookie-parser'); 
var bodyParser = require('body-parser'); 
var passport = require('passport'); 
var LdapStrategy = require('passport-ldapauth'); 

var app = express(); 

app.use(logger('dev')); 
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: false })); 
app.use(cookieParser()); 
app.use(express.static(path.join(__dirname, 'public'))); 


// Allow self signed certificates 
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 

app.use('/', index); 
app.use('/users', users); 
var OPTS = { 
    server: { 
    url: 'ldaps://...', 
    bindDN: 'cn=Manager,dc=com', 
    bindCredentials: 'secret', 
    searchBase: 'ou=people,dc=com', 
    searchFilter: '(uid={{username}})', 
    tlsOptions: { 
     ca: [fs.readFileSync('/path/to/certificate.crt')] 
    } 
    }, 
    handleErrorsAsFailures: true, 
    failureErrorCallback: (err) => console.log(err) 
}; 

passport.use(new LdapStrategy(OPTS)); 
passport.use('test', new LdapStrategy(OPTS)); 

app.use(passport.initialize()); 

app.post('/login', function(req, res, next) { 
    passport.authenticate('ldapauth', function(err, user, info) { 
     if (err) return next(err); 
     if (!user) return res.status(401).send(info); 

     res.json(user); 
     // req.logIn(user, function(err) { 
     // if (err) 
     //  console.error(err); 
     // if (err) return next(err); 
     // return res.json(user); 
     // }) 
    })(req, res, next); 
    }); 
+1

qu'en est-il de jwt. J'ai encore essayé? –

Répondre

1

La stratégie passport-ldapauth ne vous permet pas d'effectuer des contrôles ou des requêtes supplémentaires autant que je sache à la lecture sur la documentation. La stratégie et le passeport en général visent à rendre le processus de connexion/authentification simple et transparent. Donc, toutes les contraintes supplémentaires devront être traitées par vous-même. Cela dit, passport-ldapauth utilise ldapauth-fork en dessous de laquelle ldapjs est utilisé. Vous pouvez essayer d'utiliser ldapjs comme indiqué here et here, mais je pense que la solution la plus simple serait d'utiliser ldapauth-fork directement.

Nous avons d'abord besoin de mettre en place ldapauth-fork, donc nous allons utiliser l'exemple suivant app/ldap/index.js:

const LdapAuth = require('ldapauth-fork') 

const ldap = new LdapAuth({ 
    url: 'ldaps://...', 
    bindDN: 'cn=Manager,dc=com', 
    bindCredentials: 'secret', 
    searchBase: 'ou=people,dc=com', 
    searchFilter: '(uid={{username}})', 
    tlsOptions: { 
    ca: [fs.readFileSync('/path/to/certificate.crt')] 
}) 

ldap.on('error', (err) => { throw err }) 

module.exports = ldap 

Notre exemple app/controllers/auth.js pourrait ressembler à ceci:

const jwt = require('jsonwebtoken') 
const ldap = require('../ldap') 
const { User } = require('../database/models') // mongoose model 
const Promise = require('bluebird') 

exports.login = async (req, res) => { 
    const { username, password } = req.body 

    if (!username || !password) { 
    res.status(400 
    res.json({ error: 'Missing username or password.' }) 
    return 
    } 

    // ldapauth-fork doesn't support Promises. 
    // You can try to promisfy it, but I prefer this. 
    // I've named it `profile`, but you can name it whatever you want. 
    const profile = await Promise.fromCallback(cb => ldap.authenticate(username, password, cb)) 

    // Since this is a REST API, we need to send back a token. 
    // For this example, we're creating it by hand. 
    const token = jwt.sign({ user: profile }, 'secret', {}) 

    // Use epoch time from the token instead of generating it ourselves. 
    const { exp } = jwt.verify(token, 'secret') 

    // Finally send the token. 
    // By convention, the keys are snake case. 
    res.json({ 
    access_token: token, 
    token_type: 'Bearer', 
    expires_in: exp, 
    user: profile 
    }) 
} 

Maintenant que nous avons notre jeton créé, nous avons besoin d'un moyen de vérifier ce jeton. Pour ce faire, nous devons écrire notre propre middleware. Ainsi, par exemple app/middleware/valid-token.js:

const jwt = require('jsonwebtoken') 

exports.needsAdminAccess = (req, res, next) => { 
    // This token should have already been validated by the `requiresToken` middleware 
    let token = req.header('authorization').split(' ')[1] 
    token = jwt.verify(token, 'secret') 

    // Let's check if they are in the admin group 
    // Remember that we set the user/profile value in the controller. 
    if (!token.user.dn.includes('ou=ADMIN')) { 
    next(new Error('You must be an admin to access this route.')) 
    return 
    } 

    // Any additional checks would go here. 
    // ... 

    // If everything is fine then call next to let the request continue. 
    next() 
} 

exports.requiresToken = (req, res, next) => { 
    // Assuming the token is in the header as Authorization: Bearer token 
    let token = req.header('authorization').split(' ')[1] 

    // Make sure our secret key matches 
    token = jwt.verify(token, 'secret') 

    // Additional checks of the token should be done here as well. 
    // ... 

    // Don't forget to call next if all is good 
    next() 
} 

Enfin, nous utilisons le middleware où vous définissez vos itinéraires, par exemple:

const express = require('express') 
const app = express() 
const { requiresToken, needsAdminAccess } = require('./middleware/valid-token') 

// This route needs a valid token, but not admin rights 
app.get('/user', requiresToken, (req, res) => { }) 

// This route needs a valid token AND admin rights 
app.get('/admin', requiresToken, needsAdminAccess, (req, res) => { }) 

j'ai écrit tout à partir de zéro dans l'espoir peindre une image claire sur la façon dont tout fonctionne. Vous pouvez utiliser les packages other pour valider le jeton, mais nous devons vérifier certaines choses afin d'écrire les nôtres.

+0

semble bon, mais je ne suis toujours pas clair sur la façon d'ajouter l'autorisation à des appels spécifiques. disons que j'ai un chemin '/ admin' et un chemin'/user', et je veux que seuls les admins puissent accéder au premier chemin, et tout utilisateur authentifié pour accéder au second chemin, comment puis-je faire cela? –

+1

J'ai ajouté un exemple. Voir ma réponse mise à jour ci-dessus. –