A small AngularJS Service to interact with Devise Authentication.
This service requires Devise to respond to JSON. To do that, simply add
# config/application.rbmoduleRailsAppclassApplication < Rails::Application# ...config.to_preparedoDeviseController.respond_to:html,:jsonendendendAdditionally, 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:
AngularDevise is registered as angular-devise in bower.
bower install --save angular-deviseYou can then use the main file at angular-devise/lib/devise-min.js.
To get AngularDevise via Rails Assets add to your Gemfile:
source"https://rails-assets.org"dogem"rails-assets-angular-devise"endThen bundle. Finally, to require the JS:
//= require angular-deviseJust 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() returns a promise that will be resolved into the currentUser. There are three possible outcomes:
- Auth has authenticated a user, and will resolve with that user.
- 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-sessionevent will be broadcast with the current user as the argument. - 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 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() 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});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){varcredentials={email: '[email protected]',password: 'password1'};varconfig={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');});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){varconfig={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');});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 expressionAuthProvider.parse(function(response){returnresponse.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){returnnewUser(response.data);};returnAuth;});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){varcredentials={email: '[email protected]',password: 'password1',password_confirmation: 'password1'};varconfig={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');});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){varparameters={email: '[email protected]'};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');});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){varparameters={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');});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:
- create a deferred
- broadcast a
devise:unauthorizedevent passing:- the ajax response
- the deferred
- 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.varconfig={interceptAuth: false};// Ask user for login credentialsAuth.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 everywhereAuthInterceptProvider.interceptAuth(true);}).controller('myCtrl',function($http){// Disable per-request$http({url: '/',interceptAuth: false,// ...});});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){returnresponse.data;};All of these can be configured using a .config block in your module.
angular.module('myModule',['Devise']).config(function(AuthProvider,AuthInterceptProvider){// Customize loginAuthProvider.loginMethod('GET');AuthProvider.loginPath('/admins/login.json');// Customize logoutAuthProvider.logoutMethod('POST');AuthProvider.logoutPath('/user/logout.json');// Customize registerAuthProvider.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 expressionAuthProvider.parse(function(response){returnresponse.data.user;});// Intercept 401 Unauthorized everywhere// Enables `devise:unauthorized` interceptorAuthInterceptProvider.interceptAuth(true);});AngularDevise is maintained by Cloudspace, and is distributed under the MIT License.

