techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

既存 Rails アプリに OAuth 認証を実装する

既存 Rails アプリに OAuth 認証を実装する

Doorkeeper を使った OAuth 2 の実装

Rails Tutorial で作成したアプリケーションに、REST API を実装したいな、と。

doorkeeper-gem_doorkeeper_ Doorkeeper is an OAuth 2 provider for Rails.

$ rails generate doorkeeper:install
Running via Spring preloader in process 22456
      create  config/initializers/doorkeeper.rb
      create  config/locales/doorkeeper.en.yml
       route  use_doorkeeper
===============================================================================

There is a setup that you need to do before you can use doorkeeper.

Step 1.
Go to config/initializers/doorkeeper.rb and configure
resource_owner_authenticator block.

Step 2.
Choose the ORM:

If you want to use ActiveRecord run:

  rails generate doorkeeper:migration

And run

  rake db:migrate

Step 3.
That's it, that's all. Enjoy!

===============================================================================

[timestamp]_create_doorkeeper_tables.rb なマイグレーションファイルが出来上がるので、以下の行を追加。

    add_foreign_key :table_name, :users, column: :resource_owner_id

マイグレーション実行。

$ rails db:migrate
== 20170719212201 CreateDoorkeeperTables: migrating ===========================
-- create_table(:oauth_applications, {})
   -> 0.0112s
-- add_index(:oauth_applications, :uid, {:unique=>true})
   -> 0.0028s
-- create_table(:oauth_access_grants, {})
   -> 0.0010s
-- add_index(:oauth_access_grants, :token, {:unique=>true})
   -> 0.0049s
-- add_foreign_key(:oauth_access_grants, :oauth_applications, {:column=>:application_id})
   -> 0.0000s
-- create_table(:oauth_access_tokens, {})
   -> 0.0008s
-- add_index(:oauth_access_tokens, :token, {:unique=>true})
   -> 0.0007s
-- add_index(:oauth_access_tokens, :resource_owner_id)
   -> 0.0010s
-- add_index(:oauth_access_tokens, :refresh_token, {:unique=>true})
   -> 0.0014s
-- add_foreign_key(:oauth_access_tokens, :oauth_applications, {:column=>:application_id})
   -> 0.0000s
-- add_foreign_key(:table_name, :users, {:column=>:resource_owner_id})
   -> 0.0000s
== 20170719212201 CreateDoorkeeperTables: migrated (0.0257s) ==================

config/initializers/doorkeeper.rbresource_owner_authenticator メソッドを書き換える。(設定より規約、の Rails に設定が増えてしまった。)

  resource_owner_authenticator do
    if (user_id = session[:user_id])
      @user ||= User.find_by(id: user_id)
    else
      session[:forwarding_url] = request.fullpath
      redirect_to(login_url)
    end
  end

上記でフレンドリーフォワーディングにも対応できる。
(ログインしていないユーザーが OAuth 認可を取得しようとしたなら、ログインした後には自分のプロフィールページを表示するのではなく OAuth 認可ができる方がいいよね、というやつ)

これで rails s して /oauth/applications にアクセスすると、アプリケーション一覧画面が表示される。
New Application をクリックして必要な情報を入力すると OAuth ログインに必要な情報が作成され、確認できる。

Authorization code の取得までは可能。
ただ、開発環境で TLS 対応はしておらず動作確認できないのでここから先は Heroku 上で行う。

Heroku へのデプロイ

$ git push heroku
$ heroku pg:reset DATABASE
$ heroku run rails db:environment:set RAILS_ENV=production
$ heroku run rails db:schema:load DISABLE_DATABASE_ENVIRONMENT_CHECK=1
$ heroku run rails db:seed

ここからは以下に沿って動作を確認する。

Testing your provider with OAuth2 gem · doorkeeper-gem_doorkeeper Wiki

$ irb
>> require 'oauth2'
=> true
>> client_id = 'hogehoge'
=> "hogehoge"
>> client_secret = 'hogehoge'
=> "hogehoge"
>> redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
=> "urn:ietf:wg:oauth:2.0:oob"
>> site = "https://hogehoge.herokuapp.com"
=> "https://hogehoge.herokuapp.com"
>> client = OAuth2::Client.new(client_id, client_secret, :site => site)
=> #<OAuth2::Client:0x00000001d8e428 @id="hogehoge", @secret="hogehoge", @site="https://hogehoge.herokuapp.com", @options={:authorize_url=>"/oauth/authorize", :token_url=>"/oauth/token", :token_method=>:post, :auth_scheme=>:request_body, :connection_opts=>{}, :connection_build=>nil, :max_redirects=>5, :raise_errors=>true}>
>> client.auth_code.authorize_url(:redirect_uri => redirect_uri)
=> "https://hogehoge.herokuapp.com/oauth/authorize?client_id= hogehoge&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code"
>> code = "hogehoge"
=> "hogehoge"
>> token = client.auth_code.get_token(code, :redirect_uri => redirect_uri)
=> #<OAuth2::AccessToken:0x000000027e5d90 @client=#<OAuth2::Client:0x00000001d8e428 @id="hogehoge", @secret="hogehoge", @site="https://hogehoge.herokuapp.com", @options={:authorize_url=>"/oauth/authorize", :token_url=>"/oauth/token", :token_method=>:post, :auth_scheme=>:request_body, :connection_opts=>{}, :connection_build=>nil, :max_redirects=>5, :raise_errors=>true}, @auth_code=#<OAuth2::Strategy::AuthCode:0x00000001d7f838 @client=#<OAuth2::Client:0x00000001d8e428 ...>>, @connection=#<Faraday::Connection:0x00000001d7f4f0 @parallel_manager=nil, @headers={"User-Agent"=>"Faraday v0.12.1"}, @params={}, @options=#<Faraday::RequestOptions (empty)>, @ssl=#<Faraday::SSLOptions verify=true>, @default_parallel_manager=nil, @builder=#<Faraday::RackBuilder:0x00000001d7eff0 @handlers=[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp], @app=#<Faraday::Request::UrlEncoded:0x0000000205aeb8 @app=#<Faraday::Adapter::NetHttp:0x0000000205afa8 @app=#<Proc:0x0000000205b0e8@/usr/local/rvm/gems/ruby-2.3.1/gems/faraday-0.12.1/lib/faraday/rack_builder.rb:152 (lambda)>, @connection_options={}, @config_block=nil>>>, @url_prefix=#<URI::HTTPS https://hogehoge.herokuapp.com/>, @proxy=nil>>, @token="hogehoge", @refresh_token=nil, @expires_in=7200, @expires_at=1500699196, @options={:mode=>:header, :header_format=>"Bearer %s", :param_name=>"access_token"}, @params={"token_type"=>"bearer", "created_at"=>1500691996}>

AccessToken が取得できた!

その他

そもそも OAuth 2 じゃなくてよくね。。?(なんとなく OAuth 2 を選択したのがなんか浅はかだったような)

Doorkeeper の Gem はよくできてて簡単ですね。

  • REST API の実装
  • OAuth による上記 API の保護

と進めていく予定。