バックエンドをRailsのAPI専用モード、フロントをAngular2/4でSPA構築時、OAuth認証でFacebookログインをしようとした時に遭遇した不具合対応記録


前提

  • Angular2/4側は特に不具合がなかったので説明を割愛します。API側のみの説明になります。
    • → ちなみにangular2-token.signInOAuth()メソッドを使用して実装しました。
  • 基本的に下記記事を参考にAPIを構築しております。そのため、今回遭遇した不具合に対し行った対策となる差異部分だけを記述していく形にします。
  • 使用gem
gem 'rack-cors'

gem 'devise_token_auth'
gem 'omniauth'
gem 'omniauth-oauth2'
gem 'omniauth-facebook'

不具合

1. API側でFacebookのOAuth認証完了後、認証情報とユーザー情報を返す際、フロント側で認証用のポップアップウィンドウを閉じることが出来ない、かつ、送られてきた情報を取得することができない。

不具合発生の流れ

  1. angular2-token.signInOAuth()メソッドで認証用のポップアップウィンドウがhttp://localhost:3000/auth/facebookのURLで開かれる。
  2. API側がFacebookに問い合わせをし、Facebookの認証画面が返却され、ポップアップウィンドウに表示される。
  3. 認証完了後、API側でユーザー認証をし、ユーザー情報と認証情報を返却する。

不具合の原因

不具合の流れの3で返却された情報がjson形式のため、angular2-token.signInOAuth()メソッドではハンドリングできない。

不具合の解決方法

まず、angular2-token.signInOAuth()メソッドで返却された情報をハンドリングするためには、下記対象箇所で行っているRequest.credentialsのイベントをポップアップウィンドウ側でキャッチしなければ行けない。
対象箇所

実はgemのdevise_token_authで上記内容に対応するhtmlが用意されている。
そのため、レスポンスに上記htmlを返すようにするだけで良い。

app/controllers/users/omniauth_callbacks_controller.rb
module Users
  class OmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController
    include Devise::Controllers::Rememberable

    def omniauth_success
      get_resource_from_auth_hash
      create_token_info
      set_token_on_resource
      create_auth_params

      sign_in(:user, @resource, store: false, bypass: false)

      @resource.save!
      # update_token_authをつけることでレスポンスヘッダーに認証情報を付与できる。
      update_auth_header
      yield @resource if block_given?

      render_data_or_redirect('deliverCredentials', @auth_params.as_json, @resource.as_json)
    end

    protected

    def render_data(message, data)
      @data = data.merge({
        message: message
      })

      html = File.open("app/views/devise_token_auth/omniauth_external_window.html.erb").read
      template = ERB.new(html).result(binding)
      render html: template.html_safe
    end

end

↑のomniauth_external_window.html.erbdevise_token_authgems/devise_token_auth-0.1.42/app/views/devise_token_auth/omniauth_external_window.html.erbをファイル指定しやすいようにapp/views/devise_token_authにコピーしただけのもので、内容は同じものとなります。

2. ポップアップウィンドウにFacebook認証画面が表示されている時、「後で」等で認証をキャンセルした際、エラーが発生する。

不具合発生の流れ

不具合の原因

認証をキャンセルすると本来パラメータに存在すべきキーのcodeが存在しないため、ミドルウェア段階でハンドリングされて、Devise::OmniauthCallbacksController#failureのアクションが走る。この時、flashを使用しようする記述があるが、そもそも定義されていないのでエラーとなる。

不具合の解決方法

Devise::OmniauthCallbacksControllerfailureをオーバーライドし、omniauth_successのようにomniauth_external_window.html.erbを返すようにする。

下記ファイルを作成

config/initializers/devise.rb
Rails.application.config.to_prepare do
  Devise::OmniauthCallbacksController.class_eval do
    def failure
      @data = {
        message: 'authFailure'
      }
      html = File.open("app/views/devise_token_auth/omniauth_external_window.html.erb").read
      template = ERB.new(html).result(binding)
      render html: template.html_safe
    end
  end
end

本来ならUsers::OmniauthCallbacksControlleromniauth_failureをオーバーライドし、上記処理を定義したかったのですが、ミドルウェア段階でハンドリングされているらしく(詳しく処理を追えてない)暫定的な形で対応してしまいました。
もし方法がわかる方いましたらアドバイス頂けると幸いです。

その他

ここに載せていないAPIorAngular2/4側の設定だったり、記述の仕方を知りたい方は気軽にご連絡頂ければと思います。