suzuyu0115 / meshitelog

8 stars 0 forks source link

LI NE認証機能のルーティングエラーについて #33

Closed suzuyu0115 closed 1 year ago

suzuyu0115 commented 1 year ago

質問内容、実現したいこと

LINE認証機能のルーティングエラーを解消し、LINE認証機能を実現したい

現在発生している問題・エラーメッセージ

ローカル環境でLINE認証ボタンを押すと、/auth/lineというルートがないとエラーが発生してしまう ngrokを使用して動作確認しても同様のエラーが発生しました。

dc1c6791a2d49babf33fbdade2680048

該当のソースコード

GPT-4に提案された流れで実装しましたので、流れを記載します。

1. Gemのインストール

Gemfileに以下の行を追加してからbundle installを実行します。

gem 'omniauth'
gem 'omniauth-line'

2. LINE Developersでの設定

次に、LINE Developersにログインし、プロバイダを作成します。プロバイダ作成後、チャネルを作成します("LINEログイン"を選択)。

3.OmniAuthの設定

config/initializers/omniauth.rb ファイルを作成し、以下のように記述します。

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :line, ENV['LINE_CHANNEL_ID'], ENV['LINE_CHANNEL_SECRET']
end

ここで、 LINE_CHANNEL_ID と LINE_CHANNEL_SECRET は、先程LINE Developersで作成したチャネルの詳細ページから取得できます。これらの値は公開すべきではないため、環境変数を通して設定します。

4. ユーザーモデルとセッションの作成

ユーザーのログイン状態を管理するために、User モデルとセッションコントローラを作成します。以下は一例です:

# app/models/user.rb
class User < ApplicationRecord
  def self.find_or_create_from_auth(auth)
    provider = auth[:provider]
    uid = auth[:uid]
    name = auth[:info][:name]

    user = self.find_or_initialize_by(provider: provider, uid: uid)
    user.name = name
    user.save ? user : nil
  end
end
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    user = User.find_or_create_from_auth(request.env['omniauth.auth']) # OmniAuthから取得した情報を使ってユーザーを検索(なければ作成)
    session[:user_id] = user.id # セッションにユーザーIDを保存
    redirect_to root_path, notice: 'ログインしました'
  end

  def destroy
    reset_session # セッションをリセット
    redirect_to root_path, notice: 'ログアウトしました'
  end
end

5. ルーティングの設定

認証のリクエストとコールバック用のルーティングを設定します。

# config/routes.rb
Rails.application.routes.draw do
  get '/auth/:provider/callback', to: 'sessions#create'
  delete '/logout', to: 'sessions#destroy'
  # 他のルーティング
end

6. 認証ボタンの追加

最後に、ビューに認証用のボタンを追加します。

<%= link_to "LINEでログイン", "/auth/line", class: "btn btn-primary" %>

7. セッションを利用したログイン状態の管理

ログインしたユーザーの情報を保存し、それによってログイン状態を制御する方法として、セッションを利用します。セッションはブラウザが閉じられるまで維持されるので、これにユーザーIDを保存すれば、ブラウザが閉じられるまではそのユーザーがログインした状態を保つことができます。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  helper_method :current_user

  private

  def current_user
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
  end
end

上記のコードでは、current_userメソッドを定義しています。これを使用することで、現在ログインしているユーザー(もしあれば)を取得することができます。helper_methodを使用しているので、これはビューでも使用できます。たとえば、ログインしている場合はユーザーの名前を、ログインしていない場合はログインボタンを表示する、といったことが可能です。

以上が、LINEの認証を使ってRailsアプリケーションにログイン機能を追加する基本的な手順です。しかし、これはあくまで基本的な手順であり、具体的な実装はアプリケーションの要件によって変わる可能性があります。セキュリティ対策なども含めて適切に実装を行ってください。

エラーから考えられる原因

/auth/lineのルーティングが定義されていないのが原因。 しかし、gem omniauth-lineによって、上記のルーティングはよしなにLINE認証ページへ飛ばしてくれるはずそうなので、八方塞がりな状態です。

以下、GPTの抜粋

"/auth/line"はOmniAuthが自動的に生成するルートで、直接routes.rbに定義する必要はありません。

OmniAuthは、初期設定ファイル(config/initializers/omniauth.rb)で定義したプロバイダー名(この場合は :line)をもとに、自動的に認証リクエストのルートを生成します。そのため、"/auth/line"へのリクエストはOmniAuthによって処理され、LINEの認証ページへリダイレクトされます。

試したこと

参考

https://github.com/omniauth/omniauth https://github.com/kazasiki/omniauth-line https://github.com/cookpad/omniauth-rails_csrf_protection

また、実装が滞った場合、下記記事のDeviceを用いたLINE認証機能の実装方法も視野に入れようかと考えております。 https://qiita.com/s10aim_tana/items/2d174d4e31e4041700ee

以上、よろしくお願いいたします🙇‍♂️

kerochelo commented 1 year ago

@suzuyu0115 rails routesの一覧も共有いただいてよろしいでしょうか?

suzuyu0115 commented 1 year ago

@kerochelo コメントありがとうございます! こちらになります。

$ rails routes
                                  Prefix Verb   URI Pattern                                                                                       Controller#Action
                                    root GET    /                                                                                                 static_pages#top
                      auth_line_callback GET    /auth/line/callback(.:format)                                                                     sessions#create
                                  logout DELETE /logout(.:format)                                                                                 sessions#destroy
        turbo_recede_historical_location GET    /recede_historical_location(.:format)                                                             turbo/native/navigation#recede
        turbo_resume_historical_location GET    /resume_historical_location(.:format)                                                             turbo/native/navigation#resume
       turbo_refresh_historical_location GET    /refresh_historical_location(.:format)                                                            turbo/native/navigation#refresh
           rails_postmark_inbound_emails POST   /rails/action_mailbox/postmark/inbound_emails(.:format)                                           action_mailbox/ingresses/postmark/inbound_emails#create
              rails_relay_inbound_emails POST   /rails/action_mailbox/relay/inbound_emails(.:format)                                              action_mailbox/ingresses/relay/inbound_emails#create
           rails_sendgrid_inbound_emails POST   /rails/action_mailbox/sendgrid/inbound_emails(.:format)                                           action_mailbox/ingresses/sendgrid/inbound_emails#create
     rails_mandrill_inbound_health_check GET    /rails/action_mailbox/mandrill/inbound_emails(.:format)                                           action_mailbox/ingresses/mandrill/inbound_emails#health_check
           rails_mandrill_inbound_emails POST   /rails/action_mailbox/mandrill/inbound_emails(.:format)                                           action_mailbox/ingresses/mandrill/inbound_emails#create
            rails_mailgun_inbound_emails POST   /rails/action_mailbox/mailgun/inbound_emails/mime(.:format)                                       action_mailbox/ingresses/mailgun/inbound_emails#create
          rails_conductor_inbound_emails GET    /rails/conductor/action_mailbox/inbound_emails(.:format)                                          rails/conductor/action_mailbox/inbound_emails#index
                                         POST   /rails/conductor/action_mailbox/inbound_emails(.:format)                                          rails/conductor/action_mailbox/inbound_emails#create
       new_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/new(.:format)                                      rails/conductor/action_mailbox/inbound_emails#new
      edit_rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id/edit(.:format)                                 rails/conductor/action_mailbox/inbound_emails#edit
           rails_conductor_inbound_email GET    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#show
                                         PATCH  /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#update
                                         PUT    /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#update
                                         DELETE /rails/conductor/action_mailbox/inbound_emails/:id(.:format)                                      rails/conductor/action_mailbox/inbound_emails#destroy
new_rails_conductor_inbound_email_source GET    /rails/conductor/action_mailbox/inbound_emails/sources/new(.:format)                              rails/conductor/action_mailbox/inbound_emails/sources#new
   rails_conductor_inbound_email_sources POST   /rails/conductor/action_mailbox/inbound_emails/sources(.:format)                                  rails/conductor/action_mailbox/inbound_emails/sources#create
   rails_conductor_inbound_email_reroute POST   /rails/conductor/action_mailbox/:inbound_email_id/reroute(.:format)                               rails/conductor/action_mailbox/reroutes#create
rails_conductor_inbound_email_incinerate POST   /rails/conductor/action_mailbox/:inbound_email_id/incinerate(.:format)                            rails/conductor/action_mailbox/incinerates#create
                      rails_service_blob GET    /rails/active_storage/blobs/redirect/:signed_id/*filename(.:format)                               active_storage/blobs/redirect#show
                rails_service_blob_proxy GET    /rails/active_storage/blobs/proxy/:signed_id/*filename(.:format)                                  active_storage/blobs/proxy#show
                                         GET    /rails/active_storage/blobs/:signed_id/*filename(.:format)                                        active_storage/blobs/redirect#show
               rails_blob_representation GET    /rails/active_storage/representations/redirect/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations/redirect#show
         rails_blob_representation_proxy GET    /rails/active_storage/representations/proxy/:signed_blob_id/:variation_key/*filename(.:format)    active_storage/representations/proxy#show
                                         GET    /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format)          active_storage/representations/redirect#show
                      rails_disk_service GET    /rails/active_storage/disk/:encoded_key/*filename(.:format)                                       active_storage/disk#show
               update_rails_disk_service PUT    /rails/active_storage/disk/:encoded_token(.:format)                                               active_storage/disk#update
                    rails_direct_uploads POST   /rails/active_storage/direct_uploads(.:format)                                                    active_storage/direct_uploads#create
kerochelo commented 1 year ago

@suzuyu0115 ありがとうございます。 /auth/line自体のルーティングはなさそうですね

ChatGPTの返答があっていない可能性が高いので、omniauthのgemをもう一度確認して、ルーティングが自動で作成されるのか調査してみてください。 omniauthにそのような機能がなければ自分でルーティングを設定して必要な処理を実装する必要があるかと思います!

この部分をroute.rbに記載しているので、自分で追記する必要があるかもの気がします

get '/auth/:provider/callback', to: 'sessions#create'

ちなみにGET /auth/lineはどんなページになる感じですかね?

suzuyu0115 commented 1 year ago

/auth/lineは、認証のエンドポイントで、ユーザーの認証プロセスにリダイレクトされる仕様となっているそうです。 自動生成されるルーティングで、routes.rbへの記載は不要らしいです。

/auth/line というパスは、OmniAuthが提供する認証のエンドポイントです。このエンドポイントを訪れると、ユーザーはLINEの認証プロセスにリダイレクトされます。

具体的には次のような流れになります:

ユーザーがアプリケーションの "LINEでログイン" ボタンをクリックし、 /auth/line へリダイレクトされます。

OmniAuth(LINEプロバイダー)がアクティブ化され、ユーザーはLINEの認証ページへリダイレクトされます。

ユーザーはLINEの認証ページでログインし、アクセスを許可します。

ユーザーはアプリケーションへリダイレクトされます。その際、OmniAuthが提供するコールバックURL(例: /auth/line/callback)に対してLINEから認証情報(uid, nameなど)が送信されます。

なお、 /auth/line へ直接アクセスしても、アプリケーション自体のページが表示されるわけではなく、LINEのログインページにリダイレクトされます。したがって、通常このパスには直接的なビューは関連付けられていません。

公式なども今一度目を通したのですが、原因解明できず。。。 他のやり方を模索するべきでしょうか?

kerochelo commented 1 year ago

@suzuyu0115 初期化時に下記を追加してあげないといけないかもです。

OmniAuth.config.allowed_request_methods = [:get]

とはいえGETは推奨していなさそうなので、logなどに情報が出るかもなので試してみてください! https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284

suzuyu0115 commented 1 year ago

いただいたコードを追加したところ、無事認証機能を実装することができました! ありがとうございます!!!🙇‍♂️