RailsなWebアプリを構築する際の認証系のライブラリとして人気のDeviseをAngularJSと連携してみる。

DeviseとAngularJSを連携させるAngularDeviseというライブラリが公開されてるのでそれを導入。

インストール

bowerでangular-deviseをインストールする。bower-railsがインストールされてる前提で↓
(bowerではなくRailsのgemとしてアセットを追加する方法もある)
まずBowerfileにangular-deviseを追加し、

asset 'angular-devise'

インストールすると

$ bundle exec rake bower:install

vendor/assets/bower_components/angular-deviseが生成される。

最後にapplication.jsにangular-deviseのロード処理を記述。

//= require angular-devise

Rails側の設定

認証結果をJSONで受け取る必要があるので、ApplicationControllerに↓を定義。

class ApplicationController < ActionController::Base
  respond_to :html, :json
...
end

また、CSRF Forgery Protection を有効にする際はangular_rails_csrfの利用を推奨。

AngularDeviseのロード

AngularJSの初期化時にDeviseをロードするモジュールとして追加する。

var app = angular.module('authApp', [
..
  'Devise',
...
]).config(function(AuthProvider){
  // AuthProviderを使ってAuthサービスを設定
});

モジュールがロードされるとAuthサービスが利用できる状態になる。

認証処理

未認証の場合はログイン画面へ、認証済みの場合はトップ画面に遷移するといったよくある認証系の遷移処理を実装する。認証状態による遷移の切り替えは、AngularJS でログインのフローを作るを参考にして実装。
今回は$rootScopeに$routeChangeStartをバインドして認証状態を判断して、ルーティングの挙動を変更する方法で実装。

app.config(function($routeProvider) {
  $routeProvider.when('/', {controller: 'MainController', templateUrl: 'top.html', reloadOnSearch: false});
...
  $routeProvider.when('/login', {controller: 'LoginController', templateUrl: 'login.html', reloadOnSearch: false});
  $routeProvider.otherwise({redirectTo: '/'});
});
app.run(function($rootScope, $location, $route, Auth){
  $rootScope.$on('$routeChangeStart', function(env, next, current){
    if(next.controller == 'LoginController'){
      if(Auth.isAuthenticated()){
        $location.path('/');
        $route.reload();
      }
    } else{
      if(!Auth.isAuthenticated()){
        $location.path('/login');
        $route.reload();
      }
    }
  });
});

認証画面のtemplateとcontrollerは↓

  • login.html
Forms
Login

  • login.controller.js
'use strict';
angular.module('authApp')
  .controller('LoginController', function($scope, $location, Auth){
    $scope.login = function(){
      var credentials = {
        email: $scope.email,
        password: $scope.password
      };
      console.log(credentials);
      Auth.login(credentials).then(function(user){
        console.log(user);
        $location.path('/');
      }, function(error){
        console.log(error);
        $scope.email = "";
        $scope.password = "";
        $scope.disable = false;
      })
    };
});

とAngular DeviseのAuthサービスを使うだけであとはよろしくやってくれる。

2015/05/20 追記
認証状態の判断は、↑の$routeChangeStartで行わずともAngularDeviseが提供しているAuthInterceptProviderを利用する方がスマートかも。
まず、AuthInterceptProviderを有効にする。

app.config(function (AuthInterceptProvider) {
  // Intercept 401 Unauthorized everywhere
  AuthInterceptProvider.interceptAuth(true);
});

401が発生すると、”devise:unauthorized”がブロードキャストされるので、ログイン画面へ遷移させる。

app.run(function($rootScope, $location, $route, Auth, $resource){
  $rootScope.$on('devise:unauthorized', function(event, xhr, deferred){
    Auth._currentUser = null;
    $location.path('/login');
    $route.reload();
  });
});