ExPassword (expassword v0.2.0) View Source

Documentation for ExPassword.

Link to this section Summary

Functions

Extracts the options and the algorithm used to generate hash.

Hashes password using the given algorithm and options.

Returns true if the hash has not been issued by algorithm or options are different from the one used to generate hash

Checks if password matches the given hash

This a convenient function which handles ExPassword.verify?/2 and ExPassword.needs_rehash?/3 for you (and takes care of timing attacks)

Link to this section Types

Specs

algorithm() :: module()

Link to this section Functions

See ExPassword.Registry.available_algorithms/0.

Specs

get_options(hash :: ExPassword.Algorithm.hash()) ::
  {:ok, ExPassword.Algorithm.options()} | {:error, :invalid}

Extracts the options and the algorithm used to generate hash.

Returns {:error, :invalid} if hash is invalid or not recognized by available_algorithms/0.

Link to this function

hash(algorithm, password, options)

View Source

Specs

hash(
  algorithm :: algorithm(),
  password :: ExPassword.Algorithm.password(),
  options :: ExPassword.Algorithm.options()
) :: ExPassword.Algorithm.hash() | no_return()

Hashes password using the given algorithm and options.

algorithm has to be a module present in available_algorithms/0.

Link to this function

needs_rehash?(algorithm, hash, options)

View Source

Specs

needs_rehash?(
  algorithm :: algorithm(),
  hash :: ExPassword.Algorithm.hash(),
  options :: ExPassword.Algorithm.options()
) :: boolean() | no_return()

Returns true if the hash has not been issued by algorithm or options are different from the one used to generate hash

Specs

verify?(
  password :: ExPassword.Algorithm.password(),
  hash :: ExPassword.Algorithm.hash()
) :: boolean() | no_return()

Checks if password matches the given hash

Raises a ExPassword.UnidentifiedAlgorithmError error if any of available_algorithms/0 recognizes hash

Link to this function

verify_and_rehash_if_needed(user, password, field \\ :encrypted_password, algorithm, options, changes \\ [])

View Source

This a convenient function which handles ExPassword.verify?/2 and ExPassword.needs_rehash?/3 for you (and takes care of timing attacks)

Returns:

  • {:error, :user_is_nil}: if user is nil (not found in your repo, typically after a Repo.get_by/3 operation)
  • {:error, :password_missmatch}: if password does not match the hash in user.field
  • {:ok, []}: if password is correct (matches the hash in user.field) and does not need to be updated according to algorithm nor options
  • {:ok, a non empty keyword-list}: if password is correct but needs to be updated, meaning the algorithm and/or options extracted from the hash (user.field) are different from algorithm and/or options to allow you to then update user's hash

A typical usage would be:

# in config/*
config :my_app,
  password_algorithm: ExPassword.Bcrypt,
  password_options: %{cost: 11}

algorithm = Application.fetch_env!(:my_app, :password_algorithm)
options = Application.fetch_env!(:my_app, :password_options)
user = MyApp.Context.Users.get_user_by(name: login)
case ExPassword.verify_and_rehash_if_needed(user, password, algorithm, options) do
  {:ok, changes} ->
    if changes != [] do
      user
      |> Ecto.Changeset.change(changes)
      |> MyApp.Repo.update!()
    end
    # ... (successful authentication) ...
  {:error, _reason} ->
    # ... (authentication failed) ...
end

The case above can even be simplified if at each authentication you do additional updates like the following:

case ExPassword.verify_and_rehash_if_needed(user, password, algorithm, options, [last_sign_in: DateTime.utc_now()]) do
  {:ok, changes} ->
    user
    |> Ecto.Changeset.change(changes)
    |> MyApp.Repo.update!()
    # ... (successful authentication) ...
  {:error, _reason} ->
    # ... (authentication failed) ...
end