krgm.so-manager-dev.com/app/Http/Controllers/Auth/EmailOtpController.php
OU.ZAIKOU 13d2ecfceb
All checks were successful
Deploy main / deploy (push) Successful in 25s
【ログイン】二重認証実装
2026-01-21 22:37:38 +09:00

139 lines
4.6 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Mail\EmailOtpMail;
use App\Models\Ope;
use App\Services\EmailOtpService;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
/**
* OTP メール認証コントローラー
*
* ログイン後の OTPワンタイムパスワード検証プロセスを処理します
*/
class EmailOtpController extends Controller
{
use ValidatesRequests;
protected EmailOtpService $otpService;
/**
* コンストラクタ
*/
public function __construct(EmailOtpService $otpService)
{
$this->otpService = $otpService;
}
/**
* OTP 入力フォームを表示
*
* ログイン直後、ユーザーに6桁の OTP コードを入力させるページを表示します
* メールアドレスはマスク表示a***@example.com
*/
public function show(Request $request)
{
/** @var Ope */
$user = $request->user();
// メールアドレスをマスク最初の1文字のみ表示
$maskedEmail = $this->otpService->maskEmail($user->ope_mail);
// 次の重発までの待機時間
$resendWaitSeconds = $this->otpService->getResendWaitSeconds($user);
return view('auth.otp', [
'maskedEmail' => $maskedEmail,
'resendWaitSeconds' => $resendWaitSeconds,
]);
}
/**
* OTP コード検証
*
* ユーザーが入力した6桁のコードを検証します
*
* 成功時email_otp_verified_at を更新し、ホームページにリダイレクト
* 失敗時:エラーメッセージと共に OTP 入力フォームに戻す
*/
public function verify(Request $request)
{
// 入力値を検証
$validated = $this->validate($request, [
'code' => ['required', 'string', 'size:6', 'regex:/^\d{6}$/'],
], [
'code.required' => 'OTPコードは必須です。',
'code.size' => 'OTPコードは6桁である必要があります。',
'code.regex' => 'OTPコードは6桁の数字である必要があります。',
]);
/** @var Ope */
$user = $request->user();
// OTP コードを検証
if ($this->otpService->verify($user, $validated['code'])) {
// 検証成功:ホームページにリダイレクト
return redirect()->intended(route('home'))
->with('success', 'OTP認証が完了しました。');
}
// 検証失敗:エラーメッセージと共に戻す
return back()
->withInput()
->with('error', '無効なまたは有効期限切れのOTPコードです。');
}
/**
* OTP コード再送
*
* ユーザーが OTP コード再送をリクエストした場合に実行
* 60秒以内の連続再送はブロックします
*/
public function resend(Request $request)
{
/** @var Ope */
$user = $request->user();
// 重発可能か確認
if (!$this->otpService->canResend($user)) {
$waitSeconds = $this->otpService->getResendWaitSeconds($user);
return back()->with('error', "{$waitSeconds} 秒待機してからリクエストしてください。");
}
try {
// 新しい OTP コードを発行
$otpCode = $this->otpService->issue($user);
// ope_mail はセミコロン区切りで複数アドレスを保持する可能性があるため、最初のアドレスのみ抽出
$operatorEmails = explode(';', trim($user->ope_mail));
$primaryEmail = trim($operatorEmails[0] ?? $user->ope_mail);
Log::info('OTP 再送メール送信開始: ' . $primaryEmail);
// メール送信
Mail::to($primaryEmail)->send(new EmailOtpMail(
$otpCode,
$user->name ?? 'ユーザー'
));
Log::info('OTP 再送メール送信完了: ' . $primaryEmail);
return back()->with('success', 'OTPコードを再送信しました。');
} catch (\Exception $e) {
Log::error('OTP resend error: ' . $e->getMessage(), [
'exception' => $e,
'user_id' => $user->ope_id ?? null,
'user_email' => $user->ope_mail ?? null,
]);
return back()->with('error', 'OTP送信に失敗しました。もう一度お試しください。');
}
}
}