Version 0.5 removes the non-atomic passkey-registration fallback from
WebAuthnAdapter. Authentication, challenge consumption, credential listing,
counter advancement, and deletion remain on the storage adapter. Registration
adds a separate persistence capability because only the host application can
atomically combine account eligibility, its credential cap, and insertion.
Any webauthn configuration now requires adapters.webauthn to satisfy
WebAuthnRegistrationAdapter:
import type {
CreateWebAuthnCredentialWithinLimitInput,
WebAuthnCredentialCreationAdapter,
WebAuthnCredentialCreationOutcome
} from '@goobits/auth/adapters/webauthn'
class AppWebAuthnAdapter
extends YourWebAuthnAdapter
implements WebAuthnCredentialCreationAdapter
{
async createCredentialWithinLimit(
input: CreateWebAuthnCredentialWithinLimitInput
): Promise<WebAuthnCredentialCreationOutcome> {
return yourOwnerScopedTransaction(input)
}
}Custom adapters that previously inherited the base implementation must add
this method. The built-in D1, Drizzle, and PostgreSQL adapters remain valid for
storage, authentication, and management, but no longer claim to provide the
application-owned registration transaction. MemoryWebAuthnAdapter implements
the complete registration contract for one process.
Inside one transaction or owner-scoped lock:
- Verify that the credential owner still exists and may add a sign-in method.
- Count the owner's current credentials and return
limit-reachedat the cap. - Insert by globally unique credential ID without replacing an existing owner.
- Return
created,duplicate,limit-reached, orowner-unavailable.
Do not implement the method as listCredentials() followed by
createCredential(). Concurrent requests can both observe space and exceed the
cap. For SQL applications, lock the canonical account/lifecycle row (or use an
equivalent serialized transaction) before counting and inserting.
Typecheck every application that enables webauthn. Add a concurrency test
against the production database when possible: submit more registrations than
the configured cap for one owner and assert that no more than the cap persist.