Skip to content
This repository was archived by the owner on Aug 30, 2021. It is now read-only.

[UNRESOLVED] JWT Authentication #1163

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
09389f0
Initial Commit
trainerbill Jan 26, 2016
e3d6b87
Stash
trainerbill Jan 27, 2016
a7d0131
Route changes
trainerbill Jan 27, 2016
02a7ad4
Profile picture working
trainerbill Jan 27, 2016
038e84e
Articles done
trainerbill Jan 27, 2016
68307b7
Update admin user factory to use new route
trainerbill Jan 27, 2016
bc363d2
User Admin working
trainerbill Jan 28, 2016
7838bfd
Social Login Working
trainerbill Jan 28, 2016
1a83938
Remove Oauth Provider Working
trainerbill Jan 28, 2016
268e20d
Fixed signout bug by doing state reload after signout
trainerbill Jan 28, 2016
1080062
Removed debug
trainerbill Jan 28, 2016
e8d5be3
Fixed Server Tests; Removed JWTSign Promise as there is nothing Async…
trainerbill Jan 28, 2016
5ca49d1
Removed all debugging
trainerbill Jan 28, 2016
51aef0b
Client tests fixed
trainerbill Jan 28, 2016
03c2b35
E2E tests fixed
trainerbill Jan 29, 2016
c2c5bec
Frame for authentication service test
trainerbill Jan 29, 2016
61b28bc
Code Review 1
trainerbill Jan 29, 2016
939791b
Removed LocalStorage Angular module in favor of browser API. Fixed T…
trainerbill Jan 30, 2016
aaf2479
Optionize JwtAuth function
trainerbill Feb 1, 2016
b178052
Password Reset working
trainerbill Feb 1, 2016
b937bc7
JWT config update. Fixed and created some tests.
trainerbill Feb 2, 2016
690ebfc
Couple fixes
trainerbill Feb 2, 2016
e4d0bd4
Fixed bad login error message and added server test
trainerbill Feb 2, 2016
b46c6a7
SocketIO/Chat working
trainerbill Feb 3, 2016
fe7b751
Bug fix: password/salt null after profile update
trainerbill Feb 3, 2016
f7d571c
Reordered JWT strategy
trainerbill Feb 3, 2016
c79b65d
Articles Policy fixed
trainerbill Feb 4, 2016
c3bdd39
Redirect home on logout
trainerbill Feb 4, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions config/env/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,12 @@ module.exports = {
fileSize: 1*1024*1024 // Max file size in bytes (1 MB)
}
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwt: { secret: process.env.TOKEN_AUTH_SECRET || 'M3@N_R0CK5!', options: { //Anything From https://www.npmjs.com/package/jsonwebtoken expiresIn: process.env.TOKEN_EXPIRES || '1d' } } Basically not a big deal but gives a formatting warning.

jwt: {
secret: process.env.TOKEN_AUTH_SECRET || 'M3@N_R0CK5!',
options: { //Anything From https://www.npmjs.com/package/jsonwebtoken
expiresIn: process.env.TOKEN_EXPIRES || '1d'
}

}
};
2 changes: 1 addition & 1 deletion config/env/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = {
// directoryPath: process.cwd(),
// fileName: 'access.log',
// rotatingLogs: { // for more info on rotating logs - https://github.com/holidayextras/file-stream-rotator#usage
// active: false, // activate to use rotating logs
// active: false, // activate to use rotating logs
// fileName: 'access-%DATE%.log', // if rotating logs are active, this fileName setting will be used
// frequency: 'daily',
// verbose: false
Expand Down
30 changes: 30 additions & 0 deletions config/lib/jwtAuthentication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can rename this file to authorization; makes more sense to me. If we wanted to support having multiple Authorization methods we could always create a folder named authorization & put individual auth strategies there; then naming this jwt would make sense.


var config = require('../config'),
jwt = require('jsonwebtoken'),
lodash = require('lodash');



// export the token auth service
exports.signToken = function (user, options) {
var payload,
token,
jwtOptions;

if (!user || !user._id) {
return null;
}

options = options || {};

payload = {
user: user._id.toString()
};

jwtOptions = lodash.merge(config.jwt.options, options);

token = jwt.sign(payload, config.jwt.secret, jwtOptions);

return token;
};
3 changes: 2 additions & 1 deletion config/lib/socket.io.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ module.exports = function (app, db) {

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stuff above here talks about sessionSecret sessionId not sure if that's need/was causing my problem.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the above code (everything about Session and cookies and everything worked just fine! Except the tests I guess... so might have to change those

// Use Passport to populate the user details
passport.initialize()(socket.request, {}, function () {
passport.session()(socket.request, {}, function () {
passport.authenticate('jwt', { session: false })(socket.request, {}, function () {
if (socket.request.user) {
next(null, true);
} else {
next(new Error('User is not authenticated'), false);
}
});
});

});
});
});
Expand Down
12 changes: 6 additions & 6 deletions modules/articles/server/policies/articles.server.policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,28 @@ exports.invokeRolesPolicies = function () {
acl.allow([{
roles: ['admin'],
allows: [{
resources: '/api/articles',
resources: '/',
permissions: '*'
}, {
resources: '/api/articles/:articleId',
resources: '/:articleId',
permissions: '*'
}]
}, {
roles: ['user'],
allows: [{
resources: '/api/articles',
resources: '/',
permissions: ['get', 'post']
}, {
resources: '/api/articles/:articleId',
resources: '/:articleId',
permissions: ['get']
}]
}, {
roles: ['guest'],
allows: [{
resources: '/api/articles',
resources: '/',
permissions: ['get']
}, {
resources: '/api/articles/:articleId',
resources: '/:articleId',
permissions: ['get']
}]
}]);
Expand Down
26 changes: 17 additions & 9 deletions modules/articles/server/routes/articles.server.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@
/**
* Module dependencies
*/
var articlesPolicy = require('../policies/articles.server.policy'),
var passport = require('passport'),
express = require('express'),
articlesPolicy = require('../policies/articles.server.policy'),
articles = require('../controllers/articles.server.controller');


module.exports = function (app) {
var router = express.Router();


// Articles collection routes
app.route('/api/articles').all(articlesPolicy.isAllowed)
.get(articles.list)
.post(articles.create);
router.route('/')
.get(articlesPolicy.isAllowed, articles.list)
.post(passport.authenticate('jwt', { session: false }), articlesPolicy.isAllowed, articles.create);

// Single article routes
app.route('/api/articles/:articleId').all(articlesPolicy.isAllowed)
.get(articles.read)
.put(articles.update)
.delete(articles.delete);
router.route('/:articleId')
.get(articlesPolicy.isAllowed, articles.read)
.put(passport.authenticate('jwt', { session: false }), articlesPolicy.isAllowed, articles.update)
.delete(passport.authenticate('jwt', { session: false }), articlesPolicy.isAllowed, articles.delete);

// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
router.param('articleId', articles.articleByID);

app.use('/api/articles', router);
};
27 changes: 17 additions & 10 deletions modules/articles/tests/server/article.server.routes.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('Article CRUD tests', function () {
});

// Save a user to the test db and create new article
user.save(function () {
user.save(function (err, user) {
article = {
title: 'Article Title',
content: 'Article Content'
Expand All @@ -56,6 +56,7 @@ describe('Article CRUD tests', function () {
});

it('should be able to save an article if logged in', function (done) {

agent.post('/api/auth/signin')
.send(credentials)
.expect(200)
Expand All @@ -66,10 +67,12 @@ describe('Article CRUD tests', function () {
}

// Get the userId
var userId = user.id;
var userId = signinRes.body.user._id;


// Save a new article
agent.post('/api/articles')
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleSaveErr, articleSaveRes) {
Expand Down Expand Up @@ -103,7 +106,7 @@ describe('Article CRUD tests', function () {
it('should not be able to save an article if not logged in', function (done) {
agent.post('/api/articles')
.send(article)
.expect(403)
.expect(401)
.end(function (articleSaveErr, articleSaveRes) {
// Call the assertion callback
done(articleSaveErr);
Expand All @@ -124,10 +127,11 @@ describe('Article CRUD tests', function () {
}

// Get the userId
var userId = user.id;
var userId = signinRes.body.user._id;

// Save a new article
agent.post('/api/articles')
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(400)
.end(function (articleSaveErr, articleSaveRes) {
Expand All @@ -151,10 +155,11 @@ describe('Article CRUD tests', function () {
}

// Get the userId
var userId = user.id;
var userId = signinRes.body.user._id;

// Save a new article
agent.post('/api/articles')
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleSaveErr, articleSaveRes) {
Expand All @@ -168,6 +173,7 @@ describe('Article CRUD tests', function () {

// Update an existing article
agent.put('/api/articles/' + articleSaveRes.body._id)
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleUpdateErr, articleUpdateRes) {
Expand Down Expand Up @@ -258,10 +264,11 @@ describe('Article CRUD tests', function () {
}

// Get the userId
var userId = user.id;
var userId = signinRes.body.user._id;

// Save a new article
agent.post('/api/articles')
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleSaveErr, articleSaveRes) {
Expand All @@ -272,6 +279,7 @@ describe('Article CRUD tests', function () {

// Delete an existing article
agent.delete('/api/articles/' + articleSaveRes.body._id)
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleDeleteErr, articleDeleteRes) {
Expand Down Expand Up @@ -301,11 +309,9 @@ describe('Article CRUD tests', function () {
articleObj.save(function () {
// Try deleting article
request(app).delete('/api/articles/' + articleObj._id)
.expect(403)
.expect(401)
.end(function (articleDeleteErr, articleDeleteRes) {
// Set message assertion
(articleDeleteRes.body.message).should.match('User is not authorized');


// Handle article error error
done(articleDeleteErr);
});
Expand Down Expand Up @@ -351,6 +357,7 @@ describe('Article CRUD tests', function () {

// Save a new article
agent.post('/api/articles')
.set('Authorization', 'JWT ' + signinRes.body.token)
.send(article)
.expect(200)
.end(function (articleSaveErr, articleSaveRes) {
Expand Down
45 changes: 24 additions & 21 deletions modules/core/client/app/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,33 @@ angular.module(ApplicationConfiguration.applicationModuleName).config(['$locatio
]);

angular.module(ApplicationConfiguration.applicationModuleName).run(function ($rootScope, $state, Authentication) {
Authentication.ready
.then(function (auth) {
// Check authentication before changing state
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.data && toState.data.roles && toState.data.roles.length > 0) {
var allowed = false;
toState.data.roles.forEach(function (role) {
if ((role === 'guest') || (Authentication.user && Authentication.user.roles !== undefined && Authentication.user.roles.indexOf(role) !== -1)) {
allowed = true;
return true;
}
});

// Check authentication before changing state
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.data && toState.data.roles && toState.data.roles.length > 0) {
var allowed = false;
toState.data.roles.forEach(function (role) {
if ((role === 'guest') || (Authentication.user && Authentication.user.roles !== undefined && Authentication.user.roles.indexOf(role) !== -1)) {
allowed = true;
return true;
if (!allowed) {
event.preventDefault();
if (Authentication.user !== undefined && typeof Authentication.user === 'object') {
$state.go('forbidden');
} else {
$state.go('authentication.signin').then(function () {
storePreviousState(toState, toParams);
});
}
}
}
});
});

if (!allowed) {
event.preventDefault();
if (Authentication.user !== undefined && typeof Authentication.user === 'object') {
$state.go('forbidden');
} else {
$state.go('authentication.signin').then(function () {
storePreviousState(toState, toParams);
});
}
}
}
});

// Record previous state
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
Expand All @@ -45,7 +48,7 @@ angular.module(ApplicationConfiguration.applicationModuleName).run(function ($ro

// Store previous state
function storePreviousState(state, params) {
// only store this state if it shouldn't be ignored
// only store this state if it shouldn't be ignored
if (!state.data || !state.data.ignoreState) {
$state.previous = {
state: state,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
'use strict';

angular.module('core').factory('authInterceptor', ['$q', '$injector', 'Authentication',
function ($q, $injector, Authentication) {
angular.module('core').factory('authInterceptor', ['$q', '$injector',
function ($q, $injector) {
return {
responseError: function(rejection) {
if (!rejection.config.ignoreAuthModule) {
switch (rejection.status) {
case 401:
// Deauthenticate the global user
Authentication.user = null;
var auth = $injector.get('Authentication');
auth.signout();
$injector.get('$state').transitionTo('authentication.signin');
break;
case 403:
Expand Down
2 changes: 1 addition & 1 deletion modules/core/client/services/socket.io.client.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ angular.module('core').service('Socket', ['Authentication', '$state', '$timeout'
this.connect = function () {
// Connect only when authenticated
if (Authentication.user) {
this.socket = io();
this.socket = io('', { query: 'auth_token=' + Authentication.token });
}
};
this.connect();
Expand Down
2 changes: 1 addition & 1 deletion modules/core/client/views/header.client.view.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
</li>
<li class="divider"></li>
<li>
<a href="/api/auth/signout" target="_self">Signout</a>
<a href="#" ng-click="authentication.signout()">Signout</a>
</li>
</ul>
</li>
Expand Down
7 changes: 1 addition & 6 deletions modules/core/server/views/layout.server.view.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,7 @@
{% block content %}{% endblock %}
</section>
</section>

<!--Embedding The User Object-->
<script type="text/javascript">
var user = {{ user | json | safe }};
</script>


<!--Load The Socket.io File-->
<script type="text/javascript" src="/socket.io/socket.io.js"></script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$stat

$http.post('/api/auth/signup', $scope.credentials).success(function (response) {
// If successful we assign the response to the global user model
$scope.authentication.user = response;
//$scope.authentication.user = response;
Authentication.login(response.user, response.token);

// And redirect to the previous or home page
$state.go($state.previous.state.name || 'home', $state.previous.params);
Expand All @@ -44,7 +45,8 @@ angular.module('users').controller('AuthenticationController', ['$scope', '$stat

$http.post('/api/auth/signin', $scope.credentials).success(function (response) {
// If successful we assign the response to the global user model
$scope.authentication.user = response;
//$scope.authentication.user = response;
Authentication.login(response.user, response.token);

// And redirect to the previous or home page
$state.go($state.previous.state.name || 'home', $state.previous.params);
Expand Down
Loading