Ithibati.Web.Gate (Ithibati v0.1.0)

Copy Markdown View Source

Who is signed in, on a connection and in a LiveView, from one place.

pipeline :browser do
  plug :fetch_session
  plug Ithibati.Web.Gate, :current_account
end

live_session :admin, on_mount: [{Ithibati.Web.Gate, {:require_account, to: ~p"/sign-in"}}] do
  live "/admin", AdminLive
end

The gate has two modes and no others. :current_account assigns whoever the session names, or nil, and always continues. :require_account refuses when there is nobody.

An unrecognised mode raises rather than being ignored. The plug raises from init/1, which Phoenix runs at compile time under init_mode: :compile and at the first request otherwise; the on_mount raises at the mount either way. A gate that listed its modes and let anything else through would turn a typo into a page that refuses nobody, and nothing would report it.

The gate covers authentication and stops there. What an account may do is the application's, and Ithibati has no opinion about it.

Summary

Functions

The topic the sockets of one session answer on.

Signs an account in by storing a session token under Ithibati's session key.

Signs out and revokes the token rather than merely forgetting it.

The same two modes, for LiveView.

The session key Ithibati stores its token under.

Functions

live_socket_id(token)

The topic the sockets of one session answer on.

Ithibati derives the topic from the token's digest. A topic reaches logs, telemetry and everything subscribed to the pubsub server, and phx.gen.auth puts the live token itself in there. The topic is per token rather than per account, so signing out in one browser leaves the same person's other devices alone.

This function is public for an application that ends a session somewhere other than log_out/1 and holds the raw token while doing it. It cannot serve "sign out my other devices": that starts from what the database has, which is digests.

log_in(conn, account)

Signs an account in by storing a session token under Ithibati's session key.

Ithibati offers this rather than imposing it. An application decides in Ithibati.Web.Handler what a verified assertion is worth, and one that issues a bearer token for an extension instead simply never calls this. The gate then finds nothing, which is the right answer. But a gate that read a key nothing here ever wrote would leave every application guessing the convention.

log_in/2 renews the session first, because a fixed session id handed to someone before they sign in is a session an attacker already holds afterwards. Renewing clears the CSRF token along with everything else, so a sign-in has to end in a full page load. The hook does that when a handler answers with %{redirect: …}. A page that stays put after signing in holds a token the new session has never heard of, and its next form post is refused.

log_out(conn)

Signs out and revokes the token rather than merely forgetting it.

A session dropped on the client alone leaves a token that still resolves. That is the sign-out counterpart of a replayed assertion, and it fails just as quietly.

on_mount(mode, params, session, socket)

The same two modes, for LiveView.

:require_account takes :to here and has no default. A LiveView that halts with nowhere to send a person is a dead end, and the path belongs to the application.

session_key()

The session key Ithibati stores its token under.