はじめに
こんにちは!前回の記事の続きになりますが、LINEアカウントIDを取得して個人アプリのユーザーIDと連携させる方法についてまとめていきます。
前回記事はこちら
追記:今回の連携方法はワンタイムコードを用いての方法ですが、LINEログインを実装するとログイン時にuidが送られてきて、そのuidがline_user_idとなるので、今回のこの実装がより簡単になります。
開発環境
・ Ruby on Rails 7.1.3.4・ Ruby 3.4.4
完成物
今回のアプリでは、ユーザーが「通知設定」とメッセージを送ると、ワンタイムコードとリダイレクトURLが返されます。手順
1. ワンタイムコードの生成2. リダイレクトURLの作成
3. IDとワンタイムコードをパラメータに含めたままログイン
4. ワンタイムコードの検証と連携完了
1. ワンタイムコードの生成
ユーザーが公式LINEに「通知設定」と送信すると、時間制限付きのワンタイムコードが生成されます。これによりセキュリティが向上し、データの整合性も保たれます。
今回は、10分で有効期限が切れる4桁のランダムコードを生成します。
1-1. マイグレーションファイルの作成
まずはワンタイムコードを保存できるようにマイグレーションファイルを作成します。 ターミナルで以下のコマンドを実行します。rails g migration CreateOneTimeCodes user_id:bigint code:integer expires_at:datetime
これでワンタイムコードテーブルにコードと有効期限を保持するカラムが作成されました。
usersテーブルにもLINEのユーザーIDを保持できるようにカラムを追加します。
ターミナルで以下のコマンドを実行します。
rails generate migration AddLineUserIdToUsers line_user_id:string
そしてマイグレートします。
rails db:migrate
これでマイグレーションファイルの作成は完了しました。
1-2. メソッドの作成
ワンタイムコードのモデルを作成して、コードを生成するメソッドを記述します。class OneTimeCode < ApplicationRecord
belongs_to :user
validates :code, presence: true
validates :expires_at, presence: true
def generate_unique_code(user_id)
# 一意の識別コードを生成するロジック...
# 1000から9999の間のランダムな整数を生成します:
code = rand(1000..9999)
# 有効期限を設定します。この例では、コードの有効期限を10分後に設定します:
expires_at = 10.minutes.from_now
# 一意のコードとその有効期限をデータベースに保存します:
# 10分後に有効期限が切れるOneTimeCodeを作成
OneTimeCode.create(user_id: user_id, code: code, expires_at: Time.current + 10.minutes)
code
end
end
コントローラを修正、以下を記述します。
class NotificationsController < ApplicationController
# 省略
# 以下のメソッドを追記
private
def user_params
params.require(:user).permit(:unique_code)
end
def handle_one_time_code
if @one_time_code.code == params[:user][:unique_code].to_i && @one_time_code.expires_at && @one_time_code.expires_at > Time.now
link_line_account_or_redirect
elsif @one_time_code.expires_at.nil? || @one_time_code.expires_at <= Time.now
flash.now['danger'] = 'ワンタイムコードの有効期限が切れています。'
render 'notifications/link_line_account', status: :unprocessable_entity
end
end
# ここまで
# 以下のメソッドを修正
def handle_message_event(event)
received_text = event['message']['text']
line_user_id = event['source']['userId']
unless received_text == '連携'
message = {
type: 'text',
text: "アカウント連携をしたい場合は、「連携」とメッセージを送ってください。"
}
return client.reply_message(event['replyToken'], message)
end
unique_code = SecureRandom.hex(10) # ここを追加
# 以下を修正
messages = [
{
type: 'text',
text: "あなたの一意の識別コードは #{unique_code} です。アプリケーションでこのコードを入力してください。このコードの有効期限は10分です。"
},
{
type: 'text',
text: "認証を完了するには、次のリンクをクリックしてください。"
}
]
return client.reply_message(event['replyToken'], messages)
end
def handle_post_request
return redirect_with_alert unless params[:user]
@one_time_code = OneTimeCode.find_by(code: params[:user][:unique_code].to_i)
return render_with_danger if @one_time_code.nil?
handle_one_time_code
end
def handle_one_time_code
if @one_time_code.code == params[:user][:unique_code].to_i && @one_time_code.expires_at && @one_time_code.expires_at > Time.now
link_line_account_or_redirect
elsif @one_time_code.expires_at.nil? || @one_time_code.expires_at <= Time.now
flash.now['danger'] = 'ワンタイムコードの有効期限が切れています。'
render 'notifications/link_line_account', status: :unprocessable_entity
end
end
end
これでワンタイムコードを作成するロジックが完了しました。
2. リダイレクトURLの作成
LINEからアプリに戻るためのリダイレクトURLを作成します。 def handle_message_event(event)
# 省略
uniquw_code = SecureRandom.hex(10)
# 以下を追加
redirect_url = "https://kousiennow.onrender.com/notifications/link_line_account?line_user_id=#{line_user_id}&unique_code=#{unique_code}"
# 省略
end
リダイレクトURLにLINEのユーザーIDと生成されたワンタイムコードをパラメータとして乗せて、アプリにそれらの情報を送信できるようにしています。
ですが、このリダイレクトURLをクリックするとログインを必要とする場合があります。
アカウント連携の際にはユーザーがログインしていることが必須なので、リダイレクトしてからログイン画面に飛ばされると、このままではパラメータのIDとワンタイムコードが失われてしまいます。
なのでその事態を防ぐために、ログイン後パラメータの値も含めたまま該当のページにリダイレクトする設定をしていきます。
3. IDとワンタイムコードをパラメータに含めたままログイン
実はこれはすでに記事にまとめてあります。
上記の記事のように、application_controller.rb に以下を記述していきます。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
add_flash_types :success, :danger
def authenticate_user!
if user_signed_in?
super
else
session[:user_return_to] = request.fullpath # 現在のURL(パラメータを含む)をセッションに保存
redirect_to new_user_session_path, :notice => 'ログインしてください。'
end
end
private
def after_sign_in_path_for(resource)
session.delete(:user_return_to) || root_path # セッションからURLを取り出し、そのURLにリダイレクト
end
end
ログインページにリダイレクトする前のURL(パラメータを含む)をセッションに保存し、ログイン後にそのURLへリダイレクトするようになっています。
4. ワンタイムコードの検証と連携完了
notifications_controllerに以下を追記して、ワンタイムコード検証して、連携完了です!class NotificationsController < ApplicationController
# 省略
def link_line_account
@user = User.find(current_user.id)
return handle_get_request if request.get?
return handle_post_request if request.post?
end
private
# 省略
def handle_get_request
@one_time_code = OneTimeCode.find_by(code: params[:unique_code].to_i)
@unique_code = params[:unique_code]
end
def handle_post_request
return redirect_with_alert unless params[:user]
@one_time_code = OneTimeCode.find_by(code: params[:user][:unique_code].to_i)
return render_with_danger if @one_time_code.nil?
handle_one_time_code
end
def redirect_with_alert
flash[:alert] = 'フォームを正しく送信してください。'
redirect_to link_line_account_notifications_path
end
def render_with_danger
flash.now['danger'] = 'ワンタイムコードが見つかりません。'
render 'notifications/link_line_account', status: :unprocessable_entity
end
def handle_one_time_code
if @one_time_code.code == params[:user][:unique_code].to_i && @one_time_code.expires_at && @one_time_code.expires_at > Time.now
link_line_account_or_redirect
elsif @one_time_code.expires_at.nil? || @one_time_code.expires_at <= Time.now
flash.now['danger'] = 'ワンタイムコードの有効期限が切れています。'
render 'notifications/link_line_account', status: :unprocessable_entity
end
end
def link_line_account_or_redirect
if @user.line_user_id.nil?
@user.line_user_id = params[:line_user_id]
if @user.save
flash[:notice] = 'LINEアカウントが正常にリンクされました。'
redirect_to profile_path
else
flash[:alert] = 'LINEアカウントのリンクに失敗しました。'
render 'notifications/link_line_account'
end
else
flash[:notice] = 'LINEアカウントは既にリンクされています。'
redirect_to profile_path
end
end
# 省略
end
link_line_account で、ユーザーがLINEアカウントをアプリケーションのアカウントにリンクするためのエンドポイントを設定しています。
handle_get_request は、ユーザーがリダイレクトURLを使用してアプリに戻ってきたときに呼び出されます。
リダイレクトURLのパラメータに含まれていたワンタイムコードを取得し、それをデータベースから検索します。
そして、ユーザーが入力した値とデータベースのワンタイムコードが正しいかどうかを、handle_post_request で検証します。
検証の内容は、データベースにそのコードが存在する場合、コードが正しいか、有効期限が切れていないかを確認します。
そしてこれらの条件が満たされている場合、ユーザーのアカウント連携が完了します。
お疲れ様でした!
最後に
最後までお読みいただきありがとうございます!アプリからLINEにパラメータをつけてユーザーIDを送っても受け取ることができないとCopilotに言われたので、LINEからLINEのユーザーIDをつけてアプリにリダイレクトさせるようにしました。
ワンタイムコードを使用することでセキュリティの向上も見込めて、簡単に連携が完了するのでLINEアカウント連携を考えている方はぜひやってみてください!
備忘録ですが、参考になれば幸いです。
初学者ゆえ、誤解や説明不足があるかもしれませんが、ご指摘いただければ幸いです。