PHPackages                             milpa/auth-webauthn - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. milpa/auth-webauthn

ActiveLibrary[Framework](/categories/framework)

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.

v0.1.1(1mo ago)05Apache-2.0PHPPHP &gt;=8.3CI passing

Since Jul 14Pushed 2w agoCompare

[ Source](https://github.com/getmilpa/auth-webauthn)[ Packagist](https://packagist.org/packages/milpa/auth-webauthn)[ RSS](/packages/milpa-auth-webauthn/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (16)Versions (3)Used By (0)

 [   ![Milpa](https://raw.githubusercontent.com/getmilpa/core/main/art/lockup/milpa-lockup-v-color-light.svg)  ](https://github.com/getmilpa)

Milpa Auth-WebAuthn
===================

[](#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/webauthn](https://github.com/lbuchs/WebAuthn) adapter.

[![License](https://camo.githubusercontent.com/798509b4df525f56802b56f8096862487f08023e3d7561c68656f8dab10d0d6e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4170616368652d2d322e302d626c75652e737667)](LICENSE)[![PHP](https://camo.githubusercontent.com/ca03f11ea27dac4dedc8ad56a7bdfc4a9ff5feb825055f9d2983616115076607/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135253230382e332d3737376262342e737667)](https://www.php.net/)

**A verified WebAuthn assertion mints a session — it is never a per-request transport.**

`milpa/auth-webauthn` sits one tier above [`milpa/auth`](https://github.com/getmilpa/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
-------

[](#install)

```
composer require milpa/auth-webauthn
```

What this package is
--------------------

[](#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](docs/adr/0001-webauthn-mints-a-session.md) 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
---------

[](#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
---------------------------------

[](#the-three-seams-a-host-implements)

ContractIts 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
----------------------------

[](#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 same `WebAuthnVerifier` port (see [ADR 0001](docs/adr/0001-webauthn-mints-a-session.md)), 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 to `milpa/auth`'s `Actor` or `AuthContext`. `CredentialType::Passkey` is a vocabulary marker for logs, UI, and reports — not a policy input `milpa/auth` exposes today.
- **No `Credential::passkey()` factory.** A passkey ceremony is stateful and two-round-trip, never a single-shot secret a `CredentialVerifier` checks. Passkey exists as a credential type in the vocabulary, not as a per-request `Credential` factory.

Requirements
------------

[](#requirements)

- PHP **≥ 8.3**
- `milpa/auth` (the identity vocabulary this package builds on)
- `lbuchs/webauthn` `^2` (the shipped cryptography adapter)
- `psr/http-message` (the `RelyingPartyResolver` seam)
- `ext-openssl`, `ext-mbstring`, `ext-sodium`

License
-------

[](#license)

[Apache-2.0](LICENSE) © Rodrigo Vicente - TeamX Agency.

---

Milpa is developed and maintained by [TeamX Agency](https://teamx.agency).

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance95

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 87.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~15 days

Total

2

Last Release

32d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1993784?v=4)[rodrigomx](/maintainers/rodrigomx)[@rodrigomx](https://github.com/rodrigomx)

---

Top Contributors

[![rodrigoteamx](https://avatars.githubusercontent.com/u/269849276?v=4)](https://github.com/rodrigoteamx "rodrigoteamx (14 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (2 commits)")

---

Tags

authenticationfido2frameworkmilpapasskeysphpsecuritywebauthnphpframeworksecurityAuthenticationFIDO2webauthnpasskeysmilpa

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/milpa-auth-webauthn/health.svg)

```
[![Health](https://phpackages.com/badges/milpa-auth-webauthn/health.svg)](https://phpackages.com/packages/milpa-auth-webauthn)
```

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k87.7M2.3k](/packages/symfony-symfony)[neuron-core/neuron-ai

The PHP Agentic Framework.

2.1k1.0M59](/packages/neuron-core-neuron-ai)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)[tempest/framework

The PHP framework that gets out of your way.

2.3k42.4k21](/packages/tempest-framework)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

86538.6k](/packages/flow-php-flow)[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

36863.5k2](/packages/telnyx-telnyx-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
