NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

Aj prihlásenie sú dve volania — možnosti smerom von, podpísaná odpoveď dnu. Balík nájde kľúč a jeho vlastníka; session alebo token potom vydá vaša aplikácia:

use RoundlyConsulting\Passkeys\Facades\Passkeys;
use RoundlyConsulting\Passkeys\DataTransferObjects\AuthenticationResponseData;

// 1. Server -> client: request options (usernameless — any account's passkey can answer).
$options = Passkeys::authenticationOptions();      // or Passkeys::for($user)->authenticationOptions()
return response()->json($options);                 // feed publicKey to navigator.credentials.get()

// 2. Client -> server: verify the assertion; the resolved credential's owner is reachable
//    via $passkey->authenticatable. The host then issues its own session or token.
$passkey = Passkeys::authenticate(
    AuthenticationResponseData::fromArray($request->validated()),
);
$user = $passkey->authenticatable;

Bez mena alebo viazané na používateľa

  • Bez používateľa — allowCredentials je prázdne, takže autentifikátor ponúkne ľubovoľný objaviteľný kľúč pre vašu RP a nahlási jeho identifikátor používateľa. Odpoveď musí tento userHandle obsahovať (WebAuthn §7.2, krok 6); odpoveď bez neho dostane jednotné CredentialNotFound.
  • S používateľom, cez Passkeys::for($user)->authenticationOptions() — allowCredentials obsahuje kľúče tohto používateľa a ceremónia je na ne viazaná: dokončiť ju môže len ponúknutý kľúč. Kľúč iného účtu alebo kľúč pridaný až po vydaní možností dostane rovnakú jednotnú odpoveď CredentialNotFound ako neznámy kľúč.

authenticationOptions() vracia RequestOptionsData, ktorý sa serializuje do tvaru pre navigator.credentials.get({ publicKey }). ceremonyId sa vracia spolu s odpoveďou, aby bezstavová aplikácia vedela priradiť challenge:

{
  "ceremonyId": "…40 chars…",
  "publicKey": {
    "challenge": "…b64url…",
    "rpId": "example.com",
    "timeout": 60000,
    "userVerification": "required",
    "allowCredentials": []
  }
}

Prihlásenie používateľa

AuthenticationResponseData::fromArray() načíta rawId (alebo id), ceremonyId, response.clientDataJSON, response.authenticatorData, response.signature a voliteľný response.userHandle. Balík nikoho neprihlasuje — to zostáva na vás:

use RoundlyConsulting\Passkeys\DataTransferObjects\AuthenticationResponseData;

$response = AuthenticationResponseData::fromArray($request->all());
$passkey  = Passkeys::authenticate($response);          // Passkey

// The host owns login — start its own session/token:
Auth::login($passkey->authenticatable);

Čo overí authenticate()

  • Kľúč sa vyhľadá podľa ID; každý neúspech je jednotné CredentialNotFound, takže registrovaných používateľov nemožno zistiť. Ak príde userHandle, musí sa zhodovať s uloženým (porovnanie v konštantnom čase).
  • Ak ste zadali AuthenticationExpectation, overí sa hneď potom — ešte pred spotrebovaním challenge a pred akýmkoľvek zápisom.
  • clientDataJSON sa dá dekódovať a jeho typ je webauthn.get.
  • Challenge bola vydaná pre prihlásenie a zhoduje sa v konštantnom čase; pri challenge viazanej na používateľa musí kľúč patriť tomuto používateľovi a byť medzi ponúknutými a pri challenge bez mena musí odpoveď obsahovať userHandle.
  • Allowlist originov a cross-origin politika sú splnené.
  • Hash RP ID sa zhoduje; prítomnosť používateľa je nastavená, overenie používateľa tiež, ak ho ceremónia vyžaduje, zálohovaný kľúč je na zálohu oprávnený a oprávnenosť na zálohu sa stále zhoduje s hodnotou uloženou pri registrácii (WebAuthn L3 §7.2, krok 19).
  • Podpis nad authenticatorData ‖ sha256(clientDataJSON) sa overí uloženým verejným kľúčom.
  • Zosúladí sa počítadlo podpisov, zaznamená sa stav zálohy a spustí sa PasskeyAuthenticated.

Zosúladenie počítadla podpisov

sign_count chráni pred klonovanými autentifikátormi. Počítadlo sa posúva vždy len dopredu a rozhoduje databáza: posun je jeden podmienený UPDATE … WHERE sign_count < ?, takže nižšiu hodnotu nezapíše späť ani pokles, ani dva súbežné podpisy. Pri každom úspešnom podpise:

  • Vyššia hodnota posunie sign_count; stav zálohy z podpisu a last_used_at sa zapíšu v tom istom príkaze.
  • Ak je uložená aj prijatá hodnota 0 (autentifikátory bez počítadla), použitie sa zaznamená — no len kým je uložená hodnota stále 0.
  • Ak sa počítadlo neposunulo, rozhoduje sign_count_policy: reject vyhodí SignCountRegression a prihlásenie preruší; flag (predvolené) spustí PasskeySignCountRegressed, zapíše stav zálohy a last_used_at bez zmeny počítadla a prihlásenie pustí.
  • Uložená hodnota sa nikdy nezníži, takže pri flag sa klonovaný autentifikátor označí pri každom svojom podpise, nielen pri prvom.
  • Kľúč odvolaný počas overovania podpisu skončí výnimkou CredentialNotFound.

Priame použitie akcií

Volania bez mena aj volania viazané na používateľa zdieľajú tie isté dve akcie; používateľ aj očakávanie sú voliteľné (pozri DI a akcie):

use RoundlyConsulting\Passkeys\Actions\GenerateAuthenticationOptionsAction;
use RoundlyConsulting\Passkeys\Actions\VerifyAuthenticationAction;

$options = app(GenerateAuthenticationOptionsAction::class)->execute($user, $overrides); // both optional
$passkey = app(VerifyAuthenticationAction::class)->execute($response, $expectation);  // expectation optional

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 kryptomien

Odoslaní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.