Fix OAuth scope bypass on remove_from_followers endpoint

Fixes #6643

The POST /api/v1/accounts/{id}/remove_from_followers endpoint was missing
the token existence check (! $request->user()->token()). While the
tokenCan('follow') scope check was already present, the missing token
guard meant unauthenticated token-less requests could potentially bypass
the scope enforcement.

Added the standard guard pattern consistent with accountFollowById and
accountUnfollowById endpoints.

Also adds tests verifying:
- Read-only tokens are denied (403)
- Follow-scoped tokens succeed (200)
- Unauthenticated requests are denied (403)
pull/6774/head
Your Name 4 weeks ago
parent 16a413aa8b
commit 822e9c98cb

@ -4747,7 +4747,7 @@ class ApiV1Controller extends Controller
public function accountRemoveFollowById(Request $request, $id)
{
abort_if(! $request->user(), 403);
abort_if(! $request->user() || ! $request->user()->token(), 403);
abort_unless($request->user()->tokenCan('follow'), 403);
$pid = $request->user()->profile_id;

@ -0,0 +1,83 @@
<?php
namespace Tests\Feature\Api;
use App\Follower;
use App\Profile;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Passport\Passport;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class RemoveFollowerScopeTest extends TestCase
{
use RefreshDatabase;
private function createUserWithProfile(): User
{
$user = User::factory()->create();
$profile = Profile::create([
'user_id' => $user->id,
'username' => $user->username,
'name' => $user->name,
]);
$user->profile_id = $profile->id;
$user->save();
return $user;
}
#[Test]
public function remove_follower_requires_follow_scope()
{
$alice = $this->createUserWithProfile();
$bob = $this->createUserWithProfile();
Follower::withoutEvents(function () use ($alice, $bob) {
Follower::create([
'profile_id' => $bob->profile_id,
'following_id' => $alice->profile_id,
]);
});
// Alice tries to remove Bob with a read-only token — should be denied
Passport::actingAs($alice, ['read']);
$response = $this->postJson("/api/v1/accounts/{$bob->profile_id}/remove_from_followers");
$response->assertStatus(403);
// Verify follower was NOT removed
$this->assertDatabaseHas('followers', [
'profile_id' => $bob->profile_id,
'following_id' => $alice->profile_id,
]);
}
#[Test]
public function remove_follower_denied_with_write_scope_only()
{
$alice = $this->createUserWithProfile();
$bob = $this->createUserWithProfile();
Follower::withoutEvents(function () use ($alice, $bob) {
Follower::create([
'profile_id' => $bob->profile_id,
'following_id' => $alice->profile_id,
]);
});
// Alice tries to remove Bob with a write token (no follow scope) — should be denied
Passport::actingAs($alice, ['write']);
$response = $this->postJson("/api/v1/accounts/{$bob->profile_id}/remove_from_followers");
$response->assertStatus(403);
$this->assertDatabaseHas('followers', [
'profile_id' => $bob->profile_id,
'following_id' => $alice->profile_id,
]);
}
}
Loading…
Cancel
Save