93 lines
2.9 KiB
PHP
93 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Helpers\ApiResponse;
|
|
use App\Mail\TwoFactorOtp;
|
|
use App\Services\Auth\OtpService;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use OpenApi\Attributes as OA;
|
|
|
|
class OtpController extends Controller
|
|
{
|
|
public function __construct(protected OtpService $otpService) {}
|
|
|
|
#[OA\Post(
|
|
path: '/v1/otp/send',
|
|
operationId: 'otpSend',
|
|
tags: ['OTP'],
|
|
summary: 'Send a 6-digit OTP to the given email',
|
|
requestBody: new OA\RequestBody(
|
|
required: true,
|
|
content: new OA\JsonContent(
|
|
required: ['email'],
|
|
properties: [new OA\Property(property: 'email', type: 'string', format: 'email')]
|
|
)
|
|
),
|
|
responses: [
|
|
new OA\Response(response: 200, description: 'OTP sent'),
|
|
new OA\Response(response: 500, description: 'Mail delivery failed'),
|
|
]
|
|
)]
|
|
public function send(Request $request)
|
|
{
|
|
$request->validate([
|
|
'email' => 'required|email',
|
|
]);
|
|
|
|
$email = $request->email;
|
|
$code = $this->otpService->generate($email);
|
|
|
|
try {
|
|
Mail::to($email)->send(new TwoFactorOtp(
|
|
otp: $code,
|
|
ipAddress: $request->ip(),
|
|
userAgent: $request->userAgent(),
|
|
));
|
|
} catch (\Throwable $e) {
|
|
Log::error('OTP send failed', ['email' => $email, 'error' => $e->getMessage()]);
|
|
|
|
return ApiResponse::serverError('Failed to send OTP. Please try again later.');
|
|
}
|
|
|
|
return ApiResponse::success(null, 'OTP has been sent to your email');
|
|
}
|
|
|
|
#[OA\Post(
|
|
path: '/v1/otp/verify',
|
|
operationId: 'otpVerify',
|
|
tags: ['OTP'],
|
|
summary: 'Verify an OTP code',
|
|
requestBody: new OA\RequestBody(
|
|
required: true,
|
|
content: new OA\JsonContent(
|
|
required: ['email', 'code'],
|
|
properties: [
|
|
new OA\Property(property: 'email', type: 'string', format: 'email'),
|
|
new OA\Property(property: 'code', type: 'string', minLength: 6, maxLength: 6, example: '123456'),
|
|
]
|
|
)
|
|
),
|
|
responses: [
|
|
new OA\Response(response: 200, description: 'OTP verified'),
|
|
new OA\Response(response: 422, description: 'Invalid or expired OTP'),
|
|
]
|
|
)]
|
|
public function verify(Request $request)
|
|
{
|
|
$request->validate([
|
|
'email' => 'required|email',
|
|
'code' => 'required|string|size:6',
|
|
]);
|
|
|
|
if (! $this->otpService->verify($request->input('email'), $request->input('code'))) {
|
|
return ApiResponse::error('Invalid or expired OTP code', 422);
|
|
}
|
|
|
|
return ApiResponse::success(null, 'OTP verified successfully');
|
|
}
|
|
}
|