Skip to content

Instantly share code, notes, and snippets.

@danwit
Created May 1, 2014 23:23
Show Gist options
  • Star 83 You must be signed in to star a gist
  • Fork 30 You must be signed in to fork a gist
  • Save danwit/e0a7c5ad57c9ce5659d2 to your computer and use it in GitHub Desktop.
Save danwit/e0a7c5ad57c9ce5659d2 to your computer and use it in GitHub Desktop.
Authentication and authorization with passportjs + node_acl + mongo + express
/**
* Simple authentication and authorization example with passport, node_acl,
* MongoDB and expressjs
*
* 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
*/
var express = require( 'express' ),
mongodb = require( 'mongodb' ),
passport = require( 'passport' ),
node_acl = require( 'acl' ),
app = express(),
localStrategy = require( 'passport-local' ).Strategy,
acl;
// Some test data. Get this from your database.
var users = [
{ id: 1, username: 'bob', password: 'secret', email: 'bob@example.com' },
{ id: 2, username: 'joe', password: 'birthday', email: 'joe@example.com' }
];
// Setup express
app.configure( function() {
app.use( express.cookieParser() );
app.use( express.bodyParser() );
app.use( express.methodOverride() );
app.use( express.session( { secret: 'Example' } ) );
// Initialize Passport. Also use passport.session() middleware, to support
// persistent login sessions.
app.use( passport.initialize() );
app.use( passport.session() );
app.use( app.router );
});
// Error handling
app.use( function( error, request, response, next ) {
if( ! error ) {
return next();
}
response.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( function( user, done ) {
done( null, user.id );
});
passport.deserializeUser( function( id, done ) {
find_user_by_id( id, function ( error, user ) {
done( error, user );
});
});
// Setup strategy (local in this case)
passport.use( new localStrategy(
function( username, password, done ) {
process.nextTick( function () {
find_by_username( username, function( 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() );
// Defining roles and routes
set_roles();
set_routes();
}
// This creates a set of roles which have permissions on
// different resources.
function set_roles() {
// Define roles, resources and permissions
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', function( request, response ) {
acl.userRoles( get_user_id( request, response ), function( error, roles ){
response.send( 'User: ' + JSON.stringify( request.user ) + ' Roles: ' + JSON.stringify( roles ) );
});
});
// Only for users and higher
app.get( '/secret',
// Actual auth middleware
[ authenticated, acl.middleware( 1, get_user_id ) ],
function( request, response ) {
response.send( 'Welcome Sir!' );
}
);
// Logging out the current user
app.get( '/logout', function( request, response ) {
request.logout();
response.send( 'Logged out!' );
});
// Logging in a user
// http://localhost:3500/login?username=bob&password=secret
app.get( '/login',
passport.authenticate( 'local', {} ),
function( request, response ) {
response.send( 'Logged in!' );
}
);
// Setting a new role
app.get( '/allow/:user/:role', function( request, response, next ) {
acl.addUserRoles( request.params.user, request.params.role );
response.send( request.params.user + ' is a ' + request.params.role );
});
// Unsetting a role
app.get( '/disallow/:user/:role', function( request, response, next ) {
acl.removeUserRoles( request.params.user, request.params.role );
response.send( request.params.user + ' is not a ' + request.params.role + ' anymore.' );
});
}
// This gets the ID from currently logged in user
function get_user_id( request, response ) {
// Since numbers are not supported by node_acl in this case, convert
// them to strings, so we can use IDs nonetheless.
return request.user && request.user.id.toString() || false;
}
// Helper used in session setup by passport
function find_user_by_id( id, callback ) {
var index = id - 1;
if ( users[ index ] ) {
callback( null, users[ index ] );
} else {
var error = new Error( 'User does not exist.' );
error.status = 404;
callback( error );
}
}
// Helper used in the local strategy setup by passport
function find_by_username( username, callback ) {
var usersLength = users.length,
i;
for ( i = 0; i < usersLength; i++ ) {
var user = users[ i ];
if ( user.username === username ) {
return callback( null, user );
}
}
return callback( null, null );
}
// Generic debug logger for node_acl
function logger() {
return {
debug: function( msg ) {
console.log( '-DEBUG-', msg );
}
};
}
// Authentication middleware for passport
function authenticated( request, response, next ) {
if ( request.isAuthenticated() ) {
return next();
}
response.send( 401, 'User not authenticated' );
}
app.listen( 3500, function() {
console.log( 'Express server listening on port 3500' );
});
@Globik
Copy link

Globik commented Jun 19, 2016

Аnd what about mongodb's built-in roles?

@cloud-tribal
Copy link

@Globik mongodb built-in roles handles roles for db users not application users.

@gbrigens
Copy link

Nice gist,I have a question on sign up do I have to add role in schema so that it will allow user to select the type of role they will we in? eg guests or members?

@jcmina
Copy link

jcmina commented Jun 21, 2017

The code throws type mismatch error

@itaditya
Copy link

itaditya commented Nov 4, 2017

Thanks for this

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