Passkeys, more sign-in providers, and a content seam

Four things. Passkeys (WebAuthn) are wired end to end and proven against a real database with a software authenticator. Social sign-in became one catalogue entry per provider, with GitHub as the second. The blog now reads through a swappable content source, so a CMS can replace MDX without touching routes or SEO. And this changelog rides on that same machinery.

Four changes, each with its own acceptance spec.

Passkeys

@better-auth/passkey is registered on the server and in the browser client. Enrollment lives on the account page beside two-factor; /sign-in leads with a passkey button that asks for no email address, because the assertion already carries the credential id.

Proving it was the interesting part. WebAuthn lives behind navigator.credentials, so a headless browser has nothing to answer the ceremony. Rather than stub it and get a green test that proves nothing, the acceptance spec drives the real endpoints with a software authenticator that generates real ES256 keys and signs real assertions.

You need to do nothing. Passkeys need no keys and no account.

Social sign-in, generalised

socialProviders used to be a single ternary for Google. It is now a catalogue: one entry per provider in lib/social-providers.ts, one line each in socialProvidersFrom, and nothing anywhere else names a provider.

GitHub ships as the second one. Registering the OAuth app is your step — see guide/keys/github.md. Until you do, the button is absent rather than broken.

A content source behind the blog

The blog was hard-wired to content/blog/*.mdx. It now reads through a two-method interface, so moving to a hosted CMS is an adapter rather than a rewrite of every route, the sitemap and the SEO layer:

export interface ContentSource<T> {
  list(): Promise<T[]>;
  get(slug: string): Promise<T | undefined>;
}

You need to do nothing unless you want a CMS, in which case exampleSources in lib/content.ts is the template.

This changelog

A second collection behind that same interface, fed into the same page registry — so it has metadata, a canonical, an OG image, a sitemap entry and an RSS feed without a line of changelog-specific SEO code.

← All Releases