Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

session doesn't get updated automatically #1

Closed
giosakti opened this issue Mar 19, 2013 · 14 comments
Closed

session doesn't get updated automatically #1

giosakti opened this issue Mar 19, 2013 · 14 comments

Comments

@giosakti
Copy link

Hi, I'm currently exploring the possibility of integrating devise as an authentication backend with angular as its frontend, and i found your code, which has put me into the right path :)

however I found some problems with your "Session" service here:

angular.module('angularDevise.services').service('Session', function($cookieStore, UserSession, UserRegistration) {

  this.currentUser = $cookieStore.get('_angular_devise_user');
  this.signedIn = !!$cookieStore.get('_angular_devise_user');
  this.signedOut = !this.signedIn;
  this.userSession = new UserSession( { email:"foo@bar.com", password:"example", remember_me:true } );
  this.userRegistration = new UserRegistration( { email:"foo-" + Math.floor((Math.random()*10000)+1) + "@bar.com", password:"example", password_confirmation:"example" } );

});

during my experiment, I don't think that those three variable (currentUser, signedIn and signedOut) will get automatically updated after this code at the session controller:

      .success(function(data, status, headers, config) {
        $cookieStore.put('_angular_devise_user', data);
      });

Therefore to make the login process seamless, I have to do some $broadcast and $on to catch the changes and change the variable manually so that the view will update accordingly.

Is this an expected behavior or am I missing something here ?

@karlfreeman
Copy link
Owner

@giosakti Your bang on. This repo is still a work in progress. The missing piece of the puzzle is the session integration. I'm still working on a solution but for the most part I'm a little unsure what the best route to go down is as there are quite a few ways to do it.

I'll keep you posted, you should if your looking in to Angular with Rails make sure to be aware of how it compresses ( I've updated the repo with the required changes )

@giosakti
Copy link
Author

Ah okay then, I want to share some of my gists regarding authentication after taking your codes further:

First, now my session service looks like this:

https://gist.github.com/giosakti/5251023

Notice that I now use sessionStorage, not cookie. It's an interesting concept in HTML5 that you may want to check: http://www.w3schools.com/html/html5_webstorage.asp

However it only lives within a tab or window in a browser, so if you open a new tab the session data won't last, but it's perfect for my needs now.

I also create an interceptor for handling login and logout:

https://gist.github.com/giosakti/5251029

Now I can just do something like this in my session controller:

app.controller "SessionsController", ($scope, Session) ->
  $scope.session = Session.userSession

  $scope.create = ->
    if Session.signedOut
      $scope.session.$save().then ((response) ->
        $scope.$emit('event:loginConfirmed', response.data)
      ), (error) ->

  $scope.destroy = ->
    $scope.session.$destroy()
    $scope.$emit('event:logoutConfirmed')

I still found some glitches regarding this implementation, such as handling session timeout, etc. I want to try researching further on this matter.

@karlfreeman
Copy link
Owner

@giosakti That session service / interceptor of yours look really great! Initially I looked into sessionStorage but never as much as your gists. Whilst doing so I started to think that storing session information in the sessionStorage alone isn't quite right. Hear me out.

We ideally want two pieces of information available to Angular, the current_user and their session.

Storing both these sets of information in the sessionStorage means that ( the user is never 'remembered' correctly):

  • The user logs in
  • From a successful login we populate the current_user
  • We store the returned session information into sessionStorage
  • The user idles, returns within the same tab
  • We still have current_user
  • The user leaves, returns in a new tab
  • We have no idea who the current_user is anymore
  • The session expires
  • We have no idea who the current_user is anymore nor do we know about the expired session information

Storing both these sets of information in the localStorage ( the user is never 'forgotten' correctly ):

  • The user logs in
  • From a successful login we populate the current_user
  • We store the returned session information into localStorage
  • The user idles, returns within the same tab
  • We still have the current_user
  • The user leaves, returns in a new tab
  • We still have the current_user
  • The session expires
  • We still have the current_user, but now with expired session information

I think a combination of the two might be the best possible solution. We want the session to to be persistant across contexts / tabs and when each context / tab requests the current_user the validity of the session can be determined server side.

Storing in a mix of both sessionStorage and localStorage:

  • The user logs in
  • From a successful login we populate the current_user in sessionStorage and the session information in localStorage
  • The user idles, returns within the same tab
  • We still have the current_user in sessionStorage
  • The user leaves, returns in a new tab
  • We don't have a current_user but we do have session information in localStorage so we re-request the current_user
  • From a successful request we re-populate the current_user
  • The session expires
  • We still have the current_user
  • The user leaves, returns in a new tab
  • We don't have a current_user but we do have expired session information in localStorage so we re-request the current_user.
  • The session has expired so the request fails so we clean up localStorage and ask the user to log back in

Thoughts?

@karlfreeman
Copy link
Owner

Also I'd preface this all with a I'm not sure there is a 'definitive' way of handling sessions in JS alone. Eg the 'session' in some instances is in fact an oauth2 bearer_token or a temporary api_key or [insert other way of individually identifying a user].

Would love to hear how others are attempting this, so will try and reach out.

@giosakti
Copy link
Author

@karlfreeman that was some interesting illustrations you provide there.

It's true that I still haven't figured out the best way in handling session timeout. So regarding the usage of both session storage and local storage, I taken this from wiki:

"Session storage is per-page-per-window and is limited to the lifetime of the window. Session storage is intended to allow separate instances of the same web application to run in different windows without interfering with each other, a use case that's not well supported by cookies."

So by that definition sessionStorage has their own use-case, which is to provide multiple session within the same browser. However there is also other case, such as ours where we want the application to remember user even though they have closed the tab/window, thus I now believe that sessionStorage (maybe) doesn't have the capability to support this.

Now taking the above into account, I agree with you that maybe the combination of both is perhaps the right way to go. This combination can provide user with the ability to login with different cred simultaneously and the browser will still remember even after they have closed the tab/window. But we may need to make some adjustment on the server-side first before this can work out (for example the session expiring system, handling login with the same account simultaneously, etc)

(Unfortunately when I check google implementation of multiple login using firebug, the sessionStorage and localStorage was still empty, meaning that they still use cookies for this purpose. Perhaps because they have the obligation to maximize compability)

@jesalg
Copy link

jesalg commented May 2, 2013

What do you guys think of the approach taken in this repo: https://github.com/colindensem/demo-rails-angularjs - It's using token_auth wrapping. Do you see any downsides to it?

@giosakti
Copy link
Author

giosakti commented May 8, 2013

Hi @jesalg, token_auth_wrapping is a sound concept, I know about it from the same reference that your demo link also refer to (http://nils-blum-oeste.net/).

Actually, I have used it on my application but haven't got the chance to write about it on my blog. These are my gists that relates to token_auth_wrapping:

the token wrapper:
https://gist.github.com/giosakti/5537888

factory that uses the wrapper:
https://gist.github.com/giosakti/5537889

@jesalg
Copy link

jesalg commented May 8, 2013

Thanks @giosakti I'll check those out!

@karlfreeman
Copy link
Owner

Still working on an internal implementation too add to this repo. Sorry for not being responsive :(

@jesalg
Copy link

jesalg commented May 8, 2013

@karlfreeman Great job so far, this demo has been very helpful.

I was thinking of a simpler strategy for my own use where I would just ping a server route such as /current_user which will either send me the logged-in user's info or 401. An interceptor in angular would handle that 401 by broadcasting an event and/or sending the user to to a login screen. In addition to that, I could also call getCurrentUser() every time the application starts to make sure I have the current state of the user. That would solve most of the issues discussed above such as current_user expiring or being stale when stored in localStorage or sessionStorage

That would also greatly simplify things, only downside being that there would be a call to the back-end on every route change. Although if most of your angular controllers are hitting the server for data this would be enforced anyway with before_filter :authenticate_user!

Now the only other thing I need to figure out is how to take them back to the angular controller they came from. There might be a way to store that in the localStorage somehow via the interceptor right before they are redirected to the login controller.

@karlfreeman
Copy link
Owner

@jesalg Your bang on the same trail I've headed down.

Personally I wouldn't use an interceptor for all 401's as against the api I'm building sometimes 401 is due to lack of permission, not authorization.

How I've handled the route change checking /current_user is to wrap them in a resolve which checks against the state of the current_user. The redirect back path is set within the resolve. When I'm abit more free I'll be updating this with the missing peice of the puzzle. Thanks :)

@jesalg
Copy link

jesalg commented May 8, 2013

@karlfreeman That's interesting. I'll explore that option. Thanks!

@giosakti
Copy link
Author

giosakti commented May 9, 2013

The alternative system looks interesting guys!

However design-practice-wise, is it a good practice to make additional request for authentication every time the route changes? So basically, when I click on my app that points to another location, it will (minimum) make two request; one for the resource and the other one for the authentication information. And technically speaking, you also have to send the resource that user wants to access to the '/current_user' right?

Pardon me if my understanding is wrong, I do think that it will simplify things but I'm seeing potential security problem there. Because i believe that authentication, authorization and accessing resource should be made in one request.

@jesalg
Copy link

jesalg commented May 9, 2013

@giosakti It won't necessarily be making two requests every time. Assuming we are still taking about using this with Rails and devise, the resource in question will most likely be protected with before_filter :authenticate_user! in the Rails controller. So your interceptor should catch that in case of an unauthenticated user, have the user authenticate (possibly storing the auth response in global scope for UI purposes), and redirect them back to the controller they came from.

Only scenario I see where you'd have to make an explicit request to /current_user is when you have a route with static content in angular which you want to protect (but that is very unlikely) and of course at the beginning of the application if needed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants