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/Unit/VerifiedFactorTest.php
<?php

declare(strict_types=1);

namespace GhostCompiler\LaravelAuth\Tests\Unit;

use GhostCompiler\LaravelAuth\Support\VerifiedFactor;
use GhostCompiler\LaravelAuth\Tests\Fixtures\User;
use GhostCompiler\LaravelAuth\Tests\TestCase;
use RuntimeException;

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

        $factor = VerifiedFactor::issue($user, 'otp');

        self::assertSame('otp', $factor->type);
        self::assertSame((string) $user->getAuthIdentifier(), $factor->userId);
        self::assertIsInt($factor->verifiedAt);
        self::assertGreaterThan(0, $factor->verifiedAt);
    }

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

        $before = now()->timestamp;
        $factor = VerifiedFactor::issue($user, 'passkey');
        $after = now()->timestamp;

        self::assertGreaterThanOrEqual($before, $factor->verifiedAt);
        self::assertLessThanOrEqual($after, $factor->verifiedAt);
    }

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

        $factor = VerifiedFactor::issue($user, 'otp');

        // Should not throw
        $factor->assertMatches($user);
        self::assertTrue(true);
    }

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

        $factor = VerifiedFactor::issue($user1, 'otp');

        $this->expectException(RuntimeException::class);
        $this->expectExceptionMessage('Invalid verification proof.');

        $factor->assertMatches($user2);
    }

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

        $factor = VerifiedFactor::issue($user, 'passkey');
        self::assertSame('passkey', $factor->type);
    }

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

        $factor = VerifiedFactor::issue($user, 'otp');
        $reflection = new \ReflectionClass($factor);

        $typeProp = $reflection->getProperty('type');
        $userIdProp = $reflection->getProperty('userId');
        $verifiedAtProp = $reflection->getProperty('verifiedAt');

        self::assertTrue($typeProp->isReadOnly());
        self::assertTrue($userIdProp->isReadOnly());
        self::assertTrue($verifiedAtProp->isReadOnly());
    }
}