Help us understand the problem. What is going on with this article?

OmniAuth(OAuth2)処理をキックしたときのパラメータを認証用リクエストにも渡したい

More than 3 years have passed since last update.

OmniAuth(OAuth2)処理をキックしたときのパラメータを認証用リクエストにも渡したい

はじめに

RailsアプリにOAuth2認証を導入する場合に利用できるOmniAuthがあります。
TwitterやGitHubなどの外部認証用にすでにいくつもStrategyが用意されています。

リストにない場合は自作することになりますが、認証サービス用のStrategyを作成時にはまった点を紹介します。

特にOmniAuth処理をキックされたときの動的なパラメータを扱う点は、検索しても情報が見つけられなかったので苦労しました。
参考になれば幸いです。

ここで取り上げるポイント

  • OAuth用のURLを変更する
  • callback_urlの注意点(デフォルトのままだとエラーになってしまうパターン)
  • OmniAuth処理をキックされたときのパラメータを認証時のパラメータとして渡す
  • OmniAuth処理をキックされたときのパラメータをapi呼び出し結果に格納する

OAuth2の処理の流れはこちらをご覧ください。(とてもわかりやすかったです)
「OmniAuth OAuth2 を使って OAuth2 のストラテジーを作るときに知っていると幸せになれるかもしれないこと」

OmniAuthでOAuth2用のカスタムStrategyを作成

前提

今回は架空のhogeというOAuth2認証サービス用のstrategyを作成します。

API側

サイト https://hoge.xxx.xxx
認可エンドポイント https://hoge.xxx.xxx/oauth/authorize
認可時に渡すカスタムパラメータ group_id=9876543210
api利用用に登録したRedirect URL https://myapp.localhost/sessions/callback
呼び出すapi https://hoge.xxx.xxx/api/user_information
apiの戻り値 json形式※1
(※1)apiの戻り値
{
  "user_id" : "012345",
  "first_name" : "Firstname",
  "last_name" : "Lastname",
  "email" : "test@localhost"
}

OAuthを利用するWebアプリ側

サイト https://myapp.localhost
OAuth用リクエストパス https://myapp.localhost/sessions/auth
OAuth用コールバックパス https://myapp.localhost/sessions/callback

OAuth認証時に呼び出すリクエストパスと、OAuth認証結果を受け取るコールバックパスはデフォルトのパスから変更しています。

変更前(デフォルト) 変更後
https://myapp.localhost/auth/hoge https://myapp.localhost/sessions/auth
https://myapp.localhost/auth/hoge/callback https://myapp.localhost/sessions/callback

1. カスタムStrategyクラスを作成する

lib/omniauth/strategies/hoge.rb

を作成します。

https://github.com/intridea/omniauth-oauth2 などをひな形にして次のようなファイルを作成します。

lib/omniauth/strategies/hoge.rb
require 'omniauth-oauth2'
module OmniAuth
  module Strategies
    class Hoge < OmniAuth::Strategies::OAuth2
      # Give your strategy a name.
      option :name, "hoge"

      # This is where you pass the options you would pass when
      # initializing your consumer from the OAuth gem.
      option :client_options, { :site => 'https://myapp.localhost' }

      # These are called after authentication has succeeded. If
      # possible, you should try to set the UID without making
      # additional calls (if the user id is returned with the token
      # or as a URI parameter). This may not be possible with all
      # providers.
      uid{ raw_info['user_id'] }

      info do
        {
          # JSON結果を加工しておける
          user_id: raw_info['user_id'],
          first_name: raw_info['first_name'],
          last_name: raw_info['last_name'],
          email: raw_info['email'],
          # リクエストパラメータも取得できる
          group_id: request.env['omniauth.params']['group_id']
        }
      end

      extra do
        {
          'raw_info' => raw_info
        }
      end

      def raw_info
        @raw_info ||= JSON.load(access_token.get('/api/user_information').body)
      end

      # リクエストにカスタムパラメータを追加
      def request_phase
        _authorize_params = { :redirect_uri => callback_url }.merge(authorize_params)
        _authorize_params.merge!({ group_id: request.env['omniauth.params']['group_id'] })
        redirect client.auth_code.authorize_url(_authorize_params)
      end

      # リダイレクトURLを固定(デフォルトではクエリパラメータが付加されるため)
      def callback_url
        full_host + script_name + callback_path
      end
    end
  end
end

以下、それぞれの説明です。

1.1 callback_urlの注意点

def callback_url
  full_host + script_name + callback_path
end

ここでcallback_urlをオーバーライドしていますが、理由があります。

callback_urlはデフォルトでクエリパラメータが付加されるため、OmniAuth2::Client#request でエラーになる可能性があります。(登録されているアプリと違うと判断されるため)

original
def callback_url
  full_host + script_name + callback_path + query_string
end

Hoge#callback_urlでquery_stringが付加されない形に上書きしています。

1.2 OAuth認証時に動的にベンダー用カスタムパラメータを渡す

def request_phase
  _authorize_params = {:redirect_uri => callback_url}.merge(authorize_params)
  _authorize_params.merge!({ group_id: request.env['omniauth.params']['group_id'] })
  redirect client.auth_code.authorize_url(_authorize_params)
end

https://hoge.xxx.xxx/oauth/authorize?group_id=9876543210のようにOAuth認証にパラメータを追加したい場合の対応です。

固定値を渡す場合は、

lib/omniauth/strategies/hoge.rb
:
option :authorize_params, { group_id: '9876543210' }
:

のように書いておけば良いのですが、動的に値を変えたい場合はどうすれば良いのでしょう?
このパラメータ(group_id)を、OAuth認証をキックするときに受け取るようにします。

https://myapp.localhost/sessions/auth?group_id=9876543210

OmniAuthでは、/sessions/auth呼び出し時のパラメータが
request.env['omniauth.params']
に格納されています。

今回の例では、request.env['omniauth.params']['group_id']となります。
このパラメータをOAuthリクエストパラメータに追加すればいいわけです。

パラメータを追加すべき処理は
OmniAuth::Strategies::OAuth2#request_phase
になります。

original
def request_phase
  redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
end

Hoge#request_phaseでパラメータを追加する形で上書きします。

この変更により
https://hoge.xxx.xxx/oauth/authorize?group_id=9876543210
のようにパラメータが渡されます。

1.3 api呼び出し結果を取得する

def raw_info
  @raw_info ||= JSON.load(access_token.get('/api/user_information').body)
end

認可されたaccess_tokenを使ってapiを呼び出します。
今回は結果がjson形式で受け取れることを想定しているので、parseして@raw_infoに格納します。

1.4 結果をHashにセットする

uid{ raw_info['user_id'] }

info do
  {
    # JSON結果を加工しておける
    user_id: raw_info['user_id'],
    first_name: raw_info['first_name'],
    last_name: raw_info['last_name'],
    email: raw_info['email'],
    # リクエストパラメータも取得できる
    group_id: request.env['omniauth.params']['group_id']
  }
end

extra do
  {
    'raw_info' => raw_info
  }
end

OmniAuthによるapi呼び出し結果(OmniAuth::AuthHash)はrequest.env['omniauth.auth']に格納されます。
次のように結果にアクセスできるので、その値をそれぞれ設定しています。

request.env['omniauth.auth'].uid
request.env['omniauth.auth'].info
request.env['omniauth.auth'].extra
request.env['omniauth.auth'].credentials # tokenに関する情報(今回上書きしてません)

このHashにはOmniAuth呼び出し時のパラメータも設定できます。

group_id: request.env['omniauth.params']['group_id']

2. HogeクラスをOmniAuthのProviderに設定する

OmniAuthがHogeを利用できるように config/initializers/omniauth.rb を作成します。

config/initializers/omniauth.rb
require 'omniauth/strategies/hoge'

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :hoge, ENV['KEY'], ENV['SECRET'],
    :request_path => '/sessions/auth', :callback_path => '/sessions/callback'
end

2.1 request_pathとcallback_pathを変更する

  provider :hoge, ENV['KEY'], ENV['SECRET'],
    :request_path => '/sessions/auth', :callback_path => '/sessions/callback'

namespaceを使っている場合など、認証用のパスと戻り値を受け取るパスを変更したい場合があります。
今回の設定では次のようにパスを変更しています。

対象 変更前(デフォルト) 変更後
requrest_path /auth/hoge /sessions/auth
callback_path /auth/hoge/callback /sessions/callback

3. 認証結果を元にユーザーを作成する

今回はSessionsController#createで認証結果を受け取ります。

app/controllers/sessions_controller.rb
:
def create
  user = User.find_or_create_from_auth_hash(auth_hash)
  :
end

private

def auth_hash
  request.env['omniauth.auth'].info
end
:

先程説明したように、apiの呼び出し結果はrequest.env['omniauth.auth']で取得できます。
必要な情報はinfoで取得できるようにしてあるので、request.env['omniauth.auth'].infoの値を利用してUserを作成します。

app/models/user.rb
:
def self.find_or_create_from_auth_hash(auth_information)
  user = User.find_or_initialize_by(user_id: auth_information[:user_id])
  user.email = auth_information[:email]
  :
  user.save!
  user
end
:

最後に

OAuth2について詳しく知りたい場合は、

  • OmniAuth::Strategy
  • OmniAuth::Strategies::OAuth2

のコードを読むと良いかと思います。(いろいろ見えてきます)

参考

t_oginogin
お気に入りは、Ruby、Ruby on Rails
http://t-oginogin.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away