fix(auth): isolate password reset flows by application role

This commit is contained in:
ncoronel 2026-09-04 14:17:27 -03:00
parent 99594b17e7
commit 75152b53a4
5 changed files with 20 additions and 9 deletions

View File

@ -5,6 +5,7 @@ namespace App\Domains\Auth\Controllers;
use App\Domains\Auth\Models\ResetPasswordAttempt;
use App\Domains\Auth\Requests\ResetPasswordRequest;
use App\Domains\Auth\Services\ResetPasswordAttemptService;
use App\Domains\Authorization\Enums\RoleCode;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
@ -26,6 +27,7 @@ class ResetPasswordController extends Controller
$data['email'],
$data['codigo'],
$data['password'],
RoleCode::from($request->route('reset_role', RoleCode::User->value)),
)) {
throw ValidationException::withMessages([
'codigo' => __('api.auth.password_reset_invalid'),

View File

@ -5,6 +5,7 @@ namespace App\Domains\Auth\Controllers;
use App\Domains\Auth\Models\ResetPasswordAttempt;
use App\Domains\Auth\Requests\ValidateResetPasswordAttemptRequest;
use App\Domains\Auth\Services\ResetPasswordAttemptService;
use App\Domains\Authorization\Enums\RoleCode;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
@ -25,6 +26,7 @@ class ValidateResetPasswordAttemptController extends Controller
$result = $this->resetPasswordAttemptService->validateCode(
$data['email'],
$data['codigo'],
RoleCode::from($request->route('reset_role', RoleCode::User->value)),
);
if ($result === ResetPasswordAttemptService::CODE_EXPIRED) {

View File

@ -28,7 +28,8 @@ class ResetPasswordAttemptService
try {
$attemptId = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?int {
$user = User::query()
->where('email', $email)
->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::User->value)
->lockForUpdate()
->first();
@ -65,7 +66,7 @@ class ResetPasswordAttemptService
try {
$result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array {
$user = User::query()
->where('email', $email)
->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::AdminApp->value)
->whereNotNull('tenant_codigo')
->lockForUpdate()
@ -113,7 +114,7 @@ class ResetPasswordAttemptService
try {
$result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array {
$user = User::query()
->where('email', $email)
->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::Scanner->value)
->whereNotNull('tenant_codigo')
->lockForUpdate()
@ -152,14 +153,15 @@ class ResetPasswordAttemptService
);
}
public function validateCode(string $email, string $code): string
public function validateCode(string $email, string $code, RoleCode $role = RoleCode::User): string
{
$emailFingerprint = $this->emailFingerprint($email);
try {
return DB::transaction(function () use ($email, $code, $emailFingerprint): string {
return DB::transaction(function () use ($email, $code, $emailFingerprint, $role): string {
$user = User::query()
->where('email', $email)
->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', $role->value)
->lockForUpdate()
->first();
@ -207,14 +209,15 @@ class ResetPasswordAttemptService
}
}
public function resetPassword(string $email, string $code, string $password): bool
public function resetPassword(string $email, string $code, string $password, RoleCode $role = RoleCode::User): bool
{
$emailFingerprint = $this->emailFingerprint($email);
try {
return DB::transaction(function () use ($email, $code, $password, $emailFingerprint): bool {
return DB::transaction(function () use ($email, $code, $password, $emailFingerprint, $role): bool {
$user = User::query()
->where('email', $email)
->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', $role->value)
->lockForUpdate()
->first();

View File

@ -12,8 +12,10 @@ Route::prefix('v1/adminapp')->group(function (): void {
Route::post('password/reset-attempts', CreateAdminAppResetPasswordAttemptController::class)
->middleware('throttle:5,1');
Route::post('password/reset-attempts/validate', ValidateResetPasswordAttemptController::class)
->defaults('reset_role', 'adminapp')
->middleware('throttle:10,1');
Route::post('password/reset', ResetPasswordController::class)
->defaults('reset_role', 'adminapp')
->middleware('throttle:5,1');
Route::middleware(['auth:sanctum', 'adminapp.tenant'])
->get('me', AdminAppMeController::class);

View File

@ -12,8 +12,10 @@ Route::prefix('v1/scanner')->group(function (): void {
Route::post('password/reset-attempts', CreateScannerResetPasswordAttemptController::class)
->middleware('throttle:5,1');
Route::post('password/reset-attempts/validate', ValidateResetPasswordAttemptController::class)
->defaults('reset_role', 'scanner')
->middleware('throttle:10,1');
Route::post('password/reset', ResetPasswordController::class)
->defaults('reset_role', 'scanner')
->middleware('throttle:5,1');
Route::middleware(['auth:sanctum', 'scanner.tenant'])
->get('me', ScannerMeController::class);