スマホアプリから会員の新規登録、ログインが両方できるようにAPIを作成中。ようやく自前でアクセストークンを作ってOAuth認証が出来たのでまとめておく。
まず何がしたいか?
- スマホアプリでAPI認証ができるように、OAuthを自前で作成したい。
-> スマホアプリ側ではユーザ名とパスワードを入力すればトークンが取って来れて、そのトークンで各APIにアクセスすればユーザ固有の情報が取って来れるようになる仕組みを作る。
- スマホアプリ側でユーザ作成も出来るようにしたい。
-> APIでユーザが作れるようにする。もちろんhttps前提。
環境
gemfile
ruby '2.0.0' gem 'rails', '4.0.0' gem 'rails-api' gem 'active_model_serializers' gem 'mongoid', '4.0.0.alpha1' gem "moped", '2.0.0.beta6' gem 'sorcery' gem 'rack-cors', :require => 'rack/cors' gem 'doorkeeper'
認証ではDeviseではなく、Sorcery を。理由は Rails の認証で Devise ではなく Sorcery という選択 - ボクココ
ORM は ActiveRecord ではなく Mongoid を利用。
Sorcery と Doorkeeper のセットアップ
ここら辺はもうマニュアル通りで。前章のGemfile構成なら特に問題なくいけるはず。
rails g mongoid:config rails g sorcery:install rails g doorkeeper:install
config/initializers/doorkeeper.rb
doorkeeper で使用するORM と認証方法を指定。今回はdoorkeeperのpassword token で認証するので、以下のような感じでセット。
orm :mongoid4 resource_owner_from_credentials do |routes| User.authenticate(params[:username], params[:password]) end
config/application.rb
Moped::BSONが undefined だよっていちいち言ってくるので、以下のように修正
Bundler.require(*Rails.groups) # @see https://github.com/mongoid/mongoid/issues/3455 Moped::BSON = BSON
API でユーザ作成
通常はHTMLでユーザを作るけど、SorceryならAPIで簡単にユーザ作成できる! app/controllers/users_controller.rb
class UsersController < ApplicationController doorkeeper_for :show def create @user = User.new(user_params) if @user.save head :created else head :bad_request end end def show render json: current_user end def user_params params.require(:user).permit(:email, :password, :password_confirmation) end end
これで localhost:9000/users にpost で users[email], users[password], users[password_confirmation] を送れば作れる。
Doorkeeper の関門
ここが最大の難所。 まずちょっと解説する。 Doorkeeper で OAuth 認証をする訳だが、Doorkeeper には Application って概念があって、これは例えば MyAppWeb, MyAppAndroid, MyAppiPhone みたいに分けたり、サードパーティにAPIを提供したりで Application を作る分ける必要がある。それの id と secret とユーザのid, password をセットで送ることでアクセストークンが取得できる。
ここで問題は、DoorkeeperはこのApplicationの作成をHTMLベースで作ること前提でしか作られていない、という点だ。 doorkeeper のソースを見ると、 app/controllers/doorkeeper/applications_controller.rb あたりに書いてある。同様にViewも提供されているので、普通なら問題なく作れる。
ただ、今回はrails-api でrailsアプリを作っているため、レンダリングの仕組みはrequireしていない。もちろんそこで妥協するのも手ではあるが、それではかっこわるすぎる。てことでなんとかAPIでapplicationを作れるようにしなきゃならない。
Doorkeeper 内の applications_controller.rb を再オープン
問題となっているこいつを再定義してやる。具体的には app/controllers/doorkeeper/applications_controller.rb を作成。
module Doorkeeper class ApplicationsController < Doorkeeper::ApplicationController respond_to :json before_filter :authenticate_admin! before_filter :set_application, :only => [:show, :edit, :update, :destroy] def index @applications = Application.all render json: @applications end def create @application = Application.new(application_params) if @application.save render json: {status: "created"} else render json: {status: "failed"} end end ~~~~~~ end end
これで、http://localhost:3000/oauth/applications あてにpost でapplication[name]、application[redirect_uri] を含めて送れば登録できる!
アクセストークン取得
これさえクリアすればゴールは近い。
http://localhost:3000/oauth/token.json へ post で grant_type=password, client_id, client_secret, username, password を入れてやればAccessTokenが取得できる。
取得したアクセストークンで、 http://localhost:3000/users/ にGET で access_tokenパラメータを付けてやれば、 doorkeeper_for を通り抜け、current_user が取って来れるようになる。
あ、ちなみに app/controllers/application_controller.rb はこうする。
class ApplicationController < ActionController::API def current_user @current_user ||= User.find_by_id(doorkeeper_token.resource_owner_id) if doorkeeper_token end end
この先
まずテストコード書くか。そしてJenkins環境を作る。そんでもってJenkinsからHerokuへ自動デプロイするようにして、 vagrantで作ったvmに cap deploy できるようにして本番デプロイ(AWS or さくらVPS)をできるようにする。
そしたらアプリ固有の実装に突入!!