Skip to content

cloudspace/angular_devise

Repository files navigation

AngularDevise Build Status

A small AngularJS Service to interact with Devise Authentication.

Requirements

This service requires Devise to respond to JSON. To do that, simply add

# config/application.rb
module RailsApp
  class Application < Rails::Application
    # ...

    config.to_prepare do
      DeviseController.respond_to :html, :json
    end
  end
end

Additionally, if you have CSRF Forgery Protection enabled for your controller actions, you will also need to include the X-CSRF-TOKEN header with the token provided by rails. The easiest way to include this is to follow this post:

angular_rails_csrf.

Downloading

AngularDevise is registered as angular-devise in bower.

bower install --save angular-devise

You can then use the main file at angular-devise/lib/devise-min.js.

Rails Assets

To get AngularDevise via Rails Assets add to your Gemfile:

source "https://rails-assets.org" do
  gem "rails-assets-angular-devise"
end

Then bundle. Finally, to require the JS:

//= require angular-devise

Usage

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.currentUser()

Auth.currentUser() returns a promise that will be resolved into the currentUser. There are three possible outcomes:

  1. Auth has authenticated a user, and will resolve with that user.
  2. Auth has not authenticated a user but the server has a previously authenticated session, Auth will attempt to retrieve that session and resolve with its user. Then, a devise:new-session event will be broadcast with the current user as the argument.
  3. Neither Auth nor the server has an authenticated session, and a rejected promise will be returned. (see Interceptor for for custom handling.)
angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        Auth.currentUser().then(function(user) {
            // User was logged in, or Devise returned
            // previously authenticated session.
            console.log(user); // => {id: 1, ect: '...'}
        }, function(error) {
            // unauthenticated error
        });
    });

Auth._currentUser

Auth._currentUser will be either null or the currentUser's object representation. It is not recommended to directly access Auth._currentUser, but instead use Auth.currentUser().

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        console.log(Auth._currentUser); // => null

        // Log in user...

        console.log(Auth._currentUser); // => {id: 1, ect: '...'}
    });

Auth.isAuthenticated()

Auth.isAuthenticated() is a helper method to determine if a currentUser is logged in with Auth.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        console.log(Auth.isAuthenticated()); // => false

        // Log in user...

        console.log(Auth.isAuthenticated()); // => true
    });

Auth.login(creds, config)

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 Auth.parse(response) to customize how the response is parsed into a user.

Upon a successful login, two events will be broadcast, devise:login and devise:new-session, both with the currentUser as the argument. New-Session will only be broadcast if the user was logged in by Auth.login({...}). If the server has a previously authenticated session, only the login event will be broadcast.

Pass any additional config options you need to provide to $http with config.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var credentials = {
            email: 'user@domain.com',
            password: 'password1'
        };
        var config = {
            headers: {
                'X-HTTP-Method-Override': 'POST'
            }
        };

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

        $scope.$on('devise:login', function(event, currentUser) {
            // after a login, a hard refresh, a new tab
        });

        $scope.$on('devise:new-session', function(event, currentUser) {
            // user logged in by Auth.login({...})
        });
    });

By default, login will POST to '/users/sign_in.json' using the resource name user. The path, HTTP method, and resource name used to login are configurable using:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        AuthProvider.loginPath('path/on/server.json');
        AuthProvider.loginMethod('GET');
        AuthProvider.resourceName('customer');
    });

Auth.logout()

Use Auth.logout() to de-authenticate from the server. Auth.logout() returns a promise that will be resolved to the old currentUser. Then a devise:logout event will be broadcast with the old currentUser as the argument.

Pass any additional config options you need to provide to $http with config.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var config = {
            headers: {
                'X-HTTP-Method-Override': 'DELETE'
            }
        };
        // Log in user...
        // ...
        Auth.logout(config).then(function(oldUser) {
            // alert(oldUser.name + "you're signed out now.");
        }, function(error) {
            // An error occurred logging out.
        });

        $scope.$on('devise:logout', function(event, oldCurrentUser) {
            // ...
        });
    });

By default, logout will DELETE to '/users/sign_out.json'. The path and HTTP method used to logout are configurable using:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        AuthProvider.logoutPath('path/on/server.json');
        AuthProvider.logoutMethod('GET');
    });

Auth.parse(response)

This is the method used to parse the $http response into the appropriate user object. By default, it simply returns response.data. This can be customized either by specifying a parse function during configuration:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        // Customize user parsing
        // NOTE: **MUST** return a truth-y expression
        AuthProvider.parse(function(response) {
            return response.data.user;
        });
    });

or by directly overwriting it, perhaps when writing a custom version of the Auth service which depends on another service:

angular.module('myModule', ['Devise']).
  factory('User', function() {
    // Custom user factory
  }).
  factory('CustomAuth', function(Auth, User) {
    Auth['parse'] = function(response) {
      return new User(response.data);
    };
    return Auth;
  });

Auth.register(creds)

Use Auth.register() to register and authenticate with the server. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object that should contain any credentials needed to register with the server. Auth.register() will return a promise that will resolve to the registered user. See Auth.parse(response) to customize how the response is parsed into a user. Then a devise:new-registration event will be broadcast with the user object as the argument.

Pass any additional config options you need to provide to $http with config.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var credentials = {
            email: 'user@domain.com',
            password: 'password1',
            password_confirmation: 'password1'
        };
        var config = {
            headers: {
                'X-HTTP-Method-Override': 'POST'
            }
        };

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

        $scope.$on('devise:new-registration', function(event, user) {
            // ...
        });
    });

By default, register will POST to '/users.json' using the resource name user. The path, HTTP method, and resource name used to register are configurable using:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        AuthProvider.registerPath('path/on/server.json');
        AuthProvider.registerMethod('GET');
        AuthProvider.resourceName('customer');
    });

Auth.sendResetPasswordInstructions(creds)

Use Auth.sendResetPasswordInstructions() to send reset password mail to user. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object that should contain the email associated with the user. Auth.sendResetPasswordInstructions() will return a promise with no params. Then a devise:send-reset-password-instructions-successfully event will be broadcast.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var parameters = {
            email: 'user@domain.com'
        };

        Auth.sendResetPasswordInstructions(parameters).then(function() {
            // Sended email if user found otherwise email not sended...
        });

        $scope.$on('devise:send-reset-password-instructions-successfully', function(event) {
            // ...
        });
    });

By default, sendResetPasswordInstructions will POST to '/users/password.json'. The path and HTTP method used to send the reset password instructions are configurable using:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        AuthProvider.sendResetPasswordInstructionsPath('path/on/server.json');
        AuthProvider.sendResetPasswordInstructionsMethod('POST');
    });

Auth.resetPassword(creds)

Use Auth.resetPassword() to reset user password. Keep in mind, credentials are sent in plaintext; use a SSL connection to secure them. creds is an object that should contain password, password_confirmation and reset_password_token. Auth.resetPassword() will return a promise that will resolve to the new user data. See Auth.parse(response) to customize how the response is parsed into a user. Then a devise:reset-password-successfully event will be broadcast.

angular.module('myModule', ['Devise']).
    controller('myCtrl', function(Auth) {
        var parameters = {
            password: 'new_password',
            password_confirmation: 'new_password',
            reset_password_token: 'reset_token',
        };

        Auth.resetPassword(parameters).then(function(new_data) {
            console.log(new_data); // => {id: 1, ect: '...'}
        }, function(error) {
            // Reset password failed...
        });

        $scope.$on('devise:reset-password-successfully', function(event) {
            // ...
        });
    });

By default, resetPassword will PUT to '/users/password.json'. The path and HTTP method used to reset password are configurable using:

angular.module('myModule', ['Devise']).
    config(function(AuthProvider) {
        AuthProvider.resetPasswordPath('path/on/server.json');
        AuthProvider.resetPasswordMethod('PUT');
    });

Interceptor

AngularDevise comes with a $http Interceptor that may be enabled using the interceptAuth config. Its purpose is to listen for 401 Unauthorized responses and give you the ability to seamlessly recover. When it catches a 401, it will:

  1. create a deferred
  2. broadcast a devise:unauthorized event passing:
    • the ajax response
    • the deferred
  3. return the deferred's promise

Since the deferred is passed to the devise:unauthorized event, you are free to resolve it (and the request) inside of the event listener. For instance:

angular.module('myModule', []).
    controller('myCtrl', function($scope, Auth, $http) {
        // Guest user

        // Catch unauthorized requests and recover.
        $scope.$on('devise:unauthorized', function(event, xhr, deferred) {
            // Disable interceptor on _this_ login request,
            // so that it too isn't caught by the interceptor
            // on a failed login.
            var config = {
                interceptAuth: false
            };

            // Ask user for login credentials
            Auth.login(credentials, config).then(function() {
                // Successfully logged in.
                // Redo the original request.
                return $http(xhr.config);
            }).then(function(response) {
                // Successfully recovered from unauthorized error.
                // Resolve the original request's promise.
                deferred.resolve(response);
            }, function(error) {
                // There was an error logging in.
                // Reject the original request's promise.
                deferred.reject(error);
            });
        });

        // Request requires authorization
        // Will cause a `401 Unauthorized` response,
        // that will be recovered by our listener above.
        $http.delete('/users/1', {
            interceptAuth: true
        }).then(function(response) {
            // Deleted user 1
        }, function(error) {
            // Something went wrong.
        });
    });

The Interceptor can be enabled globally or on a per-request basis using the interceptAuth setting on the AuthIntercept provider.

angular.module('myModule', ['Devise']).
    config(function(AuthInterceptProvider) {
        // Intercept 401 Unauthorized everywhere
        AuthInterceptProvider.interceptAuth(true);
    }).
    controller('myCtrl', function($http) {
        // Disable per-request
        $http({
            url: '/',
            interceptAuth: false,
            // ...
        });
    });

AuthProvider

By default, AngularDevise uses the following HTTP methods/paths:

Method HTTP Method HTTP Path
login POST /users/sign_in.json
logout DELETE /users/sign_out.json
register POST /users.json
sendResetPasswordInstructions POST /users/password.json
resetPassword  POST /users/password.json

All credentials will be under the users namespace, and the following parse function will be used to parse the response:

function(response) {
    return response.data;
};

All of these can be configured using a .config block in your module.

angular.module('myModule', ['Devise']).
    config(function(AuthProvider, AuthInterceptProvider) {
        // Customize login
        AuthProvider.loginMethod('GET');
        AuthProvider.loginPath('/admins/login.json');

        // Customize logout
        AuthProvider.logoutMethod('POST');
        AuthProvider.logoutPath('/user/logout.json');

        // Customize register
        AuthProvider.registerMethod('PATCH');
        AuthProvider.registerPath('/user/sign_up.json');

        // Customize the resource name data use namespaced under
        // Pass false to disable the namespace altogether.
        AuthProvider.resourceName('customer');

        // Also you can change host URL for backend calls
        // (for example if it's on another server than your angular app)
        AuthProvider.baseUrl('http://localhost:3000');

        // Customize user parsing
        // NOTE: **MUST** return a truth-y expression
        AuthProvider.parse(function(response) {
            return response.data.user;
        });

        // Intercept 401 Unauthorized everywhere
        // Enables `devise:unauthorized` interceptor
        AuthInterceptProvider.interceptAuth(true);
    });

Credits

Cloudspace

AngularDevise is maintained by Cloudspace, and is distributed under the MIT License.

About

A small AngularJS Service to interact with Devise Authentication.

Resources

License

Stars

Watchers

Forks

Packages

No packages published