Right now I am building an AngularJS based application on top of Ruby on Rails and using Devise for authentication. I have the server responding properly when a user authenticates successfully and when authentication fails. I guess my question is, using $cookieStore, what's the best practice for knowing if a user is logged in or not? There is a cookie that gets set by Rails called "myapp_session", but that session doesn't necessarily mean a user is logged in. Looking for ideas on how to use AngularJS to keep user online/offline management. I'll still be ensuring that requests that require authorization get authorized by the backend regardless of the solution.
3 Answers
You can create an directive that set up the logged user when the application loads, for example, requesting the current user session on your server.
angular.module('Auth', [
'ngCookies'
])
.factory('Auth', ['$cookieStore', function ($cookieStore) {
var _user = {};
return {
user : _user,
set: function (_user) {
// you can retrive a user setted from another page, like login sucessful page.
existing_cookie_user = $cookieStore.get('current.user');
_user = _user || existing_cookie_user;
$cookieStore.put('current.user', _user);
},
remove: function () {
$cookieStore.remove('current.user', _user);
}
};
}])
;
And set in your run method in AppController:
.run(['Auth', 'UserRestService', function run(Auth, UserRestService) {
var _user = UserRestService.requestCurrentUser();
Auth.set(_user);
}])
Of course if any request to the server return an Http Status 401 - Unauthorized, you need to call the Auth.remove() service to remove the user from cookie and redirect the user to login page.
I use this approach and works very well. You can also use the localStorage, but the user data will be persisted for a long time. Unless you set an expiration date for this authentication, I don't see as best practice.
Keep in mind to always verify the user credentials on your server site =)
[EDIT]
To listen to 401 - Unauthorized server response, you can put an interceptor on your $http request, like this:
.config(['$urlRouterProvider', '$routeProvider', '$locationProvider', '$httpProvider', function ($urlRouterProvider, $routeProvider, $locationProvider, $httpProvider) {
$urlRouterProvider.otherwise('/home');
var interceptor = ['$location', '$q', function ($location, $q) {
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(interceptor);
}])
Every call with 401 response, the user will be redirected to login page at /login path.
You will find a good example here
- 10,034
- 13
- 66
- 80
-
1This is a fantastic response. Thanks for the insight. It's been useful. – francisco.preller Oct 17 '13 at 04:29
-
1I get an error about `Unknown provider: $urlRouterProvider` with this. – Sarah Vessels Oct 25 '13 at 20:48
-
Are you using angular v1.2? – Deividi Cavarzan Oct 25 '13 at 21:01
-
Sorry to Ask... in your case, what is the UserRestService? – Cheluis Dec 05 '13 at 02:25
-
Its the angular $resource that loads the current user in server session via REST. I'm using the session management at the server side to avoid trust just in the client cookie. – Deividi Cavarzan Dec 05 '13 at 03:05
-
Very nice, so you recommend that every time AngularJS request data or sends data to a web method, on the server we should check the user identify maybe by calling database or other mechanism? Thanks – VAAA Mar 08 '14 at 01:32
-
Yes, it's the best approach to work with secured data. I usually set these keys (like token and user email) in the request headers, so the server do the logic to verify the authenticity. – Deividi Cavarzan Mar 08 '14 at 01:56
You can set the cookie on your login callback with
$cookieStore.put('logged-in', some_value)
Then check for it when they enter your site with
.run(function($cookieStore) {
if ($cookieStore.get('logged-in') === some_value) {
let him enter
}
else {
you shall not pass
}
});
There might be more "correct" ways, but this works.
- 8,057
- 4
- 29
- 37
If you're having problems making the accepted answer work, be wary, that as of Angular 1.3, a proper way of adding a new interceptor is by adding it to $httpProvider.interceptors, so instead of:
$httpProvider.responseInterceptors.push(interceptor);
use:
$httpProvider.interceptors.push(interceptor);
- 5,615
- 4
- 40
- 55