Current section
Files
Jump to
Current section
Files
lib/authx_ex/credentials/schemas/password_schema.ex
defmodule AuthShield.Credentials.Schemas.Password do
@moduledoc """
Password schema model.
We do not save users password, only the encripted hash that will
be used to authenticate.
To see more about how we hash the password check `Argon2`.
"""
use Ecto.Schema
import Ecto.Changeset
alias AuthShield.Resources.Schemas.User
@typedoc """
Abstract password module type.
"""
@type t :: %__MODULE__{
id: binary(),
user: User.t(),
password_hash: String.t(),
inserted_at: NaiveDateTime.t(),
updated_at: NaiveDateTime.t()
}
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@required_fields [:password, :user_id]
schema "password_credentials" do
field(:password, :string, virtual: true)
field(:password_hash, :string)
belongs_to(:user, User)
timestamps()
end
@doc """
Generates an `Ecto.Changeset` struct with the changes.
It defines validations and also generates the password hash if
necessary.
"""
@spec changeset(model :: t(), params :: map()) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = model, params) when is_map(params) do
model
|> cast(params, @required_fields)
|> validate_required(@required_fields)
|> validate_length(:password, min: 6, max: 150)
|> unique_constraint(:user_id)
|> put_pass_hash()
end
@doc """
Generates an `Ecto.Changeset` to be used on assoc with the user.
It defines validations and also generates the password hash if
necessary.
"""
@spec changeset_assoc(model :: t(), params :: map()) :: Ecto.Changeset.t()
def changeset_assoc(%__MODULE__{} = model, params) when is_map(params) do
model
|> cast(params, [:password])
|> validate_required([:password])
|> validate_length(:password, min: 6, max: 150)
|> unique_constraint(:user_id)
|> put_pass_hash()
end
defp put_pass_hash(%{valid?: true, changes: %{password: pwd}} = changeset) do
# Append the password hash to the changeset
# We use `Argon2` to hash and verify the password
#
# See more in https://hexdocs.pm/argon2_elixir/Argon2.html
change(changeset, %{password_hash: Argon2.hash_pwd_salt(pwd), password: nil})
end
defp put_pass_hash(changeset), do: changeset
end