View Source
Installation
The package can be installed by adding haytni
to your list of dependencies in mix.exs:
def deps do
[
# ...
{:haytni, "~> 0.7.0"},
# with bcrypt support (for past and/or present passwords)
{:expassword_bcrypt, "~> 0.2"},
# with argon2 support (for past and/or present passwords)
#{:expassword_argon2, "~> 0.2"},
{:ecto_network, "~> 1.3.0"}, # for TrackablePlugin only with PostgreSQL
]
end
Then run mix deps.get
.
Configure Haytni config/config.exs
config :haytni, YourApp.Haytni,
#layout: {YourAppWeb.LayoutView, :app},
#mailer: YourApp.Mailer, # see below
otp_app: :your_app,
repo: YourApp.Repo,
schema: YourApp.User
For testing, you may also want to add the following settings to config/test.exs :
config :your_app, YourApp.Mailer,
adapter: Bamboo.TestAdapter
These are the mandatory options. See options of each plugin for full customizations.
Run mix haytni.install
which has the following options (command arguments):
--table <table>
(default:"users"
): the name of your table (used to generate migrations)--plugin Module1 --plugin Module2 ... --plugin ModuleN
: the names of the (Elixir) modules/plugins to enable
Create lib/your_app_web/haytni.ex :
defmodule YourApp.Haytni do
use Haytni, otp_app: :your_app
# with bcrypt to hash current passwords
stack Haytni.AuthenticablePlugin, hashing_method: ExPassword.Bcrypt, hashing_options: %{cost: (if Mix.env() == :test, do: 4, else: 10)}
# with argon2 to hash current passwords
#stack Haytni.AuthenticablePlugin, hashing_method: ExPassword.Argon2, hashing_options: (if Mix.env() == :test, do: %{memory_cost: 256, threads: 1, time_cost: 2, type: :argon2id}, else: %{memory_cost: 131072, threads: 2, time_cost: 4, type: :argon2id})
stack Haytni.RegisterablePlugin
stack Haytni.RememberablePlugin
stack Haytni.ConfirmablePlugin
stack Haytni.LockablePlugin
stack Haytni.RecoverablePlugin
#stack Haytni.TrackablePlugin
stack Haytni.ClearSiteDataPlugin
# add or remove/comment any plugin
end
Change lib/your_app_web/router.ex
defmodule YourAppWeb.Router do
use YourAppWeb, :router
require YourApp.Haytni # <= add this line
# ...
pipeline :browser do
# ...
plug YourApp.Haytni # <= add this line
end
scope "/" do
# ...
YourApp.Haytni.routes() # <= add this line
end
# ...
end
If you use Phoenix LiveView, you can include your Haytni stack as an on_mount
callback to also handle the current user:
defmodule YourAppWeb.Router do
use YourAppWeb, :router
require YourApp.Haytni # <= add this line
pipeline :browser do
# ...
plug YourApp.Haytni # <= add this line
end
live_session(
...,
on_mount: [
YourApp.Haytni, # <= add this line
# your other on_mount callbacks
]
) do
# your dead and live routes
YourApp.Haytni.routes() # <= add this line
end
# ...
end
Note: YourApp.Haytni.on_mount/4
, like YourApp.Haytni.call/2
(acting as a Plug), just set the current user (if he is valid according to Haytni.Plugin.invalid?/3
), if you need to restrict access, you need to do it after (with an other Plug or directly in the controller for a dead view vs a following on_mount/4
callback or by the live view itself)
Change lib/your_app/user.ex
defmodule YourApp.User do
require YourApp.Haytni # <= add this line
# ...
schema "..." do
# ...
YourApp.Haytni.fields() # <= add this line
end
def create_registration_changeset(struct = %__MODULE__{}, params = %{}) do
struct
# add any needed field by registration from your own fields in the list below
|> Ecto.Changeset.cast(params, [:email, :password])
|> YourApp.Haytni.validate_password()
# ... (your custom validations) ...
|> YourApp.Haytni.validate_create_registration()
end
def update_registration_changeset(struct = %__MODULE__{}, params = %{}) do
struct
# put the names of the fields the user is allowed to change himself in the following empty list
# but don't mention :email nor :password here, they are specifically handled by Haytni
|> Ecto.Changeset.cast(params, [])
# ... (your custom validations) ...
|> YourApp.Haytni.validate_update_registration()
end
# ...
end
Emails
For plugins which send emails (Confirmable, Lockable and Recoverable):
Create lib/mailer.ex as follows:
defmodule YourApp.Mailer do
use Bamboo.Mailer, otp_app: :your_app
def from, do: {"mydomain.com", "noreply.mydomain.com"}
end
Add to lib/your_app_web/router.ex
if Mix.env() == :dev do
Application.ensure_started(:bamboo)
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
Configure email sending in config/dev.exs:
config :your_app, YourApp.Mailer,
adapter: Bamboo.LocalAdapter
config :haytni, YourApp.Haytni,
mailer: YourApp.Mailer # <= add/change this line
For production (config/prod.exs), if you pass by your own SMTP server:
config :your_app, YourApp.Mailer,
adapter: Bamboo.SMTPAdapter,
server: "localhost", # the SMTP server is on the same host
hostname: "www.domain.com",
port: 25,
tls: :never,
no_mx_lookups: false,
auth: :never
And add {:bamboo_smtp, "~> 4.1", only: :prod}
to deps
in your mix.exs file. See Bamboo's documentation for details and other methods to send emails
General configuration:
layout
(default:false
for none): the layout to apply to Haytni's templates
Quick recap
Functions you have to implement:
- for Registerable: YourApp.User.create_registration_changeset/2 and YourApp.User.update_registration_changeset/2
- for sending emails (plugins Confirmable, Lockable and Recoverable): YourApp.Mailer.from/0