Files
preauth/src/Service/PasskeyCeremonyStoreInterface.php
T
lyra 436450cdc2 Add passkey ceremony store and manager
Builds both WebAuthn ceremonies on top of the library, with real cryptography
proven in tests rather than stubbed:

- PasskeyCeremonyStore: server-authoritative, single-use challenge state in the
  nonceCache pool. The client's challenge copy is never trusted, and consume()
  deletes before verifying so a replay cannot retry the same challenge.
- PasskeyManager: registration and login ceremonies. Library types are confined
  to this class and PasskeyCeremonyFactory. Failures return null rather than
  distinguishing unknown-credential from bad-signature, so the endpoint is not
  an enumeration oracle.
- PasskeyTestHelper: builds genuinely valid ceremonies (real P-256 keypair,
  COSE key, signed authenticatorData, CBOR attestation object).
- PasskeyRealCryptoSpikeTest: proves registration and assertion verify, that
  http:// origins are refused (D4), that challenges and rpIdHash are bound, and
  that a synchronised passkey with a constant zero counter can log in repeatedly.
2026-09-27 10:42:59 +00:00

45 lines
1.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
/**
* Server-side storage for in-flight WebAuthn ceremonies.
*
* Kept separate from {@see PasskeyManager} so the security-critical properties —
* the challenge is server-authoritative and single-use — are testable without
* constructing a real authenticator response.
*/
interface PasskeyCeremonyStoreInterface
{
public const string TYPE_LOGIN = 'login';
public const string TYPE_REGISTER = 'register';
/**
* Start a login (assertion) ceremony.
*
* @return array{ceremonyId: string, challenge: string}
*/
public function startLogin(): array;
/**
* Start a registration (attestation) ceremony for an already-authenticated
* identity.
*
* @return array{ceremonyId: string, challenge: string, userHandle: string}
*/
public function startRegistration(string $identity): array;
/**
* Read and destroy a ceremony.
*
* Returns null when the id is unknown, expired, already consumed, or was
* started for the other ceremony type.
*
* @return array{challenge: string, identity?: string, userHandle?: string}|null
*/
public function consume(string $ceremonyId, string $expectedType): ?array;
}