LoginSignup
182

More than 5 years have passed since last update.

Devise + Grape で認証付きAPIの実装

Last updated at Posted at 2015-06-30

ここ一年ほど Python でバックエンドエンジニアやってます。自分の勉強も兼ねて、デモ用 API を Ruby on Rails で作成 したので、その時の実装と参考にした記事をまとめます。

間違った内容を記述してしまうことがあるかもしれません。その際はコメント欄やツイッターなどで指摘していただけると助かります。

対象

Ruby on Rails で作成したアプリに API の実装を考えている方。
主に Grape と Devise という gem を使って API に認証機能を作成する方法について紹介します。
Ruby on Rails の環境構築や、フレームワークのメリット、API の仕組みなどについては紹介しません。

余談: 実装する背景と選択肢

Rails は API を提供するのみで、残りはすべてクライアントに委譲する、というのが目的です。
フロントエンドは AngularJS などを用いて、Ajax で必要なデータを API 叩いて取ってくる、という形になります。
ほかのクライアント (たとえば python で書かれたクライアントなど) も API を利用することができるので、一般的な

1.コントローラを Web ページ用に記述
2. API の実装

という作業のうち、コントローラを記述する工程を省略し、API の実装に集中できるというのがメリットです。デメリットとしては、Ruby on Rails が生成してくれる HTML タグなどをフロントエンドが使えないことです。API の提供のみなので、基本的にサーバとの通信は JSON のみになります。

この記事では Grape (Rails の API 作成用 gem) で作成するAPIに Devise (認証機能用の gem) の認証機能を追加する、という方法を説明します。これは、Rails の認証機能に Devise を用いるケースは多く、また API を実装する上でも Grape を利用するケースは多いと考えたからです。これ以外にも doorkeeper を用いる方法など、いくつか方法はあるようです。この記事を書く上で参考にしたサイトを最後に追加してあるので、よかったらそちらも参考にしてください。
以上余談でした。

インストール

Gemfile.rb
gem 'devise'
gem 'devise_token_auth'
gem 'grape'
gem 'omniauth', '>= 1.0.0'

設定

rails g devise_token_auth:install [User] [auth]
  • User: 認証で利用するモデル
  • auth: 自動で生成してくれる認証用APIのエンドポイント
    • この場合 http://locahost:3000/auth/ 以下に認証用APIが生成される
    • 後から変更する場合は config/routes.rb を編集するだけなので特に気にせず

注: このコマンドで migration ファイルが生成されるので、編集した後、rake db:migrate を忘れずに

applicatoin_controller.rb
class ApplicationController < ActionController::Base
  include DeviseTokenAuth::Concerns::SetUserByToken
  # protect_from_forgery with: :exception から下記へ
  # これをしないと API で POST の時にCSRFエラー
  protect_from_forgery with: :null_session
end

model/user.rb
class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable, :omniauthable
          # :confirmable をコメントアウトする
          # これをしないと User を作成するときにエラーとなる
          # それ以外は自由に。
  include DeviseTokenAuth::Concerns::User
end
config/routes.rb
Rails.application.routes.draw do

  devise_for :users

  namespace :api do
    # localhost:3000/api/v1/auth に認証API
    mount_devise_token_auth_for 'User', at: '/v1/auth'

    # mount API::Root => '/' この行は下記の Grape の実装後に追加

  end
end

これで認証用の API が 'localhost:3000/v1/auth/' に定義されている。
APIのエンドポイントは このドキュメントを参考してください。(次の項目で動作確認します)

app/api/api.rb
module API
  class Root < Grape::API
    mount V1::Root
  end
end
app/api/v1/root.rb
module V1
  class Root < Grape::API

    version 'v1', using: :path
    format :json
    formatter :json, Grape::Formatter::Jbuilder

    helpers do
      def authenticate_error!
        # 認証が失敗したときのエラー
        h = {'Access-Control-Allow-Origin' => "*", 
             'Access-Control-Request-Method' => %w{GET POST OPTIONS}.join(",")}
        error!('You need to log in to use the app.', 401, h)
      end

      def authenticate_user!
        # header から認証に必要な情報を取得
        uid = request.headers['Uid']
        token = request.headers['Access-Token']
        client = request.headers['Client']
        @user = User.find_by_uid(uid)

        # 認証に失敗したらエラー
        unless @user && @user.valid_token?(token, client)
          authenticate_error!
        end
      end

    end

    desc 'GET /api/v1/test'
    get 'test' do
      authenticate_user!
      {message: 'test'}
    end
    # サンプルなので、単純に root.rb に記述
  end
end

注: config/routes.rbmount API::Root => '/' を加える(前述)のを忘れずに。

動作確認

以下動作確認です。Postman という chrome のプラグインを用いてリクエストを作成しています。curl コマンドでも、他のクライアントでも基本的に動作します。

1. POST /api/v1/auth でユーザ作成と Token の取得
  • email, password, password_confirmation が必須
  • レスポンスの Header に Uid, Client, Access-Token

Screen Shot 2015-06-30 at 11.37.39 AM.png

2. GET /api/v1/test でデータの取得
  • ヘッダーに Client, Access-Token, Uid をセットしてリクエスト
  • 正しくデータが帰ってくることを確認

Screen Shot 2015-06-30 at 11.38.56 AM.png

余談2: Token の更新や AngularJS との連携

以上で API に認証機能が追加されていることが確認できたと思います。
Token の更新に関してですが、今回の例では一度発行した Token は再度ログインするまで更新されません。セキュリティの理由から例えばリクエストのたびに Token を更新したい場合はこのページが参考になります (このページではAuthenticateRequest クラスを作って認証を行い、Grape の after 構文の中で Token の更新および更新された Token をレスポンスのヘッダーに入れています。)。個人的には一回のリクエストごとに Token の更新をするのは実装的に面倒だと考えたのと(例えば、iphone と web ページで同じユーザが操作している場合の対応など)、HTTPS を利用しているのであれば Token の更新は30日ごとなどでも十分だと思ったので深入りはしませんでした。

AngularJS との連携ですが、ng-token-auth というライブラリがあり、これを用いたところ楽にログインを実装できました。これに関しては AngularJSとRailsをTokenベースの認証で繋ぐ方法(devise_token_auth + ng-token-auth) や、ng-auth-token の README が参考になると思います。

まとめ

Grape で提供している API に Devise で認証機能を追加しました。これによって HTTP ヘッダーを利用して Token を用いた認証が可能となります。この記事では最低限の認証の仕組みを実装したにすぎないですが、本番環境では、Token の有効期限や、有効期限が切れた後の更新などを考える必要がある点に注意です。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
182