Skip to content

Instantly share code, notes, and snippets.

@AkarshSatija
Created July 17, 2015 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save AkarshSatija/71a544c3d4d4ab1c34d7 to your computer and use it in GitHub Desktop.
Save AkarshSatija/71a544c3d4d4ab1c34d7 to your computer and use it in GitHub Desktop.
diff file for Setting up oAuth2 server in MEAN.JS(meanjs.org)
diff --git a/app/controllers/oauth.server.controller.js b/app/controllers/oauth.server.controller.js
new file mode 100644
index 0000000..a3c0c7a
--- /dev/null
+++ b/app/controllers/oauth.server.controller.js
@@ -0,0 +1,208 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var mongoose = require('mongoose'),
+ _ = require('lodash');
+
+
+var oauth2orize = require('oauth2orize'),
+ passport = require('passport'),
+ crypto = require('crypto')/*,
+ config = require('./config')*/;
+
+var faker = require('Faker');
+
+var UserModel = mongoose.model('User'),
+ ClientModel = mongoose.model('Client'),
+ AccessTokenModel = mongoose.model('AccessToken'),
+ RefreshTokenModel = mongoose.model('RefreshToken');
+
+// create OAuth 2.0 server
+var server = oauth2orize.createServer();
+
+// Exchange username & password for an access token.
+server.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
+ UserModel.findOne({ username: username }, function(err, user) {
+ if (err) { return done(err); }
+ if (!user) { return done(null, false); }
+ if (!user.authenticate(password)) { return done(null, false); }
+
+ RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {
+ if (err) return done(err);
+ });
+ AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {
+ if (err) return done(err);
+ });
+
+ var tokenValue = crypto.randomBytes(32).toString('hex');
+ var refreshTokenValue = crypto.randomBytes(32).toString('hex');
+ var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.id });
+
+ var refreshToken = new RefreshTokenModel({
+ token: refreshTokenValue,
+ clientId: client.clientId,
+ userId: user.id });
+ console.log(refreshToken);
+ refreshToken.save(function (err) {
+
+ if (err) { return done(err); }
+ });
+ var info = { scope: '*' }
+ token.save(function (err, token) {
+ if (err) { return done(err); }
+ done(null, tokenValue, refreshTokenValue, { 'expires_in': 3600/*config.get('security:tokenLife')*/ });
+ });
+ });
+}));
+
+// Exchange refreshToken for an access token.
+server.exchange(oauth2orize.exchange.refreshToken(function(client, refreshToken, scope, done) {
+ RefreshTokenModel.findOne({ token: refreshToken }, function(err, token) {
+ if (err) { return done(err); }
+ if (!token) { return done(null, false); }
+ if (!token) { return done(null, false); }
+
+ UserModel.findById(token.userId, function(err, user) {
+ if (err) { return done(err); }
+ if (!user) { return done(null, false); }
+
+ RefreshTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {
+ if (err) return done(err);
+ });
+ AccessTokenModel.remove({ userId: user.userId, clientId: client.clientId }, function (err) {
+ if (err) return done(err);
+ });
+
+ var tokenValue = crypto.randomBytes(32).toString('hex');
+ var refreshTokenValue = crypto.randomBytes(32).toString('hex');
+ var token = new AccessTokenModel({ token: tokenValue, clientId: client.clientId, userId: user.id });
+ var refreshToken = new RefreshTokenModel({ token: refreshTokenValue, clientId: client.clientId, userId: user.id });
+ refreshToken.save(function (err) {
+ if (err) { return done(err); }
+ });
+ var info = { scope: '*' }
+ token.save(function (err, token) {
+ if (err) { return done(err); }
+ done(null, tokenValue, refreshTokenValue, { 'expires_in': 3600/*config.get('security:tokenLife')*/ });
+ });
+ });
+ });
+}));
+
+// token endpoint
+exports.token = [
+ passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),
+ server.token(),
+ server.errorHandler()
+];
+
+
+
+/**
+ * Setting up oauth Tokens and users
+ */
+var out=[];
+exports.setup = function(req, res) {
+ /*UserModel.remove({}, function(err) {
+ var user = new UserModel({ username: "andrey", password: "simplepassword", wpId:'12345678', provider:"local",bookmarks:[] });
+ user.save(function(err, user) {
+ if(err){
+ return console.log(err); exit;
+ }
+ else
+ console.log("New user - %s:%s",user.username,user.password);
+ });*/
+
+ /*for(var i=0; i<4; i++) {
+ var user = new UserModel({ username: faker.random.first_name().toLowerCase(), password: faker.Lorem.words(1)[0] , wpId:'12345678', Provider:"local",bookmarks:[] });
+ user.save(function(err, user) {
+ if(err){
+ return console.log(err); exit;
+ }
+ else
+ console.log("New user - %s:%s",user.username,user.password);
+ });
+ }*/
+ // });
+
+ ClientModel.remove({}, function(err) {
+ var client = new ClientModel({ name: "OurService iOS client v1", clientId: "mobileV1", clientSecret:"abc123456" });
+ client.save(function(err, client) {
+ if(err){
+ return console.log(err); exit;
+ }
+ else
+ console.log("New client - %s:%s",client.clientId,client.clientSecret);
+ });
+ });
+
+ /*AccessTokenModel.remove({}, function (err) {
+ if (err){
+ return console.log(err); exit;
+ }
+ });
+
+ RefreshTokenModel.remove({}, function (err) {
+ if (err){
+ return console.log(err); exit;
+ }
+ });*/
+
+ /*setTimeout(function() {
+ mongoose.disconnect();
+ res.json(out);
+ }, 3000);*/
+};
+
+
diff --git a/app/controllers/users/users.authorization.server.controller.js b/app/controllers/users/users.authorization.server.controller.js
index 932e490..be1192d 100644
--- a/app/controllers/users/users.authorization.server.controller.js
+++ b/app/controllers/users/users.authorization.server.controller.js
@@ -5,7 +5,8 @@
*/
var _ = require('lodash'),
mongoose = require('mongoose'),
- User = mongoose.model('User');
+ User = mongoose.model('User'),
+ passport = require('passport');
/**
* User middleware
@@ -24,14 +25,31 @@ exports.userByID = function(req, res, next, id) {
*/
exports.requiresLogin = function(req, res, next) {
if (!req.isAuthenticated()) {
- return res.status(401).send({
- message: 'User is not logged in'
- });
+ //call another auth method
+ exports.oauthLogin(req, res, next);
}
-
- next();
+ else
+ next();
};
+exports.oauthLogin = function(req, res, next) {
+ passport.authenticate('bearer', { session: false })(req,res,function(){
+ exports.requireOauthLogin(req, res, next);
+ });
+}
+
+
+exports.requireOauthLogin = function(req, res, next) {
+ if (!req.isAuthenticated()) {
+ console.log("err");
+
+ }
+ else
+ next();
+}
+
+
+
/**
* User authorizations routing middleware
*/
diff --git a/app/models/client.server.model.js b/app/models/client.server.model.js
index 1737b96..563b0e3 100644
--- a/app/models/client.server.model.js
+++ b/app/models/client.server.model.js
@@ -20,10 +20,15 @@ var ClientSchema = new Schema({
type: Date,
default: Date.now
},
- user: {
- type: Schema.ObjectId,
- ref: 'User'
- }
+ clientId: {
+ type: String,
+ unique: true,
+ required: true
+ },
+ clientSecret: {
+ type: String,
+ required: true
+ }
});
mongoose.model('Client', ClientSchema);
\ No newline at end of file
diff --git a/app/models/oauth.server.model.js b/app/models/oauth.server.model.js
new file mode 100644
index 0000000..5c65186
--- /dev/null
+++ b/app/models/oauth.server.model.js
@@ -0,0 +1,58 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var mongoose = require('mongoose'),
+ Schema = mongoose.Schema;
+
+/**
+ * Oauth Schema
+ */
+
+
+// AccessToken
+var AccessToken = new Schema({
+ userId: {
+ type: String,
+ required: true
+ },
+ clientId: {
+ type: String,
+ required: true
+ },
+ token: {
+ type: String,
+ unique: true,
+ required: true
+ },
+ created: {
+ type: Date,
+ default: Date.now
+ }
+});
+
+mongoose.model('AccessToken', AccessToken);
+
+// RefreshToken
+var RefreshToken = new Schema({
+ userId: {
+ type: String,
+ required: true
+ },
+ clientId: {
+ type: String,
+ required: true
+ },
+ token: {
+ type: String,
+ unique: true,
+ required: true
+ },
+ created: {
+ type: Date,
+ default: Date.now
+ }
+});
+
+mongoose.model('RefreshToken', RefreshToken);
diff --git a/app/routes/articles.server.routes.js b/app/routes/articles.server.routes.js
index 6cdcc96..db0a908 100644
--- a/app/routes/articles.server.routes.js
+++ b/app/routes/articles.server.routes.js
@@ -9,7 +9,7 @@ var users = require('../../app/controllers/users.server.controller'),
module.exports = function(app) {
// Article Routes
app.route('/articles')
- .get(articles.list)
+ .get(users.requiresLogin, articles.list)
.post(users.requiresLogin, articles.create);
app.route('/articles/:articleId')
diff --git a/app/routes/oauth.server.routes.js b/app/routes/oauth.server.routes.js
new file mode 100644
index 0000000..c5aaf97
--- /dev/null
+++ b/app/routes/oauth.server.routes.js
@@ -0,0 +1,53 @@
+'use strict';
+
+
+/**
+ * Module dependencies.
+ */
+var users = require('../../app/controllers/users.server.controller'),
+ oauth2 = require('../../app/controllers/oauth.server.controller');
+
+
+var passport = require('passport');
+
+module.exports = function(app) {
+ // Routing logic
+ // ...
+
+ app.post('/oauth/token', oauth2.token);
+
+ app.get('/oauth/setup', oauth2.setup);
+
+
+
+/* Test URLs*/
+ app.get('/api/userInfo',users.requiresLogin,
+
+ function(req, res) {
+ // req.authInfo is set using the `info` argument supplied by
+ // `BearerStrategy`. It is typically used to indicate a scope of the token,
+ // and used in access control checks. For illustrative purposes, this
+ // example simply returns the scope in the response.
+ if(req.isAuthenticated())
+ res.json(req.user)
+ else
+ res.json({message:"not logged in"})
+ }
+ );
+
+
+ app.get('/api/userinf',
+ 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 a 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.id, name: req.user.username, authInfo: req.authInfo })
+ }
+ );
+
+
+
+
+};
\ No newline at end of file
diff --git a/app/tests/oauth.server.model.test.js b/app/tests/oauth.server.model.test.js
new file mode 100644
index 0000000..6148085
--- /dev/null
+++ b/app/tests/oauth.server.model.test.js
@@ -0,0 +1,55 @@
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+var should = require('should'),
+ mongoose = require('mongoose'),
+ User = mongoose.model('User'),
+ Oauth = mongoose.model('Oauth');
+
+/**
+ * Globals
+ */
+var user, oauth;
+
+/**
+ * Unit tests
+ */
+describe('Oauth Model Unit Tests:', function() {
+ beforeEach(function(done) {
+ user = new User({
+ firstName: 'Full',
+ lastName: 'Name',
+ displayName: 'Full Name',
+ email: 'test@test.com',
+ username: 'username',
+ password: 'password'
+ });
+
+ user.save(function() {
+ oauth = new Oauth({
+ // Add model fields
+ // ...
+ });
+
+ done();
+ });
+ });
+
+ describe('Method Save', function() {
+ it('should be able to save without problems', function(done) {
+ return oauth.save(function(err) {
+ should.not.exist(err);
+ done();
+ });
+ });
+ });
+
+ afterEach(function(done) {
+ Oauth.remove().exec();
+ User.remove().exec();
+
+ done();
+ });
+});
\ No newline at end of file
diff --git a/config/env/all.js b/config/env/all.js
index 435fe9e..54a47da 100644
--- a/config/env/all.js
+++ b/config/env/all.js
@@ -31,6 +31,11 @@ module.exports = {
},
// The session cookie name
sessionName: 'connect.sid',
+ // Token Life in seconds
+ "security": {
+ "tokenLife" : 3600
+ },
+ //Logs
log: {
// Can specify one of 'combined', 'common', 'dev', 'short', 'tiny'
format: 'combined',
diff --git a/config/passport.js b/config/passport.js
index 5abfae7..ca5015c 100755
--- a/config/passport.js
+++ b/config/passport.js
@@ -8,6 +8,10 @@ var passport = require('passport'),
path = require('path'),
config = require('./config');
+var BasicStrategy = require('passport-http').BasicStrategy,
+ ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
+ BearerStrategy = require('passport-http-bearer').Strategy;
+
/**
* Module init function.
*/
diff --git a/config/strategies/local.js b/config/strategies/local.js
index ad56052..ec7ec8a 100644
--- a/config/strategies/local.js
+++ b/config/strategies/local.js
@@ -5,7 +5,15 @@
*/
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
- User = require('mongoose').model('User');
+ mongoose = require('mongoose'),
+ User = mongoose.model('User'),
+ Client = mongoose.model('Client'),
+ AccessTokenModel = mongoose.model('AccessToken');
+
+var BasicStrategy = require('passport-http').BasicStrategy,
+ ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
+ BearerStrategy = require('passport-http-bearer').Strategy;
+
module.exports = function() {
// Use local strategy
@@ -35,4 +43,55 @@ module.exports = function() {
});
}
));
-};
+
+//------- OAuth
+
+ passport.use(new BasicStrategy(
+ function(username, password, done) {
+ Client.findOne({ clientId: username }, function(err, client) {
+ if (err) { return done(err); }
+ if (!client) { return done(null, false); }
+ if (client.clientSecret != password) { return done(null, false); }
+
+ return done(null, client);
+ });
+ }
+ ));
+
+ passport.use(new ClientPasswordStrategy(
+ function(clientId, clientSecret, done) {
+ Client.findOne({ clientId: clientId }, function(err, client) {
+ if (err) { return done(err); }
+ if (!client) { return done(null, false); }
+ if (client.clientSecret != clientSecret) { return done(null, false); }
+
+ return done(null, client);
+ });
+ }
+ ));
+
+ passport.use(new BearerStrategy(
+ function(accessToken, done) {
+ AccessTokenModel.findOne({ token: accessToken }, function(err, token) {
+ if (err) { return done(err); }
+ if (!token) { return done(null, false); }
+
+ if( Math.round((Date.now()-token.created)/1000) > 3600 /*config.get('security:tokenLife')*/ ) {
+ AccessTokenModel.remove({ token: accessToken }, function (err) {
+ if (err) return done(err);
+ });
+ return done(null, false, { message: 'Token expired' });
+ }
+
+ User.findById(token.userId, function(err, user) {
+ if (err) { return done(err); }
+ if (!user) { return done(null, false, { message: 'Unknown user' }); }
+
+ var info = { scope: '*' }
+ done(null, user, info);
+ });
+ });
+ }
+ ));
+
+};
\ No newline at end of file
diff --git a/package.json b/package.json
old mode 100755
new mode 100644
index 091f2e1..8325398
--- a/package.json
+++ b/package.json
@@ -1,74 +1,79 @@
{
- "name": "meanjs",
- "description": "Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js.",
- "version": "0.3.3",
- "private": false,
- "author": "https://github.com/meanjs/mean/graphs/contributors",
- "repository": {
- "type": "git",
- "url": "https://github.com/meanjs/mean.git"
- },
- "engines": {
- "node": ">=0.10.28",
- "npm": ">=1.4.28"
- },
- "scripts": {
- "start": "grunt",
- "test": "grunt test",
- "postinstall": "bower install --config.interactive=false"
- },
- "dependencies": {
- "express": "~4.10.1",
- "express-session": "~1.9.1",
- "body-parser": "~1.9.0",
- "cookie-parser": "~1.3.2",
- "compression": "~1.2.0",
- "method-override": "~2.3.0",
- "morgan": "~1.4.1",
- "connect-mongo": "~0.4.1",
- "connect-flash": "~0.1.1",
- "helmet": "~0.5.0",
- "consolidate": "~0.10.0",
- "swig": "~1.4.1",
- "mongoose": "~3.8.8",
- "passport": "~0.2.0",
- "passport-local": "~1.0.0",
- "passport-facebook": "~1.0.2",
- "passport-twitter": "~1.0.2",
- "passport-linkedin": "~0.1.3",
- "passport-google-oauth": "~0.1.5",
- "passport-github": "~0.1.5",
- "lodash": "~2.4.1",
- "forever": "~0.11.0",
- "bower": "~1.3.8",
- "grunt-cli": "~0.1.13",
- "glob": "~4.0.5",
- "async": "~0.9.0",
- "nodemailer": "~1.3.0",
- "chalk": "~1.0.0"
- },
- "devDependencies": {
- "supertest": "~0.14.0",
- "should": "~4.1.0",
- "grunt-env": "~0.4.1",
- "grunt-node-inspector": "~0.1.3",
- "grunt-contrib-watch": "~0.6.1",
- "grunt-contrib-jshint": "~0.10.0",
- "grunt-contrib-csslint": "^0.3.1",
- "grunt-ng-annotate": "~0.4.0",
- "grunt-contrib-uglify": "~0.6.0",
- "grunt-contrib-cssmin": "~0.10.0",
- "grunt-nodemon": "~0.3.0",
- "grunt-concurrent": "~1.0.0",
- "grunt-mocha-test": "~0.12.1",
- "grunt-karma": "~0.9.0",
- "load-grunt-tasks": "~1.0.0",
- "grunt-contrib-copy": "0.8",
- "karma": "~0.12.0",
- "karma-jasmine": "~0.2.1",
- "karma-coverage": "~0.2.0",
- "karma-chrome-launcher": "~0.1.2",
- "karma-firefox-launcher": "~0.1.3",
- "karma-phantomjs-launcher": "~0.1.2"
- }
+ "name": "meanjs",
+ "description": "Full-Stack JavaScript with MongoDB, Express, AngularJS, and Node.js.",
+ "version": "0.3.3",
+ "private": false,
+ "author": "https://github.com/meanjs/mean/graphs/contributors",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/meanjs/mean.git"
+ },
+ "engines": {
+ "node": ">=0.10.28",
+ "npm": ">=1.4.28"
+ },
+ "scripts": {
+ "start": "grunt",
+ "test": "grunt test",
+ "postinstall": "bower install --config.interactive=false"
+ },
+ "dependencies": {
+ "Faker": "^0.7.2",
+ "async": "~0.9.0",
+ "body-parser": "~1.9.0",
+ "bower": "~1.3.8",
+ "chalk": "~1.0.0",
+ "compression": "~1.2.0",
+ "connect-flash": "~0.1.1",
+ "connect-mongo": "~0.4.1",
+ "consolidate": "~0.10.0",
+ "cookie-parser": "~1.3.2",
+ "express": "~4.10.1",
+ "express-session": "~1.9.1",
+ "forever": "~0.11.0",
+ "glob": "~4.0.5",
+ "grunt-cli": "~0.1.13",
+ "helmet": "~0.5.0",
+ "lodash": "~2.4.1",
+ "method-override": "~2.3.0",
+ "mongoose": "~3.8.8",
+ "morgan": "~1.4.1",
+ "nodemailer": "~1.3.0",
+ "oauth2orize": "^1.0.1",
+ "passport": "~0.2.0",
+ "passport-facebook": "~1.0.2",
+ "passport-github": "~0.1.5",
+ "passport-google-oauth": "~0.1.5",
+ "passport-http": "^0.2.2",
+ "passport-http-bearer": "^1.0.1",
+ "passport-linkedin": "~0.1.3",
+ "passport-local": "~1.0.0",
+ "passport-oauth2-client-password": "^0.1.2",
+ "passport-twitter": "~1.0.2",
+ "swig": "~1.4.1"
+ },
+ "devDependencies": {
+ "supertest": "~0.14.0",
+ "should": "~4.1.0",
+ "grunt-env": "~0.4.1",
+ "grunt-node-inspector": "~0.1.3",
+ "grunt-contrib-watch": "~0.6.1",
+ "grunt-contrib-jshint": "~0.10.0",
+ "grunt-contrib-csslint": "^0.3.1",
+ "grunt-ng-annotate": "~0.4.0",
+ "grunt-contrib-uglify": "~0.6.0",
+ "grunt-contrib-cssmin": "~0.10.0",
+ "grunt-nodemon": "~0.3.0",
+ "grunt-concurrent": "~1.0.0",
+ "grunt-mocha-test": "~0.12.1",
+ "grunt-karma": "~0.9.0",
+ "load-grunt-tasks": "~1.0.0",
+ "grunt-contrib-copy": "0.8",
+ "karma": "~0.12.0",
+ "karma-jasmine": "~0.2.1",
+ "karma-coverage": "~0.2.0",
+ "karma-chrome-launcher": "~0.1.2",
+ "karma-firefox-launcher": "~0.1.3",
+ "karma-phantomjs-launcher": "~0.1.2"
+ }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment