Ithibati

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

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

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:

CI compiles both applications, runs their tests and exercises them in a browser.

The documentation includes guides for:

CHANGELOG.md records changes between releases.

Licence

MIT. See LICENSE.