Files

157 lines
4.9 KiB
PHP

<?php
use App\Models\Role;
use App\Models\User;
use App\Services\SystemConfig\SystemConfigService;
use Illuminate\Support\Facades\Cache;
use Laravel\Socialite\Contracts\User as SocialiteUserContract;
use Laravel\Socialite\Facades\Socialite;
beforeEach(function () {
$ref = new ReflectionClass(SystemConfigService::class);
$prop = $ref->getProperty('resolvedSettings');
$prop->setAccessible(true);
$prop->setValue(null, null);
Cache::flush();
Role::firstOrCreate(['name' => 'User', 'guard_name' => 'web']);
});
function enableOauth(string $provider): void
{
app(SystemConfigService::class)->update(['feature_'.$provider.'_oauth' => true]);
}
function fakeSocialiteUser(array $overrides = []): SocialiteUserContract
{
$u = new Laravel\Socialite\Two\User;
$u->id = $overrides['id'] ?? 'oauth-id-123';
$u->name = $overrides['name'] ?? 'OAuth User';
$u->email = $overrides['email'] ?? 'oauth@example.com';
$u->avatar = $overrides['avatar'] ?? 'https://example.com/avatar.png';
$u->user = $overrides['user'] ?? [];
return $u;
}
test('redirect returns 404 when provider feature is disabled', function () {
$this->get('/auth/google')->assertNotFound();
});
test('redirect issues a redirect when provider feature is enabled', function () {
enableOauth('google');
Socialite::shouldReceive('driver->redirect')
->andReturn(redirect('https://accounts.google.com/o/oauth2/auth?fake=1'));
$this->get('/auth/google')->assertRedirect();
expect(session('social_auth_provider'))->toBe('google');
});
test('callback without provider session redirects to login with error', function () {
$this->get('/auth/callback')
->assertRedirect('/login')
->assertSessionHas('error');
});
test('callback rejects unverified email from provider', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
'user' => ['email_verified' => false],
]));
$this->get('/auth/callback')
->assertRedirect('/login')
->assertSessionHas('error');
$this->assertGuest();
});
test('callback creates a new user via provider id and assigns user role', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
'id' => 'google-uid-9',
'email' => 'fresh@example.com',
'name' => 'Fresh User',
]));
$this->get('/auth/callback')->assertRedirect('/dashboard');
$user = User::where('email', 'fresh@example.com')->first();
expect($user)->not->toBeNull();
expect($user->google_id)->toBe('google-uid-9');
expect($user->hasRole('User'))->toBeTrue();
});
test('callback links to existing user with matching email when no provider id yet', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
$existing = User::factory()->create([
'email' => 'link@example.com',
'google_id' => null,
]);
Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
'id' => 'google-uid-link',
'email' => 'link@example.com',
]));
$this->get('/auth/callback')->assertRedirect('/dashboard');
expect($existing->fresh()->google_id)->toBe('google-uid-link');
$this->assertAuthenticatedAs($existing->fresh());
});
test('callback refuses to overwrite a different existing oauth identity', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
$existing = User::factory()->create([
'email' => 'taken@example.com',
'google_id' => 'different-google-id',
]);
Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
'id' => 'attacker-id',
'email' => 'taken@example.com',
]));
$this->get('/auth/callback')
->assertRedirect('/login')
->assertSessionHas('error');
expect($existing->fresh()->google_id)->toBe('different-google-id');
$this->assertGuest();
});
test('callback re-uses user matched by provider id', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
$existing = User::factory()->create(['google_id' => 'stable-id']);
Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
'id' => 'stable-id',
'email' => $existing->email,
]));
$this->get('/auth/callback')->assertRedirect('/dashboard');
$this->assertAuthenticatedAs($existing->fresh());
});
test('callback on socialite exception redirects to login with error', function () {
enableOauth('google');
session(['social_auth_provider' => 'google']);
Socialite::shouldReceive('driver->user')->andThrow(new Exception('OAuth boom'));
$this->get('/auth/callback')
->assertRedirect('/login')
->assertSessionHas('error');
$this->assertGuest();
});