Skip to content

Commit 9c8cc3a

Browse files
jasonvargaclaude
andcommitted
Use expectsJson to distinguish CP vs frontend 2FA responses
Frontend form submissions without a _redirect param now redirect back with a flash message instead of returning raw JSON. CP axios calls keep their JSON response because they set the appropriate Accept header. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cedb77b commit 9c8cc3a

8 files changed

Lines changed: 76 additions & 15 deletions

src/Http/Controllers/User/TwoFactorAuthenticationController.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ public function confirm(Request $request, ConfirmTwoFactorAuthentication $confir
3939
try {
4040
$confirm($user, $request->input('code'));
4141
} catch (ValidationException $e) {
42-
if (! $request->has('_redirect')) {
42+
if ($request->expectsJson()) {
4343
throw $e;
4444
}
4545

4646
return $this->handleFormValidationError($request, $e);
4747
}
4848

49-
if (! $request->has('_redirect')) {
49+
if ($request->expectsJson()) {
5050
return [];
5151
}
5252

@@ -59,7 +59,7 @@ public function disable(Request $request, DisableTwoFactorAuthentication $disabl
5959

6060
$disable($user);
6161

62-
if (! $request->has('_redirect')) {
62+
if ($request->expectsJson()) {
6363
if ($user->isTwoFactorAuthenticationRequired()) {
6464
return ['redirect' => $this->setupUrlRedirect()];
6565
}
@@ -100,7 +100,7 @@ private function formSuccessRedirect(Request $request, string $message)
100100
}
101101
}
102102

103-
return redirect(route('statamic.site'))->with('success', $message);
103+
return back()->with('success', $message);
104104
}
105105

106106
protected function confirmUrl()

src/Http/Controllers/User/TwoFactorRecoveryCodesController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function store(Request $request, GenerateNewRecoveryCodes $generateRecove
2222

2323
$generateRecoveryCodes($user);
2424

25-
if (! $request->has('_redirect')) {
25+
if ($request->expectsJson()) {
2626
return ['recovery_codes' => $user->twoFactorRecoveryCodes()];
2727
}
2828

tests/Feature/Users/DisableTwoFactorTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function it_disables_two_factor_authentication()
2929
$this
3030
->actingAs($user)
3131
->withActiveElevatedSession()
32-
->delete(cp_route('users.two-factor.disable'))
32+
->deleteJson(cp_route('users.two-factor.disable'))
3333
->assertOk()
3434
->assertJson(['redirect' => null]);
3535

@@ -55,7 +55,7 @@ public function it_disables_two_factor_authentication_when_two_factor_is_enforce
5555
$this
5656
->actingAs($user)
5757
->withActiveElevatedSession()
58-
->delete(cp_route('users.two-factor.disable'))
58+
->deleteJson(cp_route('users.two-factor.disable'))
5959
->assertOk()
6060
->assertJson(['redirect' => cp_route('two-factor-setup')]);
6161

tests/Feature/Users/EnableTwoFactorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public function it_confirms_two_factor_authentication($url)
143143
$this
144144
->actingAs($user)
145145
->withActiveElevatedSession()
146-
->post($url(), [
146+
->postJson($url(), [
147147
'code' => $this->getOneTimeCode($user),
148148
])
149149
->assertOk();

tests/Feature/Users/TwoFactorRecoveryCodesTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function it_generates_recovery_codes($url)
7676
$this
7777
->actingAs($user)
7878
->withActiveElevatedSession()
79-
->post($url())
79+
->postJson($url())
8080
->assertOk()
8181
->assertJsonStructure(['recovery_codes']);
8282
}

tests/Tags/User/DisableTwoFactorFormTest.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public function it_falls_back_to_default_setup_route_when_no_config_and_2fa_is_e
137137
}
138138

139139
#[Test]
140-
public function it_uses_login_redirect_from_session_when_no_redirect_param()
140+
public function it_prefers_redirect_param_over_login_redirect_in_session()
141141
{
142142
$user = $this->userWithTwoFactorEnabled();
143143

@@ -155,6 +155,37 @@ public function it_uses_login_redirect_from_session_when_no_redirect_param()
155155
$this->assertNull($user->fresh()->two_factor_confirmed_at);
156156
}
157157

158+
#[Test]
159+
public function it_redirects_back_without_redirect_param()
160+
{
161+
$user = $this->userWithTwoFactorEnabled();
162+
163+
$this
164+
->actingAs($user)
165+
->session(['statamic_elevated_session' => now()->timestamp])
166+
->from('/account')
167+
->delete(route('statamic.users.two-factor.disable'))
168+
->assertRedirect('/account')
169+
->assertSessionHas('success');
170+
171+
$this->assertNull($user->fresh()->two_factor_confirmed_at);
172+
}
173+
174+
#[Test]
175+
public function it_returns_json_for_xhr_requests()
176+
{
177+
$user = $this->userWithTwoFactorEnabled();
178+
179+
$this
180+
->actingAs($user)
181+
->session(['statamic_elevated_session' => now()->timestamp])
182+
->deleteJson(route('statamic.users.two-factor.disable'))
183+
->assertOk()
184+
->assertJson(['redirect' => null]);
185+
186+
$this->assertNull($user->fresh()->two_factor_confirmed_at);
187+
}
188+
158189
private function user()
159190
{
160191
return tap(User::make()->makeSuper()->email('test@example.com'))->save();

tests/Tags/User/ResetTwoFactorRecoveryCodesFormTest.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,24 @@ public function it_redirects_back_without_redirect_param()
111111
->actingAs($user)
112112
->session(['statamic_elevated_session' => now()->timestamp])
113113
->from('/account/security')
114-
->post(route('statamic.users.two-factor.recovery-codes.generate'), [
115-
'_redirect' => '/account/security',
116-
])
114+
->post(route('statamic.users.two-factor.recovery-codes.generate'))
117115
->assertRedirect('/account/security')
118116
->assertSessionHas('success');
119117
}
120118

119+
#[Test]
120+
public function it_returns_json_for_xhr_requests()
121+
{
122+
$user = $this->userWithTwoFactorEnabled();
123+
124+
$this
125+
->actingAs($user)
126+
->session(['statamic_elevated_session' => now()->timestamp])
127+
->postJson(route('statamic.users.two-factor.recovery-codes.generate'))
128+
->assertOk()
129+
->assertJsonStructure(['recovery_codes']);
130+
}
131+
121132
private function user()
122133
{
123134
return tap(User::make()->makeSuper()->email('test@example.com'))->save();

tests/Tags/User/TwoFactorSetupFormTest.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,34 @@ public function it_confirms_setup_and_redirects()
119119
}
120120

121121
#[Test]
122-
public function it_redirects_to_home_without_redirect_param()
122+
public function it_redirects_back_without_redirect_param()
123123
{
124124
$user = $this->userWithTwoFactorPending();
125125

126126
$this
127127
->actingAs($user)
128+
->from('/setup-2fa')
128129
->post(route('statamic.users.two-factor.confirm'), [
129130
'code' => $this->getOneTimeCode($user),
130-
]);
131+
])
132+
->assertRedirect('/setup-2fa')
133+
->assertSessionHas('success');
134+
135+
$this->assertNotNull($user->fresh()->two_factor_confirmed_at);
136+
}
137+
138+
#[Test]
139+
public function it_returns_json_for_xhr_requests()
140+
{
141+
$user = $this->userWithTwoFactorPending();
142+
143+
$this
144+
->actingAs($user)
145+
->postJson(route('statamic.users.two-factor.confirm'), [
146+
'code' => $this->getOneTimeCode($user),
147+
])
148+
->assertOk()
149+
->assertJson([]);
131150

132151
$this->assertNotNull($user->fresh()->two_factor_confirmed_at);
133152
}

0 commit comments

Comments
 (0)