1. はじめに
Rails8でLINEログイン機能を実装する際に遭遇したエラーと解決方法を記録します。
うまく動かない...という方の参考になれば幸いです。
経緯
既存のomniauth-line gemを調べてみたところ、長期間更新されておらず Rails8での動作に不安があったため、今回は独自のOmniAuth strategyを作成してLINEログインを実装する方法を選択しました。
実装については下記の記事を元に実装しました。
開発環境
・Ruby 3.3.6
・Rails 8.0.2.1
・ 認証: Devise
・Docker(開発環境)
・DB: PostgreSQL
・画像管理: Cloudinary
・デプロイ: Render / Neon
実際の実装内容
# LINEログイン
gem 'omniauth'
gem 'omniauth-oauth2'
gem 'omniauth-rails_csrf_protection'
lib/omni_auth/strategies/line.rb(クリックで展開)
require 'omniauth-oauth2'
module OmniAuth
module Strategies
class Line < OmniAuth::Strategies::OAuth2
# プロバイダー名
option :name, 'line'
# IDトークン, プロフィール情報, メールアドレスの取得権限を含める
option :scope, 'openid profile email'
# LINEのAPIエンドポイント設定
option :client_options, {
site: 'https://api.line.me',
authorize_url: 'https://access.line.me/oauth2/v2.1/authorize',
token_url: 'https://api.line.me/oauth2/v2.1/token'
}
# LINEユーザーIDをuidとして設定
uid { raw_info['sub'] }
# ユーザー情報を設定
info do
{
name: raw_info['name'],
email: raw_info['email']
}
end
# 追加情報(デバッグ用)
extra do
{
raw_info: raw_info
}
end
def raw_info
@raw_info ||= verify_id_token
end
private
# ID Tokenに必須のnonceをパラメータに追加
def authorize_params
super.tap do |params|
params[:nonce] = SecureRandom.uuid
session['omniauth.nonce'] = params[:nonce]
end
end
# callback_urlのクエリパラメータを除去
def callback_url
full_host + callback_path
end
# ID token検証 & ユーザ情報取得
def verify_id_token
@id_token_payload ||= begin
# HTTPクライアントを使用してID Token検証
response = client.request(:post, 'https://api.line.me/oauth2/v2.1/verify', {
body: {
id_token: access_token['id_token'],
client_id: options.client_id,
nonce: session.delete('omniauth.nonce')
}
})
response.parsed
rescue => e
Rails.logger.error "[LINE Login] ID token verification failed: #{e.message}"
Rails.error.report(e, context: {
action: '[LINE login] ID token verification & get user info',
client_id: options.client_id,
has_id_token: access_token['id_token'].present?
}) if defined?(Rails.error)
raise
end
end
end
end
end
app/models/user.rb 追加(クリックで展開)
def self.from_omniauth(auth, current_user = nil)
# ログイン済みユーザーがLINE連携を追加する場合
return link_line_account(auth, current_user) if current_user && !current_user.line_connected?
# 既存のLINEユーザーでログインまたは新規ユーザー作成
sign_in_or_create_user_from_line(auth)
end
# 既存ユーザーにLINE連携を追加
def self.link_line_account(auth, current_user)
# 他のユーザーが既にこのLINEアカウントを使用していないかチェック
return nil if exists?(provider: auth.provider, uid: auth.uid)
success = current_user.update(
provider: auth.provider,
uid: auth.uid,
line_notify: true
)
success ? current_user : nil
end
# LINEログインまたは新規ユーザー作成
def self.sign_in_or_create_user_from_line(auth)
find_or_create_by(
provider: auth.provider,
uid: auth.uid
) do |user|
user.email = auth.info.email || "#{auth.uid}@line.example.com"
user.name = auth.info.name || "LINE User"
user.password = Devise.friendly_token[0, 20]
user.line_notify = true
end
end
app/controllers/users/omniauth_callbacks_controller.rb(クリックで展開)
module Users
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token, only: :line
def line
@user = User.from_omniauth(request.env['omniauth.auth'], current_user)
notify_line_already_linked and return if current_user && @user.nil?
if @user.persisted?
complete_line_login
else
fail_line_login
end
end
private
def notify_line_already_linked
redirect_to user_setting_path
set_flash_message(:alert, :failure, kind: 'LINE', reason: '他アカウントでLINE連携済みです')
end
def complete_line_login
sign_in_and_redirect @user, event: :authentication
set_flash_message(:notice, :success, kind: 'LINE')
end
def fail_line_login
session['devise.line_data'] = request.env['omniauth.auth'].except(:extra)
redirect_to new_user_registration_url
set_flash_message(:alert, :failure, kind: 'LINE', reason: 'LINE連携に失敗しました')
end
end
end
LINE_CHANNEL_ID=チャネルID
LINE_CHANNEL_SECRET=チャネルシークレット
class AddOmniauthToUsers < ActiveRecord::Migration[8.0]
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
# 重複しないように
add_index :users, [:provider, :uid], unique: true
end
end
# 下記の内容を追加
require Rails.root.join('lib/omni_auth/strategies/line')
...
config.omniauth :line, ENV['LINE_CHANNEL_ID'], ENV['LINE_CHANNEL_SECRET']
【エラー1】 本番環境でのZeitwerkエラー
エラー内容
ローカル環境では正常に動作するが、Renderへのデプロイ時に以下のエラーが発生:
/opt/render/project/.gems/ruby/3.3.0/gems/zeitwerk-2.7.3/lib/zeitwerk/cref.rb:62:in `const_get': uninitialized constant Omniauth::Strategies::Line (NameError)
原因
Rails8の「Zeitwerk」が、自作したlib/omniauth/strategies/line.rb を正しく読み込めていなかった。
Zeitwerkの命名規則:
・OmniAuth → omni_auth (スネークケース)
・omniauth だと Omniauth クラスを期待してしまう
・Rails8ではこの規則がより厳格に適用される
解決方法
- lib/omniauth/strategies/line.rb
+ lib/omni_auth/strategies/line.rb
これによりRails8 の Zeitwerk がモジュール名 OmniAuthを探せてエラーが解消
【エラー2】 コールバック時の404エラー
エラー内容
LINEログイン後のコールバック処理で以下のエラーが発生:
[77d87eba-2df9-4c5f-8e76-1659254d9613] Processing by Users::OmniauthCallbacksController#passthru as HTML
[77d87eba-2df9-4c5f-8e76-1659254d9613] Completed 404 Not Found in 1ms
エラーの症状
- LINEログインボタンをクリック → 404エラー
- ログを見ると
#passthruアクションが呼ばれている
エラーの原因分析
#passthru が呼ばれるということは、
OmniAuth がコールバックリクエストを受け付けていない ということ。
OmniAuth 2.x で導入された allowed_request_methods の制限
- デフォルトで POST のみ許可
- LINE のコールバックは GET を利用するため 404 になる
4. 解決方法
# 追加 ファイルがない場合作成要
Rails.application.config.middleware.use OmniAuth::Builder do
OmniAuth.config.allowed_request_methods = [:post, :get]
end
これにより、Rails のミドルウェアに OmniAuth を追加して、明示的にgetリクエストを許可し、エラー解消
まとめ
Rails8 でOmniAuth strategyを作成してLINEログインを実装する方法についての参考記事が少ないので、今回振り返りも含めて記事にしました
