Macaw.Auth.Native.Hasher behaviour (macaw_auth v0.4.1)

Copy Markdown View Source

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.

config :macaw_auth, Macaw.Auth.Native, hasher: Macaw.Auth.Native.Hasher.Argon2

The default is Bcrypt. A configured hasher whose library is missing is refused when the configuration is read (Macaw.Auth.Native.Config), naming the dependency to add, never at the first sign-in.

Rehashing. A stored hash is replaced at a successful sign-in when it needs_rehash?/2: made with other parameters (a raised bcrypt cost), or by the other algorithm. A hash carries its algorithm's prefix, so moving from bcrypt to argon2 needs no flag day: a bcrypt hash still verifies with bcrypt (when :bcrypt_elixir is still loaded) and is rehashed with argon2 on that user's next sign-in.

No enumeration. no_user_verify/0 spends a verification's time and returns false, so refusing an unknown user takes as long as refusing a wrong password.

Summary

Callbacks

A new hash of password, with the configured parameters.

Whether hash, one of this algorithm's, was made with other parameters than now.

Spends a verification's time and returns false.

Whether hash is one of this algorithm's (by its prefix).

Whether password matches hash (constant time).

Functions

Whether module can hash here: compiled (its library is loaded) and implementing this behavior, every callback of it. :ok or {:error, message}.

Whether hash should be replaced by a configured one.

Verifies password against a stored hash with whichever available algorithm made it. A hash no available algorithm owns (or none at all) spends the configured hasher's time and is false.

Callbacks

hash(password)

@callback hash(password :: String.t()) :: String.t()

A new hash of password, with the configured parameters.

needs_rehash?(hash)

@callback needs_rehash?(hash :: String.t()) :: boolean()

Whether hash, one of this algorithm's, was made with other parameters than now.

no_user_verify()

@callback no_user_verify() :: false

Spends a verification's time and returns false.

owns?(hash)

@callback owns?(hash :: String.t()) :: boolean()

Whether hash is one of this algorithm's (by its prefix).

verify(password, hash)

@callback verify(password :: String.t(), hash :: String.t()) :: boolean()

Whether password matches hash (constant time).

Functions

check(module)

@spec check(module()) :: :ok | {:error, String.t()}

Whether module can hash here: compiled (its library is loaded) and implementing this behavior, every callback of it. :ok or {:error, message}.

needs_rehash?(configured, hash)

@spec needs_rehash?(module(), String.t()) :: boolean()

Whether hash should be replaced by a configured one.

verify(configured, password, hash)

@spec verify(module(), String.t(), String.t() | nil) :: boolean()

Verifies password against a stored hash with whichever available algorithm made it. A hash no available algorithm owns (or none at all) spends the configured hasher's time and is false.