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

This commit is contained in:
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\Models\ResetPasswordAttempt;
use App\Domains\Auth\Requests\ResetPasswordRequest; use App\Domains\Auth\Requests\ResetPasswordRequest;
use App\Domains\Auth\Services\ResetPasswordAttemptService; use App\Domains\Auth\Services\ResetPasswordAttemptService;
use App\Domains\Authorization\Enums\RoleCode;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@@ -26,6 +27,7 @@ class ResetPasswordController extends Controller
$data['email'], $data['email'],
$data['codigo'], $data['codigo'],
$data['password'], $data['password'],
RoleCode::from($request->route('reset_role', RoleCode::User->value)),
)) { )) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'codigo' => __('api.auth.password_reset_invalid'), '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\Models\ResetPasswordAttempt;
use App\Domains\Auth\Requests\ValidateResetPasswordAttemptRequest; use App\Domains\Auth\Requests\ValidateResetPasswordAttemptRequest;
use App\Domains\Auth\Services\ResetPasswordAttemptService; use App\Domains\Auth\Services\ResetPasswordAttemptService;
use App\Domains\Authorization\Enums\RoleCode;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@@ -25,6 +26,7 @@ class ValidateResetPasswordAttemptController extends Controller
$result = $this->resetPasswordAttemptService->validateCode( $result = $this->resetPasswordAttemptService->validateCode(
$data['email'], $data['email'],
$data['codigo'], $data['codigo'],
RoleCode::from($request->route('reset_role', RoleCode::User->value)),
); );
if ($result === ResetPasswordAttemptService::CODE_EXPIRED) { if ($result === ResetPasswordAttemptService::CODE_EXPIRED) {

View File

@@ -28,7 +28,8 @@ class ResetPasswordAttemptService
try { try {
$attemptId = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?int { $attemptId = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?int {
$user = User::query() $user = User::query()
->where('email', $email) ->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::User->value)
->lockForUpdate() ->lockForUpdate()
->first(); ->first();
@@ -65,7 +66,7 @@ class ResetPasswordAttemptService
try { try {
$result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array { $result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array {
$user = User::query() $user = User::query()
->where('email', $email) ->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::AdminApp->value) ->where('rol_codigo', RoleCode::AdminApp->value)
->whereNotNull('tenant_codigo') ->whereNotNull('tenant_codigo')
->lockForUpdate() ->lockForUpdate()
@@ -113,7 +114,7 @@ class ResetPasswordAttemptService
try { try {
$result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array { $result = DB::transaction(function () use ($email, $emailFingerprint, $reason): ?array {
$user = User::query() $user = User::query()
->where('email', $email) ->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', RoleCode::Scanner->value) ->where('rol_codigo', RoleCode::Scanner->value)
->whereNotNull('tenant_codigo') ->whereNotNull('tenant_codigo')
->lockForUpdate() ->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); $emailFingerprint = $this->emailFingerprint($email);
try { try {
return DB::transaction(function () use ($email, $code, $emailFingerprint): string { return DB::transaction(function () use ($email, $code, $emailFingerprint, $role): string {
$user = User::query() $user = User::query()
->where('email', $email) ->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', $role->value)
->lockForUpdate() ->lockForUpdate()
->first(); ->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); $emailFingerprint = $this->emailFingerprint($email);
try { 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() $user = User::query()
->where('email', $email) ->where('active_email', mb_strtolower(trim($email)))
->where('rol_codigo', $role->value)
->lockForUpdate() ->lockForUpdate()
->first(); ->first();

View File

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

View File

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