I am trying to implement a persistent login for an angular application. By persistent I mean I am able to redirect to a new page or refresh without being logged out.
Looking at the debugger it does not appear that my /api/users route is ever being hit. api/sessions is and im not sure why the other one wouldn't be.
my code is:
routes.js
app.post('/api/sessions', function(req, res, next) {
User.findOne({username: req.body.username})
.select('password').select('username')
.exec( function(err, user){
if (err) {return next(err)}
if (!user) {return res.send(401)}
bcrypt.compare(req.body.password, user.password, function (err, valid){
if (err) {return next(err)}
if (!valid) {return res.send(401)}
var token = jwt.encode({username: user.username}, config.secret)
res.send(token)
})
})
})
app.get('/api/users', function(req, res, next) {
if(!req.headers['x-auth']){
return res.send(401)
}
var auth = jwt.decode(req.headers['x-auth'], config.secret)
User.findOne({username: auth.username}, function (err,user){
if (err) {return next(err)}
res.json(user)
})
})
app.post('/api/users', function(req, res, next) {
var user = new User({username: req.body.username})
bcrypt.hash(req.body.password, 10, function (err, hash){
if (err) {return next (err)}
user.password = hash
user.save(function (err){
res.send(201)
})
})
})
angular.js
app.service('UserSvc', function($http, $window){
var svc = this;
svc.getUser = function() {
return $http.get('/api/users',{
headers: { 'X-Auth': this.token }
})
}
svc.login = function(username, password){
return $http.post('/api/sessions', {
username: username, password: password
}).then(function(val){
svc.token = val.data
// window.localStorage.token = val.data
return svc.getUser()
})
}
svc.logout = function() {
$http.post('/api/sessions', {
username: null, password: null
}).then(function(val){
svc.token = null
// window.localStorage.token = val.data
})
}
})
app.controller('LoginCtrl', function($scope, $location, UserSvc){
$scope.login = function(username, password) {
UserSvc.login(username, password)
.then(function(response) {
$scope.$emit('login', response.data)
$location.path('/dashboard');
})
}
$scope.logout = function() {
UserSvc.logout();
$scope.$emit('logout')
}
});
app.controller('ApplicationCtrl', function($scope, UserSvc) {
angular.element(document).ready(function () {
$scope.currentUser = UserSvc.getUser();
})
$scope.modalShown = true;
$scope.$on('login', function (_, user){
$scope.currentUser = user;
})
$scope.$on('logout', function (){
$scope.currentUser = null;
})
});
if anyone has any pointers please let me know! I have spent way to much time on this :(
I believe the problem is rather simple here.
Services need to be instantiated (with new UserSvc).
Factories do not. So if you wanted it to use it the way you are, change UserSvc to be a factory and have it return svc.
Other thing to note would be that Factories/Services are singletons, so returning the svc, along with a variable holding the resultant user object will persist through angular router traversal but not on a page refresh. For that you would want to store the user in sessionStorage on the client (or in localStorage with some sort of timeout).
Related
I am creating token at login time with node.js:
apiRoutes.put('/login', function(req, res, next){
User.findOne({email:req.body.email}, function(err, user){
bcrypt.compare(req.body.password, user.password, function(err, result){
if(result){
var token=jwt.encode(user,config.secret);
return res.json({success: true, token:'JWT' +token});
}else{
return res.json("Incorrect Email and Password")
}
})
})
});
now I am trying to show user dashboard page with /dashboard route and I am doing something like below:
apiRoutes.get('/dashboard',function(req, res) {
var token=getToken(req.headers);
if(token){
var decode=jwt.decode(token, config.secret);
console.log(decode);
User.findOne({name:decode.name}, function(err, user){
if(err){res.json(err)}
if(!user){
return res.status(403).send({success:false, msg:'Authentication Failed'})
}else{
res.json({success:true, msg:'Welcome in the Area ' +user.name+'!' })
}
})
}else{
return res.status(403).send({success:false, msg:'No Token Found'})
}
});
getToken = function (head) {
if (head && head.authorization) {
var parted = head.authorization.split(' ');
if (parted.length == 2) {
return parted[1];
} else {
return null;
}
} else {
return null;
}
};
In postman when I hit /dashboard api its working good. and printing the output success:true, msg:'Welcome in the Area Admin;
But when in angular js I am consuming this api then output in node console is null.
Below is my angular function to consume api
app.controller('dashboardCtrl', function($scope, $http, $location, $routeParams){
$http.get('/api/dashboard').success(function(res){
$scope.result=res;
})
})
I want to know how to consume token based route in angular. I know above given angular function is not right. Please let me know the right code.
Thanks
You didn't set the header for the $http.get(). Here is how you should do:
$http.get('/api/dashboard', {
headers: {
// Set header for the request here
authorization: token
}
})
.success(function(res) {
// Success
});
I'm trying to make an isAdmin() function that will check if the current user has "isAdmin: true" in the mongodb.
server.js
app.get('/api/isadmin', function (req, res) {
User.findById(req.user, function (err, user) {
if (req.user.isAdmin == true) {
res.send(user);
} else {
return res.status(400).send({ message: 'User is not Admin' });
}
});
});
AdminCtrl.js
angular.module('App')
.controller('AdminCtrl', function ($scope, $http, $auth) {
$http.get('http://localhost:3000/api/isadmin')
.then(function (res) {
$scope.isAdmin = res.data;
}
});
when I access the page /admin it throws "cannot read property 'isAdmin' of null" in the if inside app.get. Why is this occuring, and what is the optimal way for me to make this isAdmin function?
You are not using the variable sent back to you, but still the req.
app.get('/api/isadmin', function (req, res) {
User.findById(req.user, function (err, user) {
if (user.isAdmin == true) {
res.send(user);
} else {
return res.status(400).send({ message: 'User is not Admin' });
}
});
});
Maybe this should work (assuming you are returned if the user isAdmin)
try following
angular.module('App')
.controller('AdminCtrl',['$scope','$http','$auth', function ($scope, $http, $auth) {
$http.get('http://localhost:3000/api/isadmin')
.then(function (res) {
$scope.isAdmin = res.data;
}
}]);
I am trying to implement a persistent login for an angular application. By persistent I mean I am able to redirect to a new page or refresh without being logged out. This is kind of a two part question.
1) I am pretty sure my login is working but my logout button gives me a POST http://localhost:3000/api/sessions 500 (Internal Server Error)
2) I am now having trouble keeping the username showing in the top right corner of my app after a refresh or after a redirect for third party authentication. (I should also mention I am doing this with the getUser() and get a GET http://localhost:3000/api/users 401 (Unauthorized) back
my code is:
html
<section class="top-bar-section" ng-controller="LoginCtrl">
<ul class="right">
<li>
<a ui-sref="dashboard">Dashboard</a>
</li>
<li class="pink">
<a ui-sref="post-item">Post New Item!</a>
</li>
<li>
<a ng-click='logout()'>Logout</a>
</li>
<li class="has-dropdown">
<span ng-if="currentUser">{{ currentUser.username }}</span>
<ul class="dropdown">
<li>Settings</li>
<li><a ng-click="logout()">Logout</a></li>
</ul>
</li>
</ul>
</section>
routes.js
app.post('/api/sessions', function(req, res, next) {
User.findOne({username: req.body.username})
.select('password').select('username')
.exec( function(err, user){
if (err) {return next(err)}
if (!user) {return res.send(401)}
bcrypt.compare(req.body.password, user.password, function (err, valid){
if (err) {return next(err)}
if (!valid) {return res.send(401)}
var token = jwt.encode({username: user.username}, config.secret)
res.send(token)
})
})
})
app.get('/api/users', function(req, res, next) {
if(!req.headers['x-auth']){
return res.send(401)
}
var auth = jwt.decode(req.headers['x-auth'], config.secret)
User.findOne({username: auth.username}, function (err,user){
if (err) {return next(err)}
res.json(user)
})
})
app.post('/api/users', function(req, res, next) {
var user = new User({username: req.body.username})
bcrypt.hash(req.body.password, 10, function (err, hash){
if (err) {return next (err)}
user.password = hash
user.save(function (err){
res.send(201)
})
})
})
angular.js
app.service('UserSvc', function($http, $window){
var svc = this;
svc.getUser = function() {
return $http.get('/api/users',{
headers: { 'X-Auth': this.token }
})
}
svc.login = function(username, password){
return $http.post('/api/sessions', {
username: username, password: password
}).then(function(val){
svc.token = val.data
// window.localStorage.token = val.data
return svc.getUser()
})
}
svc.logout = function() {
$http.post('/api/sessions', {
username: null, password: null
}).then(function(val){
svc.token = null
// window.localStorage.token = val.data
})
}
})
app.controller('LoginCtrl', function($scope, $location, UserSvc){
$scope.login = function(username, password) {
UserSvc.login(username, password)
.then(function(response) {
$scope.$emit('login', response.data)
$location.path('/dashboard');
})
}
$scope.logout = function() {
UserSvc.logout();
$scope.$emit('logout')
}
});
app.controller('ApplicationCtrl', function($scope, UserSvc) {
angular.element(document).ready(function () {
$scope.currentUser = UserSvc.getUser();
})
$scope.modalShown = true;
$scope.$on('login', function (_, user){
$scope.currentUser = user;
})
$scope.$on('logout', function (){
$scope.currentUser = null;
})
});
if anyone has any pointers please let me know! I have spent way to much time on this :(
To make client side persistence I'd save the token in local storage using the $cookieStore service in angular.
Its a key-value storage.
To store the token in storage use:
$cookieStore.put('token', data.token)
and to retrieve it back use:
$cookieStore.get('token')
Hope that helped
1) When you logout, your /api/sessions endpoint is looking for a user with the username of null in your database, with something a query of User.findOne({username: null})
2) getUser() is setting headers with 'X-Auth' (note the capitals) but you're checking for lowercase at your users endpoint with !req.headers['x-auth']
#XxscurvyxX: You can also use $window.sessionStorage which works on the key/value pair basis and gives getter and setters.
Syntax:
$window.sessionStorage.setItem(key, value) // key: String value: String
$window.sessionStorage.getItem(key) // key: String
The key/value pairs stores in the session Storage until you close the tab or close the browser.
I am using mongoose to create a user object on register. This works fine and any errors are returned as expected.
However, I want to log the user on right after they register (so registering logs you on if there are no errors).
I have the following for the register.
register_controller:
$scope.submitRegister = function() {
AuthenticationService.register(this.details).success(function() {
$log.debug('/POST to /api/register worked');
});
}
services.js:
.service('AuthenticationService', function($http, $timeout, $q, $session, $flash) {
...
this.register = function(details) {
var register = $http.post('/api/register', details);
register.success(function() {
console.log("User added fine");
}).error(function() {
console.log("error!!!");
});
return register;
};
...
users.js:
app.post('/api/register', authentication.register);
passport's authenticate.js:
module.exports = {
...
register: function(req, res){
var User = require('./controllers/api/login_api');
User.create({name: req.body.name, email: req.body.email, password: req.body.password}, function(err){
if (err) {
console.log(err);
return;
}
console.log("User added");
return res.send(200);
});
},
...
The error is reported back fine, no troubles, but I would have thought it would report something else back (like the created object?) which I could use down the line so my register_controller can have in the success function(object) {... login(object);...}.
Is this a limitation in the .create method or am I missing something obvious?
Thank you.
Two things to change in your server code:
passport's authenticate.js:
module.exports = {
...
register: function(req, res){
var User = require('./controllers/api/login_api');
User.create({name: req.body.name, email: req.body.email, password: req.body.password}, function(err, user){
if (err) {
console.log(err);
return;
}
console.log("User added");
return res.send(200, user);
});
},
...
I've added user to your callback Mongoose model.create api - return the created object to the CB
And to change the catch in your client:
.service('AuthenticationService', function($http, $timeout, $q, $session, $flash) {
...
this.register = function(details) {
var register = $http.post('/api/register', details);
register.success(function(user) {
console.log(user);
}).error(function() {
console.log("error!!!");
});
return register;
};
...
Now you can do with the created user object whatever you need
Hi in the following Angular controller i try to initiate facebook login with Parse.com.
So I created a promise triggered on fbLogIn. What it is supposed to do, is first login to facebook, and grab first_name and store it in fieldValuesService.ff.
THEN, it is supposed to access this value and do something with it. For illustration purpose I just used console logs.
What happens is that the second console.log in second then is triggered before the first one from first .then thus is undefined.
I don't understand why anything in the second .then can be triggered before first one in this situation.
Also second problem, after a logout, the fbLogIn function is sometime inactive: it won't trigger the login process again.
If you have a clue on this issue your help will be greatly appreciated.
.controller('logController',
function ($scope, $q, fieldValuesService) {
var defer = $q.defer();
defer.promise
.then(function() {
Parse.FacebookUtils.logIn(null, {
success: function(user) {
if (!user.existed()) {
alert("User signed up and logged in through Facebook!");
} else {
$scope.currentUser = user;
$scope.$apply();
FB.api('/me', function(response) {
fieldValuesService.ff = response.first_name;
console.log(fieldValuesService.ff); //logs bob
});
}
},
error: function(user, error) {
alert("User cancelled the Facebook login or did not fully authorize.");
}
});
})
.then(function(){
console.log(fieldValuesService.ff); //logs undefined
});
$scope.fbLogIn = function() {
defer.resolve();
};
// Parse log out
$scope.logOut = function(form) {
Parse.User.logOut();
$scope.currentUser = null;
};
});
Maybe if you restructure your code, things will become a little bit easier.
I recommend to refactor everything FB related into its own service like:
module.factory('FBService', function ($q) {
var login,
logout,
getInformation;
login = function () {
var defered = $q.defer();
Parse.FacebookUtils.logIn(null, {
success: function (user) {
defered.resolve(user);
},
error: function (user, error) {
defered.reject(user, error);
}
});
return defered.promise;
};
logout = function () {
var defered = $q.defer();
Parse.User.logOut();
defered.resolve();
return defered.promise;
};
getInformation = function () {
var defered = $q.defer();
FB.api('/me', function (response) {
defered.resolve(response);
});
return defered.promise;
}
return {
login: login,
logout: logout,
getInformation: getInformation
};
});
module.controller("LoginCtrl", function ($scope, FBService, fieldValuesService) {
$scope.fbLogIn = function () {
FBService.login().then(function (user) {
$scope.currentUser = user;
return FBService.getInformation();
}).then(function (information) {
fieldValuesService.ff = information.first_name;
console.log(fieldValuesService.ff);
});
};
$scope.logOut = function () {
FBService.logout().then(function () {
$scope.currentUser = null;
});
};
});