Skip to content

Instantly share code, notes, and snippets.

@itaditya
Forked from danwit/passport_node_acl_example.js
Last active November 4, 2017 05:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itaditya/e145a27c55b42fd8456d8a7f53eb42fe to your computer and use it in GitHub Desktop.
Save itaditya/e145a27c55b42fd8456d8a7f53eb42fe to your computer and use it in GitHub Desktop.
Authentication and authorization with passportjs + node_acl + mongo + express + es6
const express = require('express'),
mongodb = require('mongodb'),
passport = require('passport'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
methodOverride = require('method-override'),
session = require('express-session'),
node_acl = require('acl'),
app = express(),
localStrategy = require('passport-local').Strategy;
let acl;
// Some test data. Get this from your database.
const users = [{
id: 1,
username: 'bob',
password: 'secret',
email: 'bob@example.com'
}, {
id: 2,
username: 'joe',
password: 'birthday',
email: 'joe@example.com'
}];
// Setup express
app.use(cookieParser());
app.use(bodyParser());
app.use(methodOverride());
app.use(session({
secret: 'Example'
}));
// Initialize Passport. Also use passport.session() middleware, to support
// persistent login sessions.
app.use(passport.initialize());
app.use(passport.session());
// Error handling
app.use((error, req, res, next) => {
if (!error) {
return next();
}
res.send(error.msg, error.errorCode);
});
authentication_setup();
// Connecting to mongo database and setup authorization
mongodb.connect('mongodb://127.0.0.1:27017/acl', authorization_setup);
// Setting up passport
function authentication_setup() {
// Setup session support
passport.serializeUser((user, done) => done(null, user.id));
passport.deserializeUser((id, done) => {
find_user_by_id(id, (error, user) => done(error, user));
});
// Setup strategy (local in this case)
passport.use(new localStrategy((username, password, done) => {
process.nextTick(() => {
find_by_username(username, (error, user) => {
if (error) {
return done(error);
}
if (!user) {
return done(null, false, {
message: 'Unknown user ' + username
});
}
if (user.password !== password) {
return done(null, false, {
message: 'Invalid password'
});
}
// Authenticated
return done(null, user);
});
});
}));
}
// Setting up node_acl
function authorization_setup(error, db) {
var mongoBackend = new node_acl.mongodbBackend(db /*, {String} prefix */ );
// Create a new access control list by providing the mongo backend
// Also inject a simple logger to provide meaningful output
acl = new node_acl(mongoBackend, logger());
set_roles();
set_routes();
}
function set_roles() {
acl.allow([{
roles: 'admin',
allows: [{
resources: '/secret',
permissions: '*'
}]
}, {
roles: 'user',
allows: [{
resources: '/secret',
permissions: 'get'
}]
}, {
roles: 'guest',
allows: []
}]);
// Inherit roles
// Every user is allowed to do what guests do
// Every admin is allowed to do what users do
acl.addRoleParents('user', 'guest');
acl.addRoleParents('admin', 'user');
}
// Defining routes ( resources )
function set_routes() {
// Check your current user and roles
app.get('/status', (req, res) => {
acl.userRoles(get_user_id(req, res), (error, roles) => {
res.send(`User: ${JSON.stringify(req.user)} Roles: ${JSON.stringify(roles)}`)
});
});
// Only for users and higher
app.get('/secret', [authenticated, acl.middleware(1, get_user_id)], (req, res) => {
res.send('Welcome Sir!');
});
// Logging out the current user
app.get('/logout', (req, res) => {
req.logout();
res.send('Logged out!');
});
// Logging in a user
// http://localhost:3500/login?username=bob&password=secret
app.get('/login', passport.authenticate('local', {}), (req, res) => {
res.send('Logged in!');
});
// Setting a new role
app.get('/allow/:user/:role', (req, res, next) => {
const { user, role } = req.params;
acl.addUserRoles(user, role);
res.send(`${user} is a ${role}`);
});
// Unsetting a role
app.get('/disallow/:user/:role', (req, res, next) => {
const { user, role } = req.params;
acl.removeUserRoles(user, role);
res.send(`${user} is not a ${role} anymore.`);
});
}
// This gets the ID from currently logged in user
function get_user_id(req, res) {
// Since numbers are not supported by node_acl in this case, convert
// them to strings, so we can use IDs nonetheless.
return req.user && req.user.id.toString() || false;
}
// Helper used in session setup by passport
function find_user_by_id(id, cb) {
var index = id - 1;
if (users[index]) {
cb(null, users[index]);
} else {
var error = new Error('User does not exist.');
error.status = 404;
cb(error);
}
}
// Helper used in the local strategy setup by passport
function find_by_username(username, cb) {
const user = users.find(user => user.username === username);
if(user) {
return cb(null, user);
}
return cb(null, null);
}
// Generic debug logger for node_acl
function logger() {
return {
debug: (msg) => console.log('-DEBUG-', msg)
};
}
// Authentication middleware for passport
function authenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.send(401, 'User not authenticated');
}
app.listen(3500, () => {
console.log('Express server listening on port 3500');
});
@itaditya
Copy link
Author

itaditya commented Nov 4, 2017

Simple authentication and authorization example with passport, node_acl, MongoDB and expressjs(v4.x)

The example shown here uses local userdata and sessions to remember a
logged in user. Roles are persistent all the way and applied to user
after logging in.

Usage:

  1. Start this as server

  2. Play with the resoures
    Login via GET
    http://localhost:3500/login?username=bob&password=secret

    Logout
    http://localhost:3500/logout

    Check your current user and roles
    http://localhost:3500/status

    Only visible for users and higher
    http://localhost:3500/secret

    Manage roles
    user is either 1 or 2 and role is either 'guest', 'user' or 'admin'
    http://localhost:3500/allow/:user/:role
    http://localhost:3500/disallow/:user/:role

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment