Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I store and set the user on this angularjs devise library auth service? Or is it being done already?

I am using the cloudspace angularjs-devise library on the client. When I try to login/register I get a 200 ok response with the plain user object visible in the chrome js console. Refreshing the page seems to lose this information even though I assumed that the service would store this at some point since it also has logout and currentUser methods. https://github.com/cloudspace/angular_devise

My questions are:

1) Is this service actually storing the user and if so how (i.e. with cookies or localstorage or in memory)?

2) If the service does not store the user how can I store this information in a custom cookie/localstorage and more importantly set the user into the service so that the services "isauthenticated" and "currentuser" methods can be used?

Partial Library Readme Instructions

Just register Devise as a dependency for your module. Then, the Auth service will be available for use.

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        // Configure Auth service with AuthProvider
    }).
    controller('myCtrl', function(Auth) {
        // Use your configured Auth service.
    }); 

Auth.login(creds): Use Auth.login() to authenticate with the server. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object which should contain any credentials needed to authenticate with the server. Auth.login() will return a promise that will resolve to the logged-in user. See AuthProvider.parse() for parsing the user into a usable object.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var credentials = {
            email: '[email protected]',
            password: 'password1'
        };

        Auth.login(credentials).then(function(user) {
            console.log(user); // => {id: 1, ect: '...'}
        }, function(error) {
            // Authentication failed...
        });
    });

My partial code:

main.js

var myApp = angular.module('mail_app', ['ngRoute', 'ngResource', 'Devise']);

myApp.config(function($routeProvider, $locationProvider, $httpProvider, AuthProvider) {
    console.log("in router")
    $locationProvider.html5Mode(true);
    $httpProvider.defaults.headers.common['X-CSRF-Token'] = 
    $('meta[name=csrf-token]').attr('content');
    $httpProvider.defaults.headers.common['ClientType'] = 'browser';

    // Customise login
    AuthProvider.loginMethod('POST');
    AuthProvider.loginPath('/api/v1/users/login.json');

    // Customise register
    AuthProvider.registerMethod('POST');
    AuthProvider.registerPath('/api/v1/users.json');

});

SessionsController.js

myApp.controller('SessionsController', ['$scope', 'Auth', '$http', function($scope, Auth, $http) {
 console.log("in session controller")
  console.log(Auth.isAuthenticated());

    $scope.loginUser = function() {
         console.log("in login")
        var credentials = {
            email: $scope.email,
            password: $scope.password
        };

        Auth.login(credentials).then(function(user) {
            $scope.authError = 'Success!';
            console.log(user); // => {id: 1, ect: '...'}
            Auth.currentUser = user;
        }, function(error) {
            $scope.authError = 'Authentication failed...';
        });     
    };

    $scope.registerUser = function(){
         console.log("in register function")
        var ncredentials = {
            email: $scope.newEmail,
            password: $scope.newPassword,
            password_confirmation: $scope.newPasswordConfirmation
        };

        Auth.register(ncredentials).then(function(registeredUser) {
            console.log(registeredUser); // => {id: 1, ect: '...'};
        }, function(error) {
            $scope.authError = 'Registration failed...';
        });
    };

    $scope.getCurrentUser = function(){
        Auth.currentUser().then(function(user) {
            // User was logged in, or Devise returned
            // previously authenticated session.
            console.log(user); // => {id: 1, ect: '...'}
            $scope.id = user.id;
        }, function(error) {
            // unauthenticated error
        });     
    };

    $scope.isUserAuthenticated = function(){
        Auth.isAuthenticated();     
    };


}]);
like image 231
LightBox Avatar asked Jun 08 '14 03:06

LightBox


2 Answers

First of all you need to understand how cookies and sessions work in Rails.

From this article:

Rails uses a CookieStore to handle sessions. What it means is that all the informations needed to identify a user's session is sent to the client and nothing is stored on the server. When a user sends a request, the session's cookie is processed and validated so rails, warden, devise, etc. can figure out who you are and instantiate the correct user from the database.

What this means is that on every request, Rails will look up at the session cookie, decode it and get something like

cookie = {
  "session_id": "Value",
  "_csrf_token": "token",
  "user_id": "1"
}

At that point Rails knows that the current user has id=1 and can make a sql query. (Like current_user = User.find(1)).

When a user is logged in, a cookie is created, when the user is logged out - the cookie is destroyed. If Rails doesn't find a cookie or the cookie doesn't have information about the current user, devise will assume that the user is not logged in (current_user is nil)

Even if you login through ajax (to be particular it is through the 'angular_devise' gem in your case) the cookie is created. It is not stored on the server, but in the browser. (This is why if you are logged in one browser, you are not automatically logged in another browser) As you pointed out the library doesn't keep information who is logged in, and this is because the information is stored in a cookie and the library cannot decode the cookie without help from the server.

This is why you will have to make a call to get the current user if the user refreshes the page. (Sorry)

The way to get the current_user is very simple. This is the cleanest solution I found.

# application_controller.rb

def me
  render json: current_user
end

# routes.rb

get "me" => "application#me"

// main.js

// I am not familiar with angular_devise lib but you get the point:
// this method fetches from server when myApp is initialized (e.g. on page reload)
// and assigns the current_user so he/she can be used by the app
myApp.run(["AuthService", function(AuthService) {

  AuthService.getUserFromServer();

}]);

If you have to load data specific to the user, you will have to load the user first and then the data. Needless to say you will have to use promises.

TL;DR: You will have to ask the server

I am open for questions and comments.

like image 108
tsikov Avatar answered Oct 03 '22 18:10

tsikov


I guess your problem is the refresh. The angular-devise lib is probably assuming you are in a SPA (Singe Page Application) so it should not refresh. With this assumption, angular-devise can store all the information in memory. When you refresh your page, you basically bootstrap the application from zero. And the request to server is probably issued by your code when application is starting. You probably call Auth.currentUser() somewhere on start of the application

like image 23
Oleksandr Kruk Avatar answered Oct 03 '22 20:10

Oleksandr Kruk