Ithibati
Passkey authentication for Elixir applications: accounts, WebAuthn credentials, recovery codes and revocable sessions. It has no opinion about what an account may do. The web half is optional and built for Phoenix.
Swahili, ithibati: proof, evidence. In WebAuthn's own vocabulary, attestation.
What it provides
- Passkey registration and authentication through WebAuthn and
wax_. - Passkey management: add another passkey, list, rename and revoke existing ones.
- Single-use recovery codes for signing in when a passkey is unavailable.
- Revocable server-side sessions. The browser holds a secret; the database stores its digest. Signing out revokes the session in the database.
- Invitation tokens, expiry and redemption, plus a way to claim the first account on an empty instance. Both open registration and invitation-only applications are supported.
- Changeset helpers and composable
Ecto.Multisteps. Create an account, its first passkey and its recovery codes in one transaction, alongside your own application changes.
What your application owns
You keep your account schema and users table. Ithibati adds the fields and associations it
needs through a schema macro. You choose the identifier field: a username, an email address or
another format your application accepts.
Your application defines roles, memberships and permissions. After Ithibati verifies a passkey, your handler decides whether to create a session and where to send the person next.
You also build the registration, sign-in and recovery-code pages. Ithibati supplies the Phoenix endpoints and browser integration; the examples show how to connect them to a working interface.
For invitations, you own the invitation table and decide what accepting one grants. Your application delivers the link. Ithibati sends no mail, and choosing an email address as an account identifier does not by itself verify ownership of that address. Rate limiting for sign-in and recovery endpoints is also the application's responsibility.
Requirements
- Elixir 1.17 or newer.
- PostgreSQL and an Ecto repo. PostgreSQL is required: the migration checks the existing account table through its database catalogue.
- For the optional web integration: Phoenix 1.8, LiveView 1.1 and Plug. They form one web layer; the identity core can be used without them.
Getting started
Add the dependency:
{:ithibati, "~> 0.1"}
Then follow Getting started. It walks a fresh Phoenix application through configuration, the account schema and migration, an auth handler, routes, browser integration and pages for signing in and saving recovery codes.
The following excerpts show how the pieces fit together. The guide supplies the surrounding modules, imports and configuration.
Your account schema names its identifier and validation format:
alias Ithibati.Schema.Identifier
alias Ithibati.Schema.User
use User, identifier: :username, format: Identifier.username_format()
Your router mounts the registration, authentication and recovery endpoints:
ithibati_routes handler: MyAppWeb.Auth, rp_name: "MyApp"
Your handler receives the verified account. Here it creates a session and returns a redirect:
@impl true
def authenticate(conn, account),
do: {:ok, conn |> Gate.log_in(account) |> json(%{redirect: "/inside"})}
A LiveView starts sign-in by sending an event to the browser hook:
def handle_event("sign-in", _params, socket),
do: {:noreply, socket |> assign(error: nil) |> push_event("ithibati:authenticate", %{})}
The browser lets the person choose a passkey for your site. They do not need to enter a username first. Registration asks for an identifier to create the account.
Once the pieces are in place, run mix ithibati.doctor to check configuration, database tables,
indexes and handler wiring. The guide also shows how to protect a page and sign out.
Without Phoenix
The identity core exposes registration and authentication as ordinary function calls. Your integration supplies the relying-party ID and origin, retains the challenge between requests, and decides what to issue after verification.
For browser clients, priv/static/ithibati.js exports register and authenticate as plain
JavaScript functions. The LiveView hook wraps those same functions.
Registering and signing in covers the Phoenix integration and direct use of the core, including the boundary where a native client or another web framework connects.
Examples and documentation
Two complete Phoenix applications show the supported registration flows:
- Open registration: anyone who reaches the page can choose a username and register a passkey.
- Invitation only: the first account claims the instance; subsequent accounts need an invitation link.
CI compiles both applications, runs their tests and exercises them in a browser.
The documentation includes guides for:
- Configuration and schemas.
- Registration and sign-in.
- Adding and managing passkeys.
- Recovery codes.
- Invitations and the first account.
- Checking your setup with
ithibati.doctor. - Credo checks for consuming applications.
CHANGELOG.md records changes between releases.
Licence
MIT. See LICENSE.