Edited at

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

More than 1 year has 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

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


参考