Skip to content

Commit 46cd34a

Browse files
Merge pull request #6 from Laragear/fix/interstitial-json-warn
[1.x] Fixes testing keys and response.
2 parents 0cc4900 + d7da8dc commit 46cd34a

5 files changed

Lines changed: 84 additions & 18 deletions

File tree

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,18 @@ use Laragear\Turnstile\Http\Controllers\InterstitialController;
777777
InterstitialController::register('/challenge', 'guest');
778778
```
779779

780+
> [!IMPORTANT]
781+
>
782+
> The interstitial middleware will _throw_ a JSON response if the request _requires_ JSON. This is because JSON response cannot be redirected. Instead, use the `redirect_url` key of the response to redirect the user to the interstitial controller.
783+
>
784+
> ```js
785+
> response = await $fetch('/comment', 'POST', {body: 'My Comment'});
786+
>
787+
> if (response.isStatus(400) && response.hasJson('redirect_url')) {
788+
> window.location.href = response.json('redirect_url')
789+
> }
790+
> ```
791+
780792
### Skip when authenticated
781793
782794
If you want to skip the challenge if a user is authenticated, you may add the `auth` keyword as parameter.
@@ -861,7 +873,7 @@ return [
861873

862874
```php
863875
return [
864-
The `true` value will remind the challenge forever. An integer will remind the challenge for that amount of minutes.
876+
'env' => env('TURNSTILE_ENV'),
865877
];
866878
```
867879

src/Http/Middleware/InterstitialMiddleware.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use Closure;
66
use Illuminate\Contracts\Auth\Factory;
77
use Illuminate\Contracts\Config\Repository;
8+
use Illuminate\Http\Exceptions\HttpResponseException;
9+
use Illuminate\Http\JsonResponse;
810
use Illuminate\Http\Request;
911
use Illuminate\Routing\Redirector;
1012
use Illuminate\Support\DateFactory;
@@ -43,9 +45,13 @@ public function handle(Request $request, Closure $next, string $auth = ''): mixe
4345
return $next($request);
4446
}
4547

46-
return $this->redirect
47-
->setIntendedUrl($request->fullUrl())
48-
->route($this->config->get('turnstile.interstitial.route'));
48+
$route = $this->config->get('turnstile.interstitial.route');
49+
50+
if ($request->expectsJson()) {
51+
$this->throwJsonException($route);
52+
}
53+
54+
return $this->redirect->setIntendedUrl($request->fullUrl())->route($route);
4955
}
5056

5157
/**
@@ -71,4 +77,18 @@ protected function shouldSkipWhenChallengeRecentlyDone(Request $request): bool
7177

7278
return $duration === true || $this->date->createFromTimestamp($duration)->isFuture();
7379
}
80+
81+
/**
82+
* Throw a JSON exception with some data for the turnstile challenge.
83+
*/
84+
protected function throwJsonException(string $route): never
85+
{
86+
throw new HttpResponseException(
87+
new JsonResponse([
88+
'success' => false,
89+
'message' => 'Requires Turnstile Challenge.',
90+
'redirect_url' => $this->redirect->route($route)->getTargetUrl(),
91+
], 400)
92+
);
93+
}
7494
}

src/Turnstile.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,9 @@ protected function getSecretKey(): string
299299
if ($this->currentEnvironment() !== 'production') {
300300
$key = match (strtolower($key)) {
301301
'' => $this->testingSecretKey->value,
302-
strtolower(SecretKey::Passing->name) => SiteKey::VisiblePassing->value,
303-
strtolower(SecretKey::Fails->name) => SiteKey::VisibleBlocks->value,
304-
strtolower(SecretKey::Spent->name) => SiteKey::InvisiblePassing->value,
302+
strtolower(SecretKey::Passing->name) => SecretKey::Passing->value,
303+
strtolower(SecretKey::Fails->name) => SecretKey::Fails->value,
304+
strtolower(SecretKey::Spent->name) => SecretKey::Spent->value,
305305
default => $key,
306306
};
307307
}

tests/Http/Middleware/InterstitialMiddlewareTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,26 @@ public function test_redirects_if_challenge_timestamp_is_past(): void
8888
->assertRedirect('turnstile/interstitial')
8989
->assertRedirectToRoute($this->app->make('config')->get('turnstile.interstitial.route'));
9090
}
91+
92+
public function test_throws_json_response_if_request_is_json(): void
93+
{
94+
$this->router()->get('test/intended', fn() => 'ok')->middleware('web', 'turnstile.interstitial');
95+
$this->router()->post('test/intended', fn() => 'ok')->middleware('web', 'turnstile.interstitial');
96+
97+
$this->getJson('test/intended')
98+
->assertStatus(400)
99+
->assertJson([
100+
'success' => false,
101+
'message' => "Requires Turnstile Challenge.",
102+
'redirect_url' => 'http://localhost/turnstile/interstitial',
103+
]);
104+
105+
$this->getJson('test/intended')
106+
->assertStatus(400)
107+
->assertJson([
108+
'success' => false,
109+
'message' => "Requires Turnstile Challenge.",
110+
'redirect_url' => 'http://localhost/turnstile/interstitial',
111+
]);
112+
}
91113
}

tests/TurnstileTest.php

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -528,23 +528,35 @@ public function test_uses_testing_site_key_by_name(SiteKey $key, string $name):
528528
public static function provideTestingSecretKeysNames(): array
529529
{
530530
return [
531-
[SiteKey::VisiblePassing, 'VisiblePassing'],
532-
[SiteKey::VisiblePassing, 'visiblepassing'],
533-
[SiteKey::VisibleBlocks, 'VisibleBlocks'],
534-
[SiteKey::VisibleBlocks, 'visibleblocks'],
535-
[SiteKey::InvisiblePassing, 'InvisiblePassing'],
536-
[SiteKey::InvisiblePassing, 'invisiblepassing'],
537-
[SiteKey::InvisibleBlocks, 'InvisibleBlocks'],
538-
[SiteKey::InvisibleBlocks, 'invisibleblocks'],
531+
[SecretKey::Passing, 'Passing'],
532+
[SecretKey::Passing, 'passing'],
533+
[SecretKey::Fails, 'Fails'],
534+
[SecretKey::Fails, 'fails'],
535+
[SecretKey::Spent, 'Spent'],
536+
[SecretKey::Spent, 'spent'],
539537
];
540538
}
541539

542540
#[DataProvider('provideTestingSecretKeysNames')]
543-
public function test_uses_testing_secret_key_by_name(SiteKey $key, string $name): void
541+
public function test_uses_testing_secret_key_by_name(SecretKey $key, string $name): void
544542
{
545543
$this->app->instance('env', 'not-production-nor-testing');
546-
$this->app->make('config')->set('turnstile.site_key', $name);
544+
$this->app->make('config')->set('turnstile.secret_key', $name);
547545

548-
static::assertSame($this->turnstile()->getSiteKey(), $key->value);
546+
$this->mock(HttpFactory::class, function (MockInterface $mock) use ($key): void {
547+
$mock->expects('asJson')->andReturnSelf();
548+
$mock->expects('acceptJson')->andReturnSelf();
549+
$mock->expects('withOptions')->andReturnSelf();
550+
$mock->expects('post')
551+
->withArgs(static function (string $endpoint, array $data) use ($key): bool {
552+
static::assertSame($data['secret'], $key->value);
553+
554+
return true;
555+
})
556+
->andReturnSelf();
557+
$mock->expects('throw')->andReturn($this->guzzleResponse());
558+
});
559+
560+
$this->turnstile()->getChallenge('test-key');
549561
}
550562
}

0 commit comments

Comments
 (0)