oliverandrich

oliverandrich

I have released the first version of Ithibati, a passkey-authentication library for Elixir. It came out of my own blog engine, where the accounts context had grown into the one part I did not want to write a third time.

Swahili, ithibati: proof, evidence. In WebAuthn’s own vocabulary, attestation.

Why another one

Most authentication libraries answer two questions at once: Who is this, and what may they do? The second answer is different in every application - sites, tenants, teams, roles, invitations - which is why the first one so rarely gets reused. Ithibati answers only the first. It knows the account row, its passkeys, its recovery codes, and its sessions, and it knows nothing about what that account may then do.

You keep your own users table. The library contributes a schema macro and the changeset pieces that validate the field an account is known by; it never assumes a column the macro did not put there.

What using it looks like

Five touchpoints, and they are close to the whole surface.

# mix.exs
{:ithibati, "~> 0.1"}

One line in your account schema names the field an account is known by:

use Ithibati.Schema.User, identifier: :username, format: Identifier.username_format()

One line in your router mounts the ceremony endpoints:

ithibati_routes handler: MyAppWeb.Auth, rp_name: "MyApp"

Your handler decides what a verified assertion is worth. Ithibati hands you the account and issues nothing of its own:

@impl true
def authenticate(conn, account),
  do: {:ok, conn |> Gate.log_in(account) |> json(%{redirect: "/"})}

And a page starts a ceremony by pushing an event to the browser hook:

def handle_event("sign-in", _params, socket),
  do: {:noreply, push_event(socket, "ithibati:authenticate", %{})}

There is no password field anywhere and no sign-in form either. A sign-in challenge names no credentials, so the browser offers whichever passkeys it holds for your site, and the person picks one. Registering is the half that still needs a form because the name does not exist yet.

What is in the box

  • Passkey registration and authentication through wax_, including the first account on an empty instance.
  • Single-use recovery codes, twelve by default, which refill themselves when the last one is spent. There is an endpoint for signing in with one.
  • Revocable server-side sessions. The cookie carries the secret, the row carries its sha256, and signing out revokes the row rather than forgetting it. It also ends the LiveViews that session opened, when your endpoint has a pubsub server.
  • Invitations, optional: Ithibati owns the token, its expiry, and the redemption, and you own the table and whatever the invitation grants.
  • Composable Ecto.Multi fragments, so creating an account, its first passkey and its recovery codes is one transaction that you hang your steps on.
  • mix ithibati.doctor, which asks twelve questions about a setup and says which one is wrong.
  • Three Credo checks a consuming project can switch on, if you want the boundaries enforced rather than merely written down.

The Phoenix half - the routes, a gate, a LiveView hook - is optional, and phoenix,phoenix_live_view and plug are optional together. Without them you still get a core that compiles, and a ceremony is a handful of function calls. priv/static/ithibati.js exportsregister and authenticate as plain functions, so a browser extension or a native client can use the same path.

What it deliberately does not do

  • API tokens. What makes one is the scopes, the rotation, the expiry chosen per token, the page where somebody sees and revokes theirs, and the audit trail. None of that is here. A session is the only credential the table holds.
  • Send mail. An application that mails an invitation link itself has proved the address by the time the account exists, because accepting an invitation refuses an account created under any other identifier. That is the whole of the address verification on offer.
  • Decide what an account may do. Roles, tenancy, and memberships stay yours.

The small print

It is a first release, and Elixir 1.17 and up.

It needs Postgres today. Two places read the database catalogue directly, which is what lets the migration and mix ithibati.doctor tell you which column or index your own table is missing rather than failing later with an undefined_column. The races are settled with a FOR NO KEY UPDATE row lock. Whether either of those has to stay a requirement is a question I have not finished answering, and a single-user instance on SQLite is the case I keep coming back to.

There are two example applications in the repository, one for open registration and one for invitation-only, and CI drives both in a real browser so they cannot quietly rot. Read those when the guides and your editor disagree.

I would rather hear that a decision is wrong now than after somebody has built on it, so criticism of the shape is as welcome as a bug report.

Showing Posts 1 to 5

wintermeyer

wintermeyer

Great work! Thanks!

type1fool

type1fool

Nice work, and thank you for sharing! It is exciting to see new open source packages, particularly for Passkey support in Elixir apps.

Choices around scope are what I find most interesting when comparing packages in the authentication/authorization space. I think you’ve made a good choice in leaving authorization, OAuth, and most tangential concerns out of Ithibati.

I am preparing updates to WebauthnComponents, which is focused on adding Passkey support for LiveView apps, and I have been narrowing scope a bit further this week.

IMHO, it was a slight mistake to position WebauthnComponents primarily as an alternative to phx.gen.auth. This week, I have removed the mix task for generating LiveViews, schemas, migrations, etc. So WC will have no session management, nor many of the features of Ithibati - it will simply abstract the client-server coordination during registration and authentication and send messages up to the LiveView.

This will also make the package less opinionated about the persistence layer. The documentation may eventually include guidance for CRUD and Event Sourcing/CQRS patterns.

It will be interesting to hear feedback. Cheers!

asianfilm

asianfilm

Love this direction.

I’m using event sourcing and want to add passkeys as 2FA for compliance with the April 2026 edition of the UK’s CyberEssentials standard, which is needed for many government contracts. Previously only admin accounts required 2FA. CE2026 also demands that you log people out after 15 minutes of inactivity.

oliverandrich

oliverandrich OP

Oh, this sounds fascinating. I am curious to see what changes you apply and how you sharpen the profile of your library. I think I gave it a try in the beginning, but I can’t remember where I struggled with it. I ended up with wax_ and wrapped it with my own code.

And I strongly agree; we need to have good Passkey support for Elixir and good solutions for different scopes. I came from a hobbyist view on the topic. I earn my money with Python/Django development, and Elixir/Phoenix is my stack for all my site projects. And I have a “customer one” that is not a fan of strong password policies, and loves all the Passkey stuff she gets on her iPhone. :slight_smile:

oliverandrich

oliverandrich OP

Just release 0.2.0 of Ithibati that closed some documentation and API issues.

— All posts loaded —

Where Next? Top

Trending in Announcing Top

restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 12366 136
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
ausimian
Castle is a package I put together to assist with building Elixir releases capable of being live-upgraded via the ‘official’ release hand...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
kip
In 2021 I started a new library called Tempo with the objective of modelling time as a set of intervals - not as instants. In 2022 I gave...
New

Other Trending Topics Top

budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews