Garage RailsでOAuth認証付きのRest APIをお手軽開発! 


CookpadさんがOSSで先日OSSで公開されたGarageはRestfulなAPI + OAuth(Doorkeeper)をワンストップで提供してくれるgemです。
ちょうど触る機会が出てきたので、今回四苦八苦しながら使ってみたのでそのメモです!


🎂 今回のサンプル実装

今回はOAuthで認証して、次のシンプルなAPIにアクセスできるようにするまでのサンプルを作成します。

GET /v1/users => ユーザーのリスト出力
GET /v1/users/:id => 個々のユーザー情報の出力

🎃 Gemの追加

Gemfileに以下を追加して、bundle install

gem 'garage', github: 'cookpad/garage'
gem 'responders', '~> 2.0' # If you use Rails4.2+

group :development, :test do
gem 'factory_girl_rails', '~> 4.5.0'
gem 'rspec-rails', '~> 3.1.0'
end

🏈 DBの設定(Migration)

GagrageやRSpecの初期設定とか、マイグレーションとかを実行。

# Doorkeeper(Oauth認証)の初期設定
$ bundle exec rails generate doorkeeper:install

# Doorkeeper(Oauth認証)のMigrationファイル生成
$ bundle exec rails generate doorkeeper:migration

# DBの作成
$ bundle exec rake db:create

# 認証用のユーザーモデル作成
$ bundle exec rails g model user name:string email:string

# マイグレーション処理の実行
$ bundle exec rake db:migrate

🍮 Garageの設定

config/initializers/garage.rbを作成して、Garageの設定を記述。

Garage.configure {}

Garage::TokenScope.configure do
register :public, desc: 'accessing publicly available data' do
access :read, User
access :write, User
end
end

Doorkeeper.configure do
orm :active_record

# デフォルトのスコープ
default_scopes :public

optional_scopes(*Garage::TokenScope.optional_scopes)

# アプリケーションのオーナーの認証
resource_owner_from_credentials do |routes|
User.find_by(email: params[:username])
end
end

🐰 ルーティングの設定

config/routes.rbにルーティングを追記。

Rails.application.routes.draw do
use_doorkeeper

scope :v1 do
resources :users, only: %i(index show update)
end
end

🗽 コントローラの作成

app/controllers/application_controller.rbに共通の設定を追記。

class ApplicationController < ActionController::Base
# ↓ 以下追記する内容
include Garage::ControllerHelper

def current_resource_owner
@current_resource_owner ||= User.find(resource_owner_id) if resource_owner_id
end
end

app/controllers/users_controller.rbを作成して、設定を追記。

class UsersController < ApplicationController
include Garage::RestfulActions

# index
def require_resources
@resources = User.all
end

# show
def require_resource
@resources = User.find(params[:id])
end
end

👽 モデルの設定

さらにapp/models/user.rbにモデルの設定を追記。

class User < ActiveRecord::Base
include Garage::Representer
include Garage::Authorizable

property :id
property :name
property :email

# index
def self.build_permissions(perms, other, target)
perms.permits! :read
end

# create/update/show/destory
def build_permissions(perms, other)
perms.permits! :read
perms.permits! :write
end
end

🎉 動作確認

# テストユーザーの作成
$ bundle exec rails runner 'User.create(name: "morizyun", email: "morizyun@example.com")'

# サーバーの起動
$ bundle exec rails s

http://localhost:3000/oauth/applications

上のURLにアクセスして、テスト用のクライアントを登録します。
登録したら、APPLICTION_IDAPPLICATION_SECRETを登録します。

# APPLICTION_IDとAPPLICATION_SECRETを使って、ACCESS_TOKENを取得
curl -u "$APPLICTION_ID:$APPLICATION_SECRET" -XPOST http://localhost:3000/oauth/token -d 'grant_type=password&username=morizyun@example.com'

# 上で取得したACCESS_TOKENを使ってAPIでuserの一覧を取得
curl -s -XGET -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:3000/v1/users | jq '.'

# 上で取得したaccess_tokenを使ってAPIでuserの一覧を取得
curl -s -XGET -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:3000/v1/users/1 | jq '.'

🐞 より複雑な処理のためにテストを書く

ここからさらに複雑な処理を記述するために、RSpecでテストを記述できるようにします。

🎳 RSpecの設定

RSpecの設定。

# RSpecの初期設定
$ bundle exec rails g rspec:install

🐝 spec_helper.rbの設定

spec/spec_helper.rbにFactoryGirl関連の設定を追加。

require 'factory_girl_rails'

RSpec.configure do |config|
config.before :all do
FactoryGirl.reload
FactoryGirl.factories.clear
FactoryGirl.sequences.clear
FactoryGirl.find_definitions
end

config.include FactoryGirl::Syntax::Methods

config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
end

🍣 request_helper.rbの設定

RSpec実行時にOAuthの認証処理をあらかじめやってくれて、access_tokenを取得してくれるhelperの作成。
spec/request_helper.rbを作成して以下を追加。

require 'active_support/concern'

module RequestHelper
extend ActiveSupport::Concern

included do

let(:params) { {} }

let(:report_status_env) do
{
accept: 'application/json',
authorization: report_status_authorization_header_value
}
end

let(:report_status_authorization_header_value) { "Bearer #{report_status_access_token.token}" }

let(:report_status_access_token) do
FactoryGirl.create(
:access_token,
resource_owner_id: resource_owner.id,
scopes: public,
application: application
)
end

let(:resource_owner) { FactoryGirl.create(:user) }
let(:report_status_scopes) { 'public' }
let(:application) { FactoryGirl.create(:application) }
end
end

😎 FactoryGirl(Fixture)の設定

spec/factories/users.rbで以下を追加。

FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
sequence(:name) {|n| "user#{n}" }
email { "#{name}@example.com" }
end
end

Doorkeeper用の設定として、spec/factories/doorkeeper.rbを追加。

FactoryGirl.define do
factory :access_token, class: Doorkeeper::AccessToken do
sequence(:resource_owner_id) { |n| n }
application
expires_in 1.hours
end

factory :application, class: Doorkeeper::Application do
sequence(:name){ |n| "Application #{n}" }
redirect_uri 'https://example.com/callback'
end
end

🗻 Specファイルの作成

spec/requests/users_spec.rbを以下のように変更。

require 'rspec_helper'
require 'request_helper'

RSpec.describe 'users', type: :request do
include RequestHelper

describe 'GET /v1/users' do
let!(:users) { create_list(:user, 3) }

it 'returns user resources' do
get '/v1/users', params, env
expect(response).to have_http_status(200)
end
end
end

🐠 RSpecの実施

# Test環境用のDBの作成
$ RAILS_ENV=test bundle exec rake db:create migrate

# migrationの実施
$ RAILS_ENV=test bundle exec rake db:migrate

# users_specの実施
$ bundle exec rspec -fp spec/requests/users_spec.rb

🚕 ここから使いこなすのに参考になりそうな資料

CookpadさんのTech Blogの解説記事です。かなりわかりやすいですし、APIを作る時に参考になります^^

RESTful Web API 開発をささえる Garage - クックパッド開発者ブログ

Cookpadのエンジニアさんが作ってくれているサンプルのおかげでかなり捗りました!

taiki45/garage-example - GitHub

🐮 参考リンク

RESTful Hypermedia APIをRailsで実現するcookpad/garageが凄い - Qiita

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍