Testovanie
Passkeys::fake() nahradí relying party triedou PasskeysFake — programovateľnou náhradou bez kryptografie, naviazanou na kontrakt PasskeyService — a vráti ju na asercie. Registračné a prihlasovacie controllery tak otestujete bez simulovania kryptografie autentifikátora. Nevykonáva dekódovanie CBOR/COSE, overenie podpisu ani kontrolu challenge a platí len pre daný test. Vidí ju fasáda, injektovaný PasskeyService aj metódy modelu:
- Ceremónie sa zaznamenajú, nevykonajú: register() uloží kľúč z factory, authenticate() vráti naprogramovaný výsledok a volania možností vrátia pripravené možnosti s uplatnenými nastaveniami. Pripravené možnosti registrácie dekódujú identifikátor používateľa rovnako ako skutočná služba, takže poškodený identifikátor sa odmietne aj tu.
- Zaznamenajú sa aj volania cez metódy modelu (registerPasskey() a ďalšie) — idú cez Passkeys::for($this).
- rename() a revoke() stále spúšťajú skutočné akcie s kontrolou vlastníctva — vrátane zápisov a udalostí PasskeyRenamed / PasskeyRevoked — a po úspechu sa zaznamenajú.
- Čítanie (all(), find(), count(), exists()) ide do databázy; attestationFormats() vráti formáty skutočného registra.
use RoundlyConsulting\Passkeys\Facades\Passkeys;
$fake = Passkeys::fake();
// The fake skips the crypto, not your controller's parsing: RegistrationResponseData::fromArray()
// still needs the browser's shape with base64url members — any bytes will do.
$this->postJson('/passkeys', [
'id' => 'AAAA',
'rawId' => 'AAAA',
'type' => 'public-key',
'response' => ['clientDataJSON' => 'e30', 'attestationObject' => 'oA'],
])->assertCreated();
$fake->assertRegisteredFor($user);
$this->deleteJson("/passkeys/{$passkey->id}")->assertNoContent();
$fake->assertRevoked($passkey);
$fake->assertNothingRenamed();
// programmable outcomes:
Passkeys::fake()->rejectAuthentication(); // authenticate() throws CredentialNotFound
Passkeys::fake()->authenticatesAs($passkey); // authenticate() returns this exact credential
Passkeys::fake()->failRegistrationWith($exception);Programovateľné výsledky
| Metóda | Efekt |
|---|---|
acceptRegistration() | register() uloží pre používateľa kľúč ES256 z factory (predvolené). |
failRegistrationWith($e) | register() namiesto uloženia vyhodí zadanú PasskeyException. |
acceptAuthentication() | authenticate() uspeje (predvolené). |
rejectAuthentication() | authenticate() vyhodí CredentialNotFound. |
authenticatesAs($passkey) | authenticate() vráti presne tento kľúč. |
Asercie
| Asercia | Prejde, keď |
|---|---|
assertRegistered() | Zaznamenala sa aspoň jedna registrácia. |
assertRegisteredFor($user) | Zaznamenala sa registrácia pre $user. |
assertNothingRegistered() | Nezaznamenala sa žiadna registrácia. |
assertRegistrationCount($n) | Zaznamenalo sa presne $n registrácií. |
assertAuthenticated() | Zaznamenalo sa aspoň jedno úspešné prihlásenie. |
assertAuthenticatedFor($user) | Zaznamenalo sa úspešné prihlásenie pre $user. |
assertAuthenticationFailed() | Aspoň jedno prihlásenie zlyhalo. |
assertAuthenticationCount($n) | Zaznamenalo sa presne $n prihlásení. |
assertRenamed(?$passkey, ?$name) | Zaznamenalo sa premenovanie — tohto kľúča a/alebo na tento názov, ak ich zadáte. |
assertNothingRenamed() | Nezaznamenalo sa žiadne premenovanie. |
assertRevoked(?$passkey) | Zaznamenalo sa odvolanie — tohto kľúča, ak ho zadáte. |
assertNothingRevoked() | Nezaznamenalo sa žiadne odvolanie. |
Asercie vyhadzujú PasskeyAssertionFailed — výnimku balíka, nie PHPUnit aserciu — takže fungujú v akomkoľvek test runneri. Náhrada rešpektuje AuthenticationExpectation rovnako ako skutočné overovanie a for($user)->authenticate() viaže výsledok na daný účet — kľúč nesprávneho vlastníka sa zaznamená ako neúspech a vyhodí CredentialNotFound:
use RoundlyConsulting\Passkeys\Exceptions\CredentialAlreadyRegistered;
use RoundlyConsulting\Passkeys\Models\Passkey;
// A rejected sign-in
$fake = Passkeys::fake()->rejectAuthentication();
$this->postJson('/login/passkey', $payload)->assertUnauthorized();
$fake->assertAuthenticationFailed();
// A registration failure surfaced to the client
$fake = Passkeys::fake()->failRegistrationWith(CredentialAlreadyRegistered::make());
$this->actingAs($user)->postJson('/passkeys', $payload)->assertStatus(422);
$fake->assertNothingRegistered();
// Sign-in resolves to a specific credential
$passkey = Passkey::factory()->es256()->forAuthenticatable($user)->create();
Passkeys::fake()->authenticatesAs($passkey);
$this->postJson('/login/passkey', $payload)->assertOk();Skutočné ceremónie s virtuálnym autentifikátorom
Ak chcete v testoch skutočné overovanie — challenge, origin, hash RP ID, príznaky, podpis, počítadlo, viazanie na používateľa — použite VirtualAuthenticator, softvérový autentifikátor ES256 s atestáciou none:
use RoundlyConsulting\Passkeys\DataTransferObjects\AuthenticationOptionsOverrides;
use RoundlyConsulting\Passkeys\Enums\UserVerification;
use RoundlyConsulting\Passkeys\Facades\Passkeys;
use RoundlyConsulting\Passkeys\Testing\VirtualAuthenticator;
$authenticator = VirtualAuthenticator::es256(); // rpId from the options, origin from config
$keys = Passkeys::for($user);
$keys->register($authenticator->register($keys->registrationOptions()));
$passkey = $keys->authenticate($authenticator->assert($keys->authenticationOptions()));
// Prove your step-up really demands user verification:
$options = $keys->authenticationOptions(new AuthenticationOptionsOverrides(userVerification: UserVerification::Required));
$keys->authenticate($authenticator->assert($options, userVerified: false)); // UserVerificationRequired
$authenticator->assert($options, signCount: 3); // explicit counter, e.g. a cloned key
$authenticator->credentialId(); // base64url, as stored in credential_id- register($options, residentKey: false) simuluje neobjaviteľný kľúč bez userHandle v odpovediach — rovnako ako so skutočným prehliadačom teda odpovie len na možnosti vydané pre jeho používateľa (Passkeys::for($user)->authenticationOptions()); ceremónia bez mena ho odmietne.
- Počítadlo sa pri každom assert() zvýši o jedna, pokiaľ nezadáte signCount; userVerified: false pošle len prítomnosť používateľa.
- Odpovie na akékoľvek možnosti, takže test môže hrať aj útočníka a dokázať, že server odmietne.
- Len na testy: jeho kľúč je jednorazový materiál z TestKeys v crypto-for-laravel, v produkčnom kóde ho nikdy nepoužívajte. V runtime autoloade je len preto, aby ho mohli použiť testy iných balíkov — rovnako ako Passkeys::fake(). Na macOS s PHP z Homebrew môže generovanie kľúča vyžadovať OPENSSL_CONF nasmerované na openssl.cnf z Homebrew.
Factory
Passkey::factory() vytvára platné uložené kľúče pre vaše testy s použitím nakonfigurovaného modelu:
use RoundlyConsulting\Passkeys\Models\Passkey;
$passkey = Passkey::factory()->es256()->forAuthenticatable($user)->create();
$rsa = Passkey::factory()->rs256()->neverUsed()->forAuthenticatable($user)->create();| Stav | Efekt |
|---|---|
es256() | Platný verejný COSE kľúč ES256 (P-256). |
rs256() | Platný verejný COSE kľúč RS256 (RSA-2048). |
withCredentialId($id) | Kľúč s explicitným ID a jeho hashom. |
forAuthenticatable($model) | Kľúč vlastnený konkrétnym modelom. |
neverUsed() | sign_count = 0, last_used_at = null. |
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.