This guide assumes you are starting with a relatively bare application. Additional notes are provided when Identity's setup might conflict with existing parts of your application.
Identity is an Elixir package that provides migrations and other helpers to assist with setup.
It is not currently available on Hex.pm.
For now, install it via GitHub by adding the following to the list of dependencies in mix.exs
and running mix deps.get
:
def deps do
[
{:identity, github: "aj-foster/identity", branch: "main"}
]
end
After installation, create a new migration to add identity-related tables:
$ mix ecto.gen.migration add_identity
In the generated migration file, add the following:
defmodule MyApp.Repo.Migrations.AddIdentity do
use Ecto.Migration
def up, do: Identity.Migrations.up()
def down, do: Identity.Migrations.down()
end
Then run the migration:
$ mix ecto.migrate
Identity's migrations make liberal use of create_if_not_exists
, meaning that existing tables (such as users
) won't cause a problem during migration.
However, if there are conflicts, it is possible that Identity will not work as intended:
- As mentioned in
Identity.User
, anyusers
table not created by Identity must have a binary (UUID) primary key calledid
. - If other tables conflict, consider copying and modifying the provided migrations to start from the existing table structure.
In order to interact with your application's database, Identity needs to know which module contains your database repo.
Following is a minimal configuration added to config.exs
:
config :identity, repo: MyApp.Repo
If you have an existing user schema, configure it using the user
key:
config :identity,
repo: MyApp.Repo,
user: MyApp.User
After the user
configuration changes, it is necessary to recompile Identity using mix deps.compile identity --force
in each Mix environment.
For information about additional configuration options, see Identity.Config
.
Identity provides controller actions and templates for all user tasks. It is not necessary to use this provided functionality, but it is likely the fastest way to get up-and-running.
A full router, using all of Identity's features, looks like this:
scope "/" do
pipe_through :browser
# Session
get "/session/new", Identity.Controller, :new_session, as: :identity
post "/session/new", Identity.Controller, :create_session, as: :identity
get "/session/2fa", Identity.Controller, :pending_2fa, as: :identity
post "/session/2fa", Identity.Controller, :validate_2fa, as: :identity
delete "/session", Identity.Controller, :delete_session, as: :identity
# Password Reset
get "/password/new", Identity.Controller, :new_password_token, as: :identity
post "/password/new", Identity.Controller, :create_password_token, as: :identity
get "/password/:token", Identity.Controller, :new_password, as: :identity
put "/password/:token", Identity.Controller, :create_password, as: :identity
# Email Addresses
get "/email/new", Identity.Controller, :new_email, as: :identity
post "/email/new", Identity.Controller, :create_email, as: :identity
get "/email/:token", Identity.Controller, :confirm_email, as: :identity
delete "/user/email", Identity.Controller, :delete_email, as: :identity
# User Registration
get "/user/new", Identity.Controller, :new_user, as: :identity
post "/user/new", Identity.Controller, :create_user, as: :identity
# User Settings
get "/user/password", Identity.Controller, :edit_password, as: :identity
put "/user/password", Identity.Controller, :update_password, as: :identity
get "/user/2fa/new", Identity.Controller, :new_2fa, as: :identity
post "/user/2fa/new", Identity.Controller, :create_2fa, as: :identity
get "/user/2fa", Identity.Controller, :show_2fa, as: :identity
delete "/user/2fa", Identity.Controller, :delete_2fa, as: :identity
put "/user/2fa/backup", Identity.Controller, :regenerate_2fa, as: :identity
# OAuth
get "/auth/:provider", Identity.Controller, :oauth_request, as: :identity
get "/auth/:provider/callback", Identity.Controller, :oauth_callback, as: :identity
end
Note that, as long as the as: :identity
option is included and the action names remain the same, paths and scopes can be customized.
See Progressive Replacement for information about customizing the templates and actions.
For routes not provided by Identity, you can use plugs from Identity.Plug
to enforce authentication rules.
For example:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
# Import Identity plugs
import Identity.Plug
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
# Add current_user assign; should be after :fetch_session
plug :fetch_identity
plug :put_root_layout, {MyAppWeb.LayoutView, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
scope "/", MyAppWeb do
pipe_through [:browser, :redirect_if_authenticated]
get "/public", PageController, :public
end
scope "/", MyAppWeb do
pipe_through [:browser, :redirect_if_unauthenticated]
get "/secret", PageController, :secret
end
# ...
end
Note that Identity-provided controller actions do not require the use of these plugs.
See Identity.Plug
for more information about the available plugs and their options.
With the use of the :fetch_identity
plug, you can use the :current_user
assign to get the currently-authenticated user from a %Plug.Conn{}
struct.
If you intend to use password-based authentication with your app, two-factor auth can be a great addition. This functionality requires two additional dependencies:
def deps do
[
# ...
{:eqrcode, "~> 0.1.10"},
{:nimble_totp, "~> 1.0"}
]
end
Respectively, eqrcode
allows Identity to generate a QR code for easy 2FA enrollment, while nimble_totp
handles generating secrets and checking codes.
After installing these additional dependencies, ensure Identity is recompiled with mix deps.compile identity --force
.
Routes related to two-factor authentication are already included in the Add Routes section above. If you use Identity-provided controller actions for login, they will already check if a user is enrolled in two-factor auth and redirect them appropriately.
Some actions require communication with the user, usually via email. For this, Identity uses a pluggable notification system. Here are the available notifiers provided by Identity:
Module | Description |
---|---|
Identity.Notifier.Log |
Default notifier. Simply logs a message via Logger . Great for development use. |
Identity.Notifier.Bamboo |
Email notifier using the Bamboo library. Requires additional configuration. |
Identity.Notifier.Swoosh |
Email notifier using the Swoosh library. Requires additional configuration. |
Identity.Notifier.Test |
Test notifier that sends a message to the current process when callbacks are called. Useful for testing purposes. |
To choose which notifier is active for your application, use the notifier
key in the configuration:
config :identity,
# ...
notifier: Identity.Notifier.Log # This is the default
Notifiers adhere to the Identity.Notifier
behaviour.
Check out that module's documentation for information about creating a custom implementation.
Identity leverages Ueberauth and its collection of OAuth strategies. To get started, install Ueberauth and the strategies you wish to use:
def deps do
[
# ...
{:ueberauth, "~> 0.10"},
{:ueberauth_github, "~> 0.8"}
]
end
Note that it is common for strategies to require a lower version of Ueberauth than you wish to use.
Adding override: true
to the :ueberauth
dependency may be necessary.
After installing these additional dependencies, ensure Identity is recompiled with mix deps.compile identity --force
.
Routes related to OAuth are already included in the Add Routes section above. Remember to configure each of the Ueberauth strategy libraries according to their documentation.
Identity-provided templates use prefixed CSS classes (.id_
) for easy styling.
If you wish to get off-the-ground quickly with some default styles, you can include Identity-provided CSS in your application:
/* Assuming your CSS is in assets/css/ or similar. */
@import "../../deps/identity/priv/static/vanilla.css";
If you happen to use Tailwind for styling, there is a tailwind variant as well:
@import "../../deps/identity/priv/static/tailwind.css";
These styles are minimal in design, and easily overridden with your own CSS.
By the end of this work, you should have the basis of a feature-rich authentication system for your application. Now you can focus on other — more important — things. When you're ready to customize how auth looks and works, check out Progressive Replacement.