milpa/auth-webauthn
Composer 安装命令:
composer require milpa/auth-webauthn
包简介
Passkey / WebAuthn for the Milpa PHP framework: the WebAuthnVerifier, ChallengeStore and WebAuthnCredentialStore contracts, ceremony value objects, in-memory defaults, and a lbuchs adapter. A verified assertion mints a session — it is never a per-request transport. Zero framework, zero ORM.
README 文档
README
Milpa Auth-WebAuthn
Passkey / WebAuthn for the Milpa PHP framework: the
WebAuthnVerifier,ChallengeStoreandWebAuthnCredentialStorecontracts, ceremony value objects, in-memory defaults, and a lbuchs/webauthn adapter.
A verified WebAuthn assertion mints a session — it is never a per-request transport.
milpa/auth-webauthn sits one tier above milpa/auth: it
consumes the identity vocabulary milpa/auth defines (Actor, AuthContext, CredentialType::Passkey)
and adds what a WebAuthn/FIDO2 registration-and-authentication ceremony needs on top — the relying
party, the ceremony type, the challenge lifecycle, and the credential store contracts a real
authenticator adapter (lbuchs/webauthn) implements against. Zero framework, zero ORM.
Install
composer require milpa/auth-webauthn
What this package is
milpa/auth-webauthn is the contracts-plus-defaults layer for a WebAuthn/FIDO2 ceremony: the
WebAuthnVerifier port a real authenticator adapter implements, the ChallengeStore and
WebAuthnCredentialStore seams a host wires to its own storage, the ceremony value objects
(RelyingParty, CeremonyType, the options/response/context shapes), in-memory reference
implementations for tests and zero-file consumers, and a lbuchs/webauthn-backed adapter
(Adapter\LbuchsWebAuthnVerifier) that does the actual cryptography. See
ADR 0001 for the three decisions this shape commits to.
A verified assertion mints a session — it is never a per-request transport, so WebAuthnVerifier
is deliberately not a Milpa\Auth\Contracts\CredentialVerifier. verifyAuthentication() returns proof
(WebAuthnAssertionResult), and the host — not this package — turns that proof into a
Milpa\Auth\SessionRecord a later request resolves via Milpa\Auth\Http\StartSession.
The shape
use Milpa\Auth\WebAuthn\Adapter\LbuchsWebAuthnVerifier; use Milpa\Auth\WebAuthn\RelyingParty; use Milpa\Auth\WebAuthn\WebAuthnAuthenticationContext; // The relying party a ceremony runs for — resolved per request/tenant by the host's // RelyingPartyResolver, never read from a single env-static value. $rp = new RelyingParty( id: 'crm.example', name: 'Acme CRM', allowedOrigins: ['https://crm.example'], ); $verifier = new LbuchsWebAuthnVerifier($challengeStore, $credentialStore); // 1. Begin: issue a single-use challenge, hand the browser its options. $options = $verifier->createAuthenticationOptions($rp, new WebAuthnAuthenticationContext()); // $options->toArray() is the JSON body for navigator.credentials.get() // 2. The browser runs the ceremony; the host relays the raw response back as a // WebAuthnAuthenticationResponse (credentialId, clientDataJSON, authenticatorData, // signature, userHandle) — none of this is verified yet. // 3. Finish: verify the assertion fail-closed. This returns PROOF, never a session. $result = $verifier->verifyAuthentication($response, $rp); // The host — not this package — mints the trusted session from that proof: $sessionStore->write(new Milpa\Auth\SessionRecord( id: $newSessionId, actorId: $result->actorId, actorType: Milpa\Auth\ActorType::User, createdAt: $now, expiresAt: $now->modify('+8 hours'), ));
Registration is the mirror image: createRegistrationOptions(Actor, RelyingParty, WebAuthnRegistrationContext)
issues a challenge and creation options; verifyRegistration(WebAuthnRegistrationResponse, RelyingParty)
verifies the ('none') attestation and returns a WebAuthnCredentialRecord for the host to save().
The three seams a host implements
| Contract | Its one job |
|---|---|
ChallengeStore |
issue/consume a single-use, expiring ChallengeRecord between a ceremony's two round-trips. |
WebAuthnCredentialStore |
save/findByCredentialId/listForActor/updateSignCount — the registered-passkey registry. |
RelyingPartyResolver |
resolve(ServerRequestInterface): RelyingParty — which RP a request's ceremony runs under (host-header, tenant path, …), never a single env-static value. |
InMemoryChallengeStore and InMemoryWebAuthnCredentialStore are the reference implementations —
array-backed, with an injectable clock, good for tests and zero-file consumers. Neither is safe for a
clustered production deployment: ChallengeStore::consume() MUST be atomic (delete-on-read) in any
production implementation, or a challenge issued once could be read and reused by two concurrent
requests before either commits its removal. A host ships its own store (Doctrine, Redis, …) behind the
same contract for that guarantee.
Non-goals, stated on purpose
- No enterprise attestation. The shipped adapter fixes attestation to
'none'— it proves possession of a key bound to the RP, not the authenticator's make/model/manufacturer chain. Verifying a specific authenticator's provenance is a future adapter behind the sameWebAuthnVerifierport (see ADR 0001), not a mode flag on this one. - No auth-strength on
Actor. This package does not add a "how strongly was this actor authenticated" field tomilpa/auth'sActororAuthContext.CredentialType::Passkeyis a vocabulary marker for logs, UI, and reports — not a policy inputmilpa/authexposes today. - No
Credential::passkey()factory. A passkey ceremony is stateful and two-round-trip, never a single-shot secret aCredentialVerifierchecks. Passkey exists as a credential type in the vocabulary, not as a per-requestCredentialfactory.
Requirements
- PHP ≥ 8.3
milpa/auth(the identity vocabulary this package builds on)lbuchs/webauthn^2(the shipped cryptography adapter)psr/http-message(theRelyingPartyResolverseam)ext-openssl,ext-mbstring,ext-sodium
License
Apache-2.0 © Rodrigo Vicente - TeamX Agency.
Milpa is developed and maintained by TeamX Agency.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: Apache-2.0
- 更新时间: 2026-07-14