Skip to content

Commit 5fc47a4

Browse files
chore: Add PR's requested changes
1 parent 10eb2e0 commit 5fc47a4

6 files changed

Lines changed: 3 additions & 43 deletions

File tree

app/Http/Controllers/UserController.php

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -631,24 +631,6 @@ private function resolveClientFromMemento(): ?Client
631631
return $client;
632632
}
633633

634-
/**
635-
* Resolves the OAuth2 client carried in the pending MFA state (the client the
636-
* challenge was issued for), so verification scopes the OTP lookup and
637-
* sibling-revoke to the same client. Returns null when the challenge was not
638-
* issued in a client context.
639-
*
640-
* @param array $pending
641-
* @return Client|null
642-
*/
643-
private function resolveClientFromPendingState(array $pending): ?Client
644-
{
645-
$clientId = $pending['client_id'] ?? null;
646-
if (is_null($clientId)) {
647-
return null;
648-
}
649-
return $this->client_repository->getClientById($clientId);
650-
}
651-
652634
/**
653635
* Verifies a 2FA OTP challenge and, on success, establishes the session.
654636
*
@@ -686,7 +668,7 @@ public function verify2FA()
686668
}
687669

688670
// Scope verification to the client the challenge was issued for.
689-
$client = $this->resolveClientFromPendingState($pending);
671+
$client = $this->resolveClientFromMemento();
690672

691673
try {
692674
// Commits the OTP redeem (+ sibling revoke) in its own tx. The

app/Strategies/MFA/AbstractMFAChallengeStrategy.php

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ abstract class AbstractMFAChallengeStrategy implements IMFAChallengeStrategy
1313
private const KEY_USER_ID = '2fa_pending_user_id';
1414
private const KEY_PENDING_AT = '2fa_pending_at';
1515
private const KEY_REMEMBER = '2fa_remember';
16-
private const KEY_CLIENT_ID = '2fa_pending_client_id';
1716
private const KEY_RECOVERY_ATTEMPTS = '2fa_recovery_attempts';
1817

1918
public function __construct(protected IUserRecoveryCodeRepository $recovery_code_repository) {}
@@ -36,7 +35,6 @@ public function getPendingState(): ?array
3635
'user_id' => $user_id,
3736
'pending_at' => $pending_at,
3837
'remember' => Session::get(self::KEY_REMEMBER, false),
39-
'client_id' => Session::get(self::KEY_CLIENT_ID),
4038
];
4139
}
4240

@@ -45,7 +43,6 @@ public function clearPendingState(): void
4543
Session::remove(self::KEY_USER_ID);
4644
Session::remove(self::KEY_PENDING_AT);
4745
Session::remove(self::KEY_REMEMBER);
48-
Session::remove(self::KEY_CLIENT_ID);
4946
Session::remove(self::KEY_RECOVERY_ATTEMPTS);
5047
}
5148

@@ -62,23 +59,17 @@ public function verifyRecoveryCode(User $user, string $code): void
6259
throw new AuthenticationException("Invalid recovery code.");
6360
}
6461
$recoveryCode->markUsed();
65-
$this->recovery_code_repository->add($recoveryCode, false);
6662
return;
6763
}
6864
}
6965
throw new AuthenticationException("Invalid recovery code.");
7066
}
7167

72-
protected function storePendingState(int $userId, bool $remember, ?string $clientId = null): void
68+
protected function storePendingState(int $userId, bool $remember): void
7369
{
7470
Session::put(self::KEY_USER_ID, $userId);
7571
Session::put(self::KEY_PENDING_AT, time());
7672
Session::put(self::KEY_REMEMBER, $remember);
77-
if (is_null($clientId)) {
78-
Session::remove(self::KEY_CLIENT_ID);
79-
} else {
80-
Session::put(self::KEY_CLIENT_ID, $clientId);
81-
}
8273
}
8374

8475
public function verifyChallenge(User $user, string $code, ?Client $client = null): void

app/Strategies/MFA/EmailOTPMFAChallengeStrategy.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ public function __construct(
2020

2121
public function issueChallenge(User $user, ?Client $client, bool $remember): array
2222
{
23-
// Carry the issuing client into the pending state so verification scopes
24-
// the OTP lookup and sibling-revoke to the same client (see verifyChallenge).
25-
$this->storePendingState($user->getId(), $remember, $client?->getClientId());
23+
$this->storePendingState($user->getId(), $remember);
2624

2725
$otp = $this->token_service->createOTPFromPayload([
2826
OAuth2Protocol::OAuth2PasswordlessConnection => OAuth2Protocol::OAuth2PasswordlessConnectionEmail,
@@ -75,14 +73,12 @@ public function verifyChallenge(User $user, string $code, ?Client $client = null
7573
}
7674

7775
$otp->redeem();
78-
$this->otp_repository->add($otp, false);
7976

8077
// Revoke other pending OTPs for this user, scoped to the same client so we
8178
// never burn unrelated OTPs (e.g. passwordless-login codes for other clients).
8279
foreach ($this->otp_repository->getByUserNameNotRedeemed($user->getEmail(), $client) as $otpToRevoke) {
8380
if ($otpToRevoke->getValue() !== $otp->getValue()) {
8481
$otpToRevoke->redeem();
85-
$this->otp_repository->add($otpToRevoke, false);
8682
}
8783
}
8884
}

app/libs/Auth/AuthService.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
use App\Services\Auth\IUserService as IAuthUserService;
2020
use Auth\Exceptions\AuthenticationException;
2121
use Auth\Exceptions\AuthenticationLockedUserLoginAttempt;
22-
use Auth\Exceptions\UnverifiedEmailMemberException;
2322
use Auth\Repositories\IUserRepository;
2423
use Exception;
2524
use Illuminate\Support\Facades\Auth;
@@ -393,7 +392,6 @@ public function login(string $username, string $password, bool $remember_me): bo
393392
{
394393
Log::debug("AuthService::login");
395394

396-
$this->last_login_error = "";
397395
if (!Auth::attempt(['username' => $username, 'password' => $password], $remember_me)) {
398396
throw new AuthenticationException
399397
(
@@ -799,10 +797,6 @@ public function verifyMFAChallenge(
799797
string $value,
800798
?Client $client = null
801799
): void {
802-
// Commits the OTP redeem (+ sibling revoke) as a single tx. Trusted-device
803-
// enrollment and audit are applied by the caller as best-effort,
804-
// non-blocking side effects after this commits, so a failure in either
805-
// does not block (or roll back) an already-verified second factor.
806800
$this->tx_service->transaction(function () use ($user, $strategy, $value, $client) {
807801
$strategy->verifyChallenge($user, $value, $client);
808802
});

tests/Unit/MFA/AbstractMFAChallengeStrategyTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ public function testVerifyRecoveryCode_withMatchingCode_marksAsUsed(): void
9393
$repo->shouldReceive('getUnusedByUser')->with($user)->andReturn([$recoveryCode]);
9494
// Lock is taken before mutating (regression guard for 3357348455).
9595
$repo->shouldReceive('refreshExclusiveLock')->with($recoveryCode)->once();
96-
$repo->shouldReceive('add')->with($recoveryCode, false)->once();
9796

9897
$strategy = new class($repo) extends AbstractMFAChallengeStrategy {
9998
public function issueChallenge(User $user, ?Client $client, bool $remember): array { return []; }

tests/Unit/MFA/EmailOTPMFAChallengeStrategyTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,6 @@ public function testVerifyChallenge_withValidOtp_redeemsAndRevokesOthers_scopedT
132132
->andReturn([$otherOtp]);
133133

134134
// The redeemed code and the revoked sibling are both persisted with deferred flush.
135-
$this->otpRepository->shouldReceive('add')->with($storedOtp, false)->once();
136-
$this->otpRepository->shouldReceive('add')->with($otherOtp, false)->once();
137135

138136
$this->strategy->verifyChallenge($user, $code, $client);
139137
$this->addToAssertionCount(1);

0 commit comments

Comments
 (0)