はじめに
スマートフォンアプリなどでよくある
「メールアドレスを入力 → 認証コードを受信 → 入力して認証」
という仕組みを、自前のRails APIで構築する方法を紹介します。
今回はその前編として、バックエンド側での
- 認証コードの生成
- メール送信(ActionMailer)
- コードの照合処理(バリデーション)
までを実装していきます。
✅ 後編では、Flutterアプリ側でこのコードを入力する画面を
pinputパッケージで構築する方法を紹介予定です!
使用技術・環境
- Ruby on Rails 7(APIモード)
- ActionMailer(メール送信)
- DB:MySQL or SQLite
- メール送信:Mailtrap(開発) / AWS SES(本番)
- Flutterアプリからリクエストを受ける構成を想定
認証フローの全体像
認証コードの発行処理
🔸 モデル作成
db/migrate/xxxx_create_email_verifications.rb
create_table :email_verifications do |t|
t.string :email, null: false
t.string :code, null: false
t.datetime :expires_at
t.datetime :verified_at
t.timestamps
end
🔸 コード生成ロジック
def generate_code
SecureRandom.random_number(1_000_000).to_s.rjust(6, '0')
end
コードリクエストAPI(メール送信)
app/controllers/auth_controller.rb
def request_code
email = params[:email]
code = generate_code
verification = EmailVerification.create!(
email: email,
code: code,
expires_at: 10.minutes.from_now
)
VerificationMailer.send_code(email, code).deliver_later
render json: { message: 'Verification code sent' }
end
メール送信処理(ActionMailer)
🔸 メーラー定義
app/mailers/verification_mailer.rb
class VerificationMailer < ApplicationMailer
def send_code(email, code)
@code = code
mail(to: email, subject: '認証コードのお知らせ')
end
end
🔸 テンプレート
app/views/verification_mailer/send_code.text.erb
以下の認証コードをアプリに入力してください:
<%= @code %>
5分以内にご入力ください。
認証コード照合API
def verify_code
email = params[:email]
code = params[:code]
record = EmailVerification.find_by(email: email, code: code)
if record.nil?
render json: { error: 'コードが一致しません' }, status: :unauthorized
elsif record.expires_at < Time.current
render json: { error: '有効期限切れ' }, status: :unauthorized
elsif record.verified_at.present?
render json: { error: 'すでに認証済みです' }, status: :unprocessable_entity
else
record.update!(verified_at: Time.current)
render json: { message: '認証成功' }
end
end
セキュリティ対策の補足
- ✅ コードの使い回し禁止:照合成功後は
verified_atを更新 - ✅ レート制限:IPやメール単位でスロットル処理(Rack::Attackなど)
- ✅ メール送信制限:本番はSES等でSPF/DKIM/DMARCの設定を忘れずに
- ✅ コード長・文字種:桁数や英数混合でブルートフォース対策
次回予告:Flutterアプリ側の実装
後編では pinput パッケージを使って、以下のUIをFlutterで実装予定です。
- 6桁の認証コード入力フィールド
- 自動フォーカス&入力完了検知
- バリデーションエラーの表示
- 成功時の画面遷移
まとめ
- Rails APIだけでメール認証コードの実装は十分できる
- Deviseを使わずにシンプルで柔軟な実装が可能
- メール認証はセキュリティ要素とユーザビリティ両立が大事
- 次回はアプリ側のUI/UX実装に進みます!