Modules
Authentication for Phoenix hosts: one contract, with a backend behind it.
What an app implements to decide what a user may do: one callback,
authorize/3. It is the app's, not the backend's: a backend says who
someone is, the app says what they may do. The app names its module
What a backend implements: who someone is, and their sessions. Nothing
else. The plugs, hooks, steps, paths and the revocation push are
Macaw.Auth's, the same for every backend, so a backend never
reimplements them. And one check: check_config!/0, which the
application calls at start, so a misconfigured backend refuses boot.
The conformance kit: what every Macaw.Auth.Backend must do, as tests a
backend's author runs.
What a backend gives the conformance kit.
The LiveView side of Macaw.Auth
The native backend: users and sessions in the host's database, passwords checked by a chain of sources (local password hashes, LDAP directories), sign-in failures throttled.
The native backend's configuration, read once and parsed into structs,
every fault named (exio's AuthConfig approach)
A fault in the native backend's configuration, named.
How Macaw.Auth.Native hashes passwords: a behavior with two
implementations, Macaw.Auth.Native.Hasher.Bcrypt and
Macaw.Auth.Native.Hasher.Argon2. Each library is an optional
dependency, and each implementation is compiled only when its library is,
so a host carries only the NIF it chose.
Argon2 (:argon2_elixir), compiled only when that library is loaded.
The parameters are argon2_elixir's own settings (config :argon2_elixir, t_cost: 3, m_cost: 16, parallelism: 4, argon2_type: 2); a hash made
with others is rehashed at sign-in.
Bcrypt (:bcrypt_elixir), compiled only when that library is loaded.
The cost is bcrypt_elixir's own setting, config :bcrypt_elixir, log_rounds: 12; a hash made at another cost is rehashed at sign-in.
Just-in-time provisioning of directory users (ported from exio's
External.resolve/2): a directory user signing in for the first time
gets a macaw_auth_users row, with source the directory's name and no
password, and at every later sign-in the directory's word replaces the
row's roles and display name. The directory is the authority on who is
in which group, so a user removed from an admin group loses the role at
their next sign-in, not whenever someone remembers to edit the row.
A row of macaw_auth_recovery_codes: one unspent recovery code of a
user's second factor (Macaw.Auth.Native.Totp).
The native backend's user commands as release entry points, for an
app's <app>-ctl (macaw_host's generated script), on macaw_host's
release protocol (Macaw.Host.Release)
Sealing at rest: what the native backend stores that must not be read
back from the table (a TOTP secret) is sealed with AES-256-GCM, OTP's
:crypto, under a key from the configuration (sealing_key:,
Macaw.Auth.Native.Config).
The sealing key is missing, too short, or not the one a stored value was sealed with. The message names the fault, never the key.
A row of macaw_auth_sessions, the native backend's sessions, in the
host's repo (mix macaw_auth.gen.migration writes the table's
migration, separate from the users').
The native backend's sessions (ported from exio's Accounts.Sessions),
in macaw_auth_sessions (Macaw.Auth.Native.SessionRecord).
Where Macaw.Auth.Native checks a password: a behavior, and the chain
that tries the configured sources in order.
A directory as a sign-in source, over OTP's :eldap (no external LDAP
library). Ported from exio's, the copy with the security fixes, plus
macaw's allow_plaintext: false. The configuration is
Macaw.Auth.Native.Source.Ldap.Config's
An LDAP source's configuration, validated with its defaults filled
(ported from exio's and macaw's LdapSource), so a bad one is refused
naming the source and the key: sources[nas].host: is required.
Escaping for values placed in LDAP distinguished names and search filters, so a username can never change a DN or widen a search. Ported from exio's, with two fixes.
RFC 4515 search filters as :eldap terms, built with the username as
data. Ported from exio's parser, with one change in how the username gets
in.
Local users: a macaw_auth_users row with a password hash.
Sign-in throttling for Macaw.Auth.Native.authenticate/2 (ported from
exio's and macaw's Accounts.Throttle, which are the same mechanism).
The native backend's second factor: TOTP and recovery codes (ported from
exio's and macaw's Accounts.Totp, which are the same rules), on the
macaw_auth_users row and in macaw_auth_recovery_codes.
The user commands of mix macaw_auth.user and of the release entry
points (Macaw.Auth.Native.Release), one implementation for both
A row of macaw_auth_users, the native backend's users, in the host's
repo (mix macaw_auth.gen.migration writes the table's migration).
The request side of Macaw.Auth, for a Phoenix router.
One signed-in session, as every backend reports it.
Macaw.Auth.Native.Throttle as a model (spec/10 §7): the table of failures, shared by
every sign-in in flight, and what each call does to it.
The signed-in user, as every backend reports it.
Mix Tasks
Writes the migrations that create the native backend's tables into the host's migrations, one file each
Runs an LDAP source's sign-in flow for a user without signing them in, and prints how each step went (connect, TLS, finding the DN, bind, attributes, groups, required group, roles), stopping at the first that failed
Administers the native backend's users, against the configured repo