ログインする時は、間口を広げるため緩い権限で、必要なときに追加で認証させたい、というニーズはわりとある。
でもOmniAuthはScopeをinitializerで指定しちゃうよなぁ、どうするのかな、と思ってたら、Setup Phase なるものがちゃんと用意されてた。知らなかった…。

例)Public Onlyでログインして、後からGistの権限を要求する

まじめにやる場合は、http://www.createdbypete.com/articles/dynamic-omniauth-provider-setup/ みたいに、Rackアプリを作るのが綺麗っぽい。
ここでは、手を抜いて、SessionsController#setupを使う。(参考:http://mikepackdev.com/blog_posts/2-dynamically-requesting-facebook-permissions-with-omniauth)

config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], setup: true # setup: trueが必要
end
cat config/routes.rb
get '/auth/:provider/callback', to: 'sessions#create'
get '/auth/:provider/setup', to: 'sessions#setup' # いつものに加えてこの行を追加
app/controllers/sessions_controller.rb
def create
    # 他の処理

    # TODO: もっと綺麗な条件で分岐したい
    if request.env['omniauth.params'].try(:[], 'scope') == 'gist'
      # gist向けの処理
    end

    # 他の処理
end

def setup
  if params[:scope] == 'gist'
    # 今回は元のscopeが無しなので単純に代入
    request.env['omniauth.strategy'].options[:scope] = 'gist'
  end
  render nothing: true, status: 404 # setupで404を返せばその後の処理はいつも通りやってくれる
end

これで、

  • /auth/githubはscope無しの認証
  • /auth/github?scope=gistはGistの権限を要求

が実現できた。