Handling session/cookie with Sails.JS and AngularJS - angularjs

I'm doing a simple SPA where I am using Sails.JS for a REST API and AngularJS for my frontend.
I'm currently having some struggles with figuring out how I should handle the sessions when combining these two.
Feel free to give me some pointers if I'm going about this the wrong way.
--
Here is part of my login function. When a successfull login happens I return the user object along with a session to my client.
User.js
if(user) {
bcrypt.compare(userObj.password, user.encryptedPassword, function(err, match) {
if(err) {
res.json({rspMessage: 'Server error'}, 500);
}
if(match) {
req.session.user = user;
res.json(req.session.user); // return user data and session.
/* This returns something like this
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
user: {
username: 'admin',
id: '549f2ad213c64d3b2f3b9777'}
}
*/
}
});
}
loginService
Here is my loginService which doesn't really do much right now. I figured this is the place to keep track of the session. I'm just not sure how to go about this... There aren't many tutorials about combining Sails + AngularJS.
MyApp.factory('loginService', ['$cookieStore', '$http', '$rootScope', function($cookieStore, $http, $rootScope){
var _user = {};
return {
login: function(credentials) {
return $http.post('/user/login', credentials)
.then(function(result) {
return result.data;
});
}
}
}])
I want to check the session against my backend somehow and see if its valid or if it has expired. If the session is still valid, the user will be kept logged in even if the user closes his browser/refresh.
Suggestions, links.. anything helpful is appreciated.

Here's some tips I can give you :
Since Sails v0.10, you can use custom responses (doc page) which is a better practice than using
res.status(...);
res.json(...);
The session cookie you are creating with Sails is saved server-side. Maybe you can create a url (e.g. GET /me) to know if this session is still valid. Your Angular app would make a request to this url each time the page is loaded (in a run block I would suggest) to know if the user is still logged in server-side.
Do not hesitate if you need more precision.

Related

Angular using cookies

I'm currently developing a Web Application with AngularJS and I have the following question with the login system: is secure to store the information like username you used to login into the cookies?
I mean, when i login I want to store the username or the id, would be the same, into a cookie, so I can load more information from the database each time the user navigates between the different links. The thing is that I don't know if Angular has any protection about cookie edition or, in other words, should I trust this cookie's value will be the correct one?
For example, having this piece of code, is it secure?
var request = {
url: 'backend/getCharacters.php?user=' + $cookies.get('username'),
method: 'GET'
};
$http(request).then(function(response) {
if(response.status == 200)
// Get character list
else
alert('Something went wrong.');
});
You should declare ngCookies in your function. You can see following example to understand more about use cookies in angularjs. I hope you would like these resources.
Use this code. I hope it will be work for you.
angular.module('myApp', ['ngCookies']);
function CookieCtrl($scope, $cookieStore) {
var tabname = [{'KaluSingh'}]; // tabname contains username
$cookieStore.put('username', tabName);
};
var request = {
url: 'backend/getCharacters.php?user=' + $cookies.get('username'),
method: 'GET'
};
$http(request).then(function(response) {
if(response.status == 200)
// Get character list
else
alert('Something went wrong.');
});
stackoverflow.com/questions/10961963/how-to-access-cookies-in-angularjs
https://docs.angularjs.org/api/ngCookies/service/$cookies
https://docs.angularjs.org/api/ngCookies

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Ionic/Laravel App Client Side Auth Management

I have been fumbling around with different implementations and ideas to get this to work, but I feel like I am not doing this as DRY or smart as I could be. I've been following this "tutorial" Angular Auth
So, I have a fully functional laravel (4.2) back end set up with some resource routes protected by the oauth filter. I am using the password grant and everything is working just fine there. I've got log in/out routes also set up and am able to sign in to my Ionic app and obtain and access_token and refresh_token from laravel just fine. Obtaining new access_tokens using the refesh_token works just fine as well. BUT, I am having some issues trying to figure out how to correctly handle the following things in Ionic:
Make sure the access_token hasn't expired before the user hits an Ionic state which will consume a resource from my back end.
Handle the case where the user's access_token & refresh token have both expired requiring them to log back in to the laravel back end in order to obtain a new pair of access & refresh tokens. I only have the user "log in" when they need to obtain a new access_token & refresh token (or they are first registering) as this route, oauth/access_token, requires the params {username, password}.
What I Tried
In the article I mentioned earlier, he sets up a rootScope watcher in the run module which watches for the statechangestart event like so.
$rootScope.$on('$stateChangeStart', function (event, next) {
var authorizedRoles = next.data.authorizedRoles;
if (!AuthService.isAuthorized(authorizedRoles)) {
event.preventDefault();
if (AuthService.isAuthenticated()) {
// user is not allowed
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
} else {
// user is not logged in
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
}
});
I am not using roles so when I implemented this I just had something like this
$rootScope.$on('$stateChangeStart', function(event, next) {
if (next.url != "/login") {
AuthService.isAuthenticated().then(function() {
console.log('you are already authed an logged in and trying to access: ' + next.url);
}, function() {
event.preventDefault();
console.log('YOU DO NOT HAVE A VALID ACCESS TOKEN');
$location.path('/app/login');
});
}
});
isAuthenticated() just hits a route inside my oauth filter so if it throws back an error (401 for example), I know that the access_token is bad. I then have a private method also inside my AuthService service that tries to get a new access_token using the users stored refresh_token
function useRefreshToken() {
console.log('Using refresh token to get new token:');
var deferred = $q.defer();
$http({
method: 'POST',
url: base_url.dev.url + 'oauth/access_token',
data: $.param({
grant_type: 'refresh_token',
client_id: API.client_id,
client_secret: API.client_secret,
refresh_token: $localStorage.session.refresh_token
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).success(function(data) {
console.log('refresh token worked!');
$localStorage.session.access_token = data.access_token;
$localStorage.session.refresh_token = data.refresh_token;
deferred.resolve();
}).error(function(error) {
console.log('refresh token failed');
CurrentUserService.setLogged(false);
console.log(JSON.stringify(error));
deferred.reject(error);
});
return deferred.promise;
};
If the above method returns back a rejected promise I just assume (which may be a good idea or not??) that the refresh token has expired and thus the user needs to log back in and retrieve a new access & refresh token pair from my laravel oauth/access_token route.
So the above methods have been working fine on their own, in that I am able to check if the users access_token is valid and if not retrieve a new access_token just fine using the users refresh_token.
Here's my isAuthenticated method in case you wanted to see that as well. It's a public method inside of my AuthService service.
isAuthenticated: function() {
console.log('Checking if token is still valid.');
var deferred = $q.defer();
$http.get(base_url.dev.url + 'valid-token', {
params: {
access_token: $localStorage.session.access_token
}
}).success(function(data) {
console.log('Token is still valid.');
CurrentUserService.setLogged(true);
deferred.resolve();
}).error(function(error) {
console.log(JSON.stringify(error));
useRefreshToken().then(function() {
deferred.resolve();
}, function(error) {
deferred.reject(error);
});
});
return deferred.promise;
}
The big problem I was running into is that because the AuthService.isAuthenticated() method runs async, the state the app was changing to, say PHOTOS, would be hit before isAuthenticated returns and if we have Case: 1 mentioned at the beginning of my post, the PHOTOS state will try to use an invalid access_token to try and consume a resource on my back end BEFORE the isAuthenticated method is able to get a new access_token using the refresh_token.
Now I was able to avoid the above issue by using a resolve on EVERY state which handled using the isAuthenticated method to check the access_token and get a new one if need be BEFORE consuming a resource. BUT that felt horribly unDRY. I apologize for the length of this post but I wanted to make sure you guys knew everything that was going on and what I was trying to accomplish.
I appreciate any feedback, criticism and instruction! Thanks guys.

Get email ID/username after login using Google+ API

I want to access the user_name/email_id of the user who logs onto my website using Google+ API. So far I have implemented the Google+ API and the return value is:
User Logged In This is his auth tokenya29.AHES6ZRWhuwSAFjsK9jYQ2ZA73jw9Yy_O2zKjmzxXOI8tT6Y
How can I use this to get the username/email id?
Specifically for retrieving an email address of an authenticated user, keep in mind that you will need to include the userinfo.email scope and make a call to the tokeninfo endpoint. For more information on this, see https://developers.google.com/+/api/oauth#scopes.
if you are correctly logged in, it's enough to call the Google+ api at this URL:
GET https://www.googleapis.com/plus/v1/people/me
where the userId has the special value me, to get all the information about the logged user. For more information see:
https://developers.google.com/+/api/latest/people/get
I'm adding a code sample to help others.
In this case the login operation is performed against Google requesting email, as well as user profile information like name,.... Once all this information is retrieved, a request to my own login service is performed:
function OnGoogle_Login(authResult) {
if (authResult['access_token']) {
gapi.client.load('oauth2', 'v2', function()
{
gapi.client.oauth2.userinfo.get().execute(function(userData)
{
$("#frmLoginGoogle input[name='id']").val(userData.id);
$("#frmLoginGoogle input[name='name']").val(userData.name);
$("#frmLoginGoogle input[name='email']").val(userData.email);
$.ajaxSetup({cache: false});
$("#frmLoginGoogle").submit();
});
});
}
}
$(document).ready(function() {
/** GOOGLE API INITIALIZATION **/
$.ajaxSetup({cache: true});
$.getScript("https://apis.google.com/js/client:platform.js", function() {
$('#btnLoginGoogle').removeAttr('disabled');
});
$("#btnLoginGoogle").click(function() {
gapi.auth.signIn({
'callback': OnGoogle_Login,
'approvalprompt': 'force',
'clientid': 'XXXXX.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
'requestvisibleactions': '',
'cookiepolicy': 'single_host_origin'
});
});
});

How do you load data asynchronously when loading an angular module?

In my angular app I need to read some data from the server before I allow the rest of my application to run. For example, I need the user authenticated so I can verify their permission before I allow them access to anything. Since I need to do this on app start and I don't know what controller will be loaded first, I can't use the resolve feature of the $routeProvider.when method because I do not know which controller will be hit first, and it only occurs once on app start.
I keep steering towards doing something in the module.run method, but I can't find a way to prevent it from continuing until get the data back from the server. I found this but it suffers from the same problem: AngularJS : Initialize service with asynchronous data.
I can also do manual bootstrapping, but I can't find any good examples of how to do this. Any help you can provide would be appreciated.
The easiest way i can think of here would be to have a separate page for login. Once the user is authenticated then only allow him access to the main app (angular app).
Also you need to secure you service resources against unauthorized request. This would safeguard against your controller \ views loading unauthorized data.
Like the other answer already mentioned, you should take care of the security of your app.
Possible solutions are:
Check if the user is authenticated
Implement a authentication in your API. You can check if the user is logged in with a responseInterceptor:
.config(['$routeProvider','$httpProvider', function($routeProvider,$httpProvider) {
//setup your routes here...
var authChecker = ['$location', '$q', function($location, $q) {
//redirects the user to /login page if he's not authorized (401)
function success(response) {
return response;
}
function error(response) {
if(response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(authChecker);
}])
See $http docs for more details.
Workaround: run scope
Does not solve the security issues, but is working, too:
Get your data in the run block like this:
.run(function($http,$location,$rootScope) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if($rootScope.gotData) {
$location.path(next.path);
} else {
$location.path('/loading');
}
});
$http.get('path/to/data').success(function() {
$rootScope.gotData = true;
$location.path('/home');
}).error(function() {
$location.path('/404');
})
})
see docs for further information.

Resources