Laravel Auth

PHP MIT

Laravel Auth by GhostCompiler adds advanced authentication for Laravel with TOTP 2FA, passkeys via WebAuthn, OTP channels (email, SMS, WhatsApp), trusted devices, and tenant-aware social login.

Stars
2
Forks
0
Downloads
N/A
Open Issues
0
Files main

Repository Files

Loading file structure...
tests/Feature/TrustedDeviceServiceTest.php
<?php

declare(strict_types=1);

namespace GhostCompiler\LaravelAuth\Tests\Feature;

use GhostCompiler\LaravelAuth\Contracts\LaravelAuthManager;
use GhostCompiler\LaravelAuth\Enums\AuthState;
use GhostCompiler\LaravelAuth\Models\TrustedDevice;
use GhostCompiler\LaravelAuth\Services\TrustedDeviceService;
use GhostCompiler\LaravelAuth\Tests\Fixtures\User;
use GhostCompiler\LaravelAuth\Tests\TestCase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Crypt;

class TrustedDeviceServiceTest extends TestCase
{
    public function test_trust_creates_a_database_record(): void
    {
        $user = User::query()->create([
            'email' => 'trust-db@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET', [], [], [], [
            'HTTP_USER_AGENT' => 'Test Browser 1.0',
            'REMOTE_ADDR' => '192.168.1.1',
        ]);

        $service = $this->app->make(TrustedDeviceService::class);
        $service->trust($user, $request, 'My Laptop');

        $device = TrustedDevice::query()->whereMorphedTo('authenticatable', $user)->first();
        self::assertNotNull($device);
        self::assertSame('My Laptop', $device->name);
        self::assertSame('192.168.1.1', $device->ip_address);
    }

    public function test_trust_queues_a_cookie(): void
    {
        $user = User::query()->create([
            'email' => 'trust-cookie@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET', [], [], [], [
            'HTTP_USER_AGENT' => 'Test Browser 1.0',
            'REMOTE_ADDR' => '127.0.0.1',
        ]);

        $service = $this->app->make(TrustedDeviceService::class);
        $service->trust($user, $request, 'Laptop');

        $cookies = collect(Cookie::getQueuedCookies());
        self::assertTrue($cookies->isNotEmpty());
        $cookie = $cookies->last();
        self::assertStringContainsString('|', $cookie->getValue());
    }

    public function test_is_trusted_returns_true_for_a_valid_device(): void
    {
        $user = User::query()->create([
            'email' => 'is-trusted@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET', [], [], [], [
            'HTTP_USER_AGENT' => 'My Browser',
            'REMOTE_ADDR' => '10.0.0.1',
        ]);

        $service = $this->app->make(TrustedDeviceService::class);
        $service->trust($user, $request, 'Test Device');

        $cookie = collect(Cookie::getQueuedCookies())->last();
        $requestWithCookie = Request::create('/', 'GET', [], [
            config('laravel-auth.trusted_devices.cookie') => $cookie->getValue(),
        ], [], [
            'HTTP_USER_AGENT' => 'My Browser',
            'REMOTE_ADDR' => '10.0.0.1',
        ]);

        self::assertTrue($service->isTrusted($user, $requestWithCookie));
    }

    public function test_is_trusted_returns_false_when_no_cookie_present(): void
    {
        $user = User::query()->create([
            'email' => 'no-cookie@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET');
        $service = $this->app->make(TrustedDeviceService::class);

        self::assertFalse($service->isTrusted($user, $request));
    }

    public function test_is_trusted_returns_false_after_forget_all(): void
    {
        $user = User::query()->create([
            'email' => 'forget@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET', [], [], [], [
            'HTTP_USER_AGENT' => 'Test Agent',
            'REMOTE_ADDR' => '127.0.0.1',
        ]);

        $service = $this->app->make(TrustedDeviceService::class);
        $service->trust($user, $request, 'Device');

        $cookie = collect(Cookie::getQueuedCookies())->last();
        $requestWithCookie = Request::create('/', 'GET', [], [
            config('laravel-auth.trusted_devices.cookie') => $cookie->getValue(),
        ], [], [
            'HTTP_USER_AGENT' => 'Test Agent',
            'REMOTE_ADDR' => '127.0.0.1',
        ]);

        $service->forgetAll($user, $request);

        self::assertFalse($service->isTrusted($user, $requestWithCookie));
        self::assertSame(0, TrustedDevice::query()->whereMorphedTo('authenticatable', $user)->count());
    }

    public function test_disable_2fa_clears_trusted_devices_and_resets_session(): void
    {
        $user = User::query()->create([
            'email' => 'disable-2fa@example.test',
            'password' => 'secret',
            'laravel_auth_totp_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP'),
            'laravel_auth_two_factor_enabled' => true,
        ]);

        $manager = $this->app->make(LaravelAuthManager::class);

        session()->put('laravel_auth.verified.'.$user->getAuthIdentifier(), true);
        session()->put('laravel_auth.state', AuthState::FullyAuthenticated->value);

        $manager->disable2FA($user);

        $user->refresh();
        self::assertNull($user->laravel_auth_totp_secret);
        self::assertFalse((bool) $user->laravel_auth_two_factor_enabled);
        self::assertSame(AuthState::FullyAuthenticated->value, session()->get('laravel_auth.state'));
        self::assertSame(0, TrustedDevice::query()->whereMorphedTo('authenticatable', $user)->count());
    }

    public function test_device_token_is_stored_as_a_hash_not_plain_text(): void
    {
        $user = User::query()->create([
            'email' => 'hash-check@example.test',
            'password' => 'secret',
        ]);

        $request = Request::create('/', 'GET', [], [], [], [
            'HTTP_USER_AGENT' => 'Security Checker',
            'REMOTE_ADDR' => '127.0.0.1',
        ]);

        $service = $this->app->make(TrustedDeviceService::class);
        $service->trust($user, $request, 'Device');

        $cookie = collect(Cookie::getQueuedCookies())->last();
        [, $plainToken] = explode('|', (string) $cookie->getValue(), 2);

        $device = TrustedDevice::query()->whereMorphedTo('authenticatable', $user)->first();
        self::assertNotSame($plainToken, $device->token);
    }
}