はじめに
- Rails でモバイルアプリのバックエンドに使う API サーバーのユーザー作成と認証の仕組みを作ったメモです
- Doorkeeper + Sorcery を使った認証の構築とテストまで記述します
導入
新しいプロジェクトを作成
$ rails new -T -B doorkeeper-test
gem のインストール
デフォルトの Gemfile に以下を追記
gem 'doorkeeper'
gem 'sorcery'
group :development, :test do
gem 'rspec-rails'
gem 'factory_girl_rails'
gem 'database_rewinder'
end
Sorcery のインストール
$ rails generate sorcery:install
Doorkeeper のインストール
$ rails generate doorkeeper:install
$ rails generate doorkeeper:migration
Doorkeeper の設定
Doorkeeper.configure do
orm :active_record
resource_owner_authenticator do
User.find_by_id(session[:current_user_id]) || redirect_to(login_url)
end
resource_owner_from_credentials do
User.authenticate(params[:username], params[:password])
end
end
Doorkeeper.configuration.token_grant_types << "password"
ユーザーコントローラの作成
- ユーザーの作成とユーザー情報表示ページを作成する
- ユーザー作成は認証なしで、ユーザー情報表示は認証必須のページにする
generate
$ rails g controller users create show
routes.rb
resources :users, only: [:create, :show]
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :doorkeeper_authorize!, only: [:show]
def create
@user = User.new(user_params)
if @user.save
render json: @user
else
head :bad_request
end
end
def show
render json: User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email, :password)
end
end
app/controllers/application_controller.rb
- protect_from_forgery with: :exception
+ protect_from_forgery with: :null_session
動作確認
ユーザーの作成
$ curl -F "user[email]=user1@example.com" -F "user[password]=password" http://127.0.0.1:3000/users
アクセストークンの取得
- client_id には「Doorkeeper の認証用キーの作成」で作成した Application Id を、client_secret には同じく Secret を指定する
$ curl -F "grant_type=password" \
-F "client_id=07461c1f68870ea090ed20af49d0680782950119213e2a1af9e1f653c688dca3" \
-F "client_secret=20dea83f022c9e2a0e6fbec1da0e9c0e20c40e1bf09d2e8db029b461653a88bd" \
-F "username=user1@example.com" -F "password=password" \
http://127.0.0.1:3000/oauth/token.json
ユーザーの取得
- Bearer 以降には「アクセストークンの取得」で取得したトークンの文字列を指定する
$ curl -H "Authorization: Bearer 18afa04c80fbe3528b5d495c24e8badcbaeee12b2866d22d8f220c44543ae01c" http://127.0.0.1:3000/users/1
テスト
rails_helper.rb に追記
config.before :suite do
DatabaseRewinder.clean_all
end
config.after :each do
DatabaseRewinder.clean
end
config.before :all do
FactoryGirl.reload
end
specs_helper.rb に追記
require 'factory_girl_rails'
config.include FactoryGirl::Syntax::Methods
factories/users.rb の作成
FactoryGirl.define do
factory :user do
sequence(:email) {|n| "user#{n}@example.com" }
password "password"
end
end
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/controllers/user_controller_spec.rb
require 'rails_helper'
RSpec.describe UsersController, type: :controller do
describe "GET #show" do
let!(:application) { create :application }
let!(:user) { create :user }
let!(:token) { create :access_token, :application => application, :resource_owner_id => user.id }
it "returns http success" do
get :show, {format: :json, access_token: token.token, id: user.id}
expect(response).to have_http_status(:success)
end
end
end
さいごに
- クライアントからは Doorkeeper のトークン情報を見れる必要はないから、中継用のアクションとかを用意したほうがいいかな?