Rozšírenie
Service provider viaže každý kontrakt na predvolený singleton. Naviažte vlastnú implementáciu a vymení sa všade:
| Kontrakt | Predvolená väzba | Účel |
|---|---|---|
PasskeyService | PasskeyManager | Koreň fasády — for($user), ceremónia bez mena a attestationFormats(). |
ChallengeRepository | CacheChallengeRepository | Jednorazové úložisko challenge s TTL. |
AttestationVerifier | AttestationGate | Politika dôvery atestácie — rebríček, kotvy, allowlist AAGUID. |
HasPasskeys | Váš model cez InteractsWithPasskeys | Model, ktorý vlastní prístupové kľúče. |
Namiesto výmeny celej služby radšej previažte jednu akciu (napríklad RevokePasskeyAction) — manažér aj handle účtu získavajú každú akciu z kontajnera, takže zmena platí pre fasádu, injektovanú službu aj metódy modelu. Úplne vlastný PasskeyService implementuje štyri metódy — for($user), ktorá vracia handle UserPasskeys, authenticationOptions(?$overrides), authenticate($response, ?$expect) a attestationFormats().
Vlastné úložisko challenge
CacheChallengeRepository ukladá každú challenge pod passkeys:challenge:<ceremonyId> v cache store-e podľa challenge.store a pull() ju načíta a zmaže pod zámkom store-u, takže z dvoch súbežných požiadaviek na jednu ceremóniu ju dostane práve jedna. Ak chcete challenge ukladať inde, implementujte kontrakt a naviažte ho:
use RoundlyConsulting\Passkeys\Contracts\ChallengeRepository;
use RoundlyConsulting\Passkeys\DataTransferObjects\ChallengeData;
final class DatabaseChallengeRepository implements ChallengeRepository
{
public function put(string $ceremonyId, ChallengeData $challenge, int $ttl): void { /* … */ }
public function pull(string $ceremonyId): ?ChallengeData { /* fetch + delete atomically */ }
}
// In a service provider:
$this->app->singleton(ChallengeRepository::class, DatabaseChallengeRepository::class);pull() musí vrátiť null, ak challenge chýba, vypršala alebo už bola spotrebovaná, a musí zostať jednorazová aj pri súbežnosti — samotné načítanie a následné samostatné zmazanie nestačí.
Pridanie formátu atestácie
Atestácia je rozdelená na dve časti. Overovače formátov dokazujú len matematiku a vracajú AttestationResult; o dôvere rozhoduje výhradne AttestationGate — rebríček, prepojenie reťazca, platnosť, kotvy aj allowlist AAGUID. Napíšte overovač:
use RoundlyConsulting\Crypto\Cose\AuthenticatorData;
use RoundlyConsulting\Passkeys\Attestation\AttestationResult;
use RoundlyConsulting\Passkeys\Attestation\AttestationVerifier;
use RoundlyConsulting\Passkeys\DataTransferObjects\AttestationObject;
use RoundlyConsulting\Passkeys\Enums\AttestationType;
final class AndroidSafetynetVerifier implements AttestationVerifier
{
public function verify(
AttestationObject $attestation,
AuthenticatorData $authenticatorData,
string $clientDataHash,
): AttestationResult {
// Prove the maths; throw InvalidAttestation on any failure. Never rule on
// trust — the gate owns that.
return AttestationResult::chained('android-safetynet', AttestationType::Basic, $chain);
}
}Potom znovu naviažte register s pridaným overovačom — všetky rozhodnutia o dôvere zostávajú v gate:
use Illuminate\Contracts\Foundation\Application;
use RoundlyConsulting\Passkeys\Attestation\AppleAttestationVerifier;
use RoundlyConsulting\Passkeys\Attestation\AttestationVerifierRegistry;
use RoundlyConsulting\Passkeys\Attestation\NoneAttestationVerifier;
use RoundlyConsulting\Passkeys\Attestation\PackedAttestationVerifier;
use RoundlyConsulting\Passkeys\Support\CredentialCrypto;
// In a service provider:
$this->app->singleton(AttestationVerifierRegistry::class, fn (Application $app) => new AttestationVerifierRegistry([
'none' => new NoneAttestationVerifier,
'packed' => new PackedAttestationVerifier($app->make(CredentialCrypto::class)),
'apple' => new AppleAttestationVerifier,
'android-safetynet' => new AndroidSafetynetVerifier,
]));Reťazce certifikátov skladajte zo surových DER bajtov, ktoré nesie CBOR, nikdy nie z base64 pomocníka pre x5c — ten zodpovedá tvaru JOSE a odmietol by každý skutočný autentifikátor. Ak chcete celú politiku atestácie nahradiť, naviažte vlastný AttestationVerifier; ceremónia volá len ten.
Vlastné napojenie používateľa
Ak potrebujete špecifické ukladanie identifikátora alebo odvodenie mena, implementujte HasPasskeys priamo namiesto traitu. Ceremónie potrebujú len štyri metódy kontraktu — dbajte len na to, aby passkeyUserHandle() zostal stabilný.
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.