MagicAuth (magic_auth v0.1.1)
MagicAuth is an authentication library for Phoenix that provides effortless configuration and flexibility for your project.
Key Features:
- Passwordless Authentication: Secure login process through one-time passwords sent via email
- Enhanced Security: Protect your application from brute force attacks with built-in rate limiting and account lockout mechanisms
- Customizable Interface: Fully customizable UI components to match your design
- Effortless Configuration: Quick and simple integration with your Phoenix project
- Schema Agnostic: Implement authentication without requiring a user schema - ideal for everything from MVPs to complex applications
To get started, see the installation documentation in MagicAuth.MixProject
.
Summary
Functions
Returns a list of child processes that should be supervised.
Creates and sends a one-time password for a given email.
Deletes all sessions associated with a given email.
Deletes all sessions associated with a given token.
Authenticates the user session by looking into the session and remember me token.
Gets the session with the given token.
Logs the session in.
Logs the user out.
Mount function for LiveViews that require authentication.
Used for routes that require the user to not be authenticated.
Plug function that verifies if the user is authenticated.
Verifies a one-time password for a given email.
Functions
Returns a list of child processes that should be supervised.
Includes token buckets needed for rate limiting:
- OneTimePasswordRequestTokenBucket: Limits one-time password requests
- LoginAttemptTokenBucket: Limits login attempts
Example
In your application.ex (this configuration is automatically added by the mix magic_auth.install
task):
children = children ++ MagicAuth.children()
Creates and sends a one-time password for a given email.
The one-time password is stored in the database to allow users to log in from a device that doesn't have access to the email where the code was sent. For example, the user can receive the code on their phone and use it to log in on their computer.
When called, this function creates a new one_time_password record and generates
a one-time password that will be used to authenticate it. The password is then passed
to the configured callback module one_time_password_requested/1
which should handle
sending it to the user via email.
One-time password generation is rate limited using a token bucket system that allows a maximum of 1 generation request per minute for each email address. This prevents abuse of the email delivery service.
Parameters
attrs
- A map containing:email
Returns
{:ok, code, one_time_password}
- Returns the created one_time_password on success{:error, changeset}
- Returns the changeset with errors if validation fails{:error, failed_value}
- Returns the failed value if the transaction fails{:error, :rate_limited, countdown}
- Returns the countdown if the rate limit is exceeded
Examples
iex> MagicAuth.create_one_time_password(%{"email" => "user@example.com"})
{:ok, code, %MagicAuth.OneTimePassword{}}
The one time password length can be configured in config/config.exs:
config :magic_auth,
one_time_password_length: 6 # default value
This function:
- Removes any existing one_time_passwords for the provided email
- Creates a new one_time_password
- Generates a new random numeric password
- Encrypts the password using Bcrypt
- Stores the hash in the database
- Calls the configured callback module's
one_time_password_requested/1
function which should handle sending the password to the user via email
Deletes all sessions associated with a given email.
This function should be called when a user is deleted or has their email changed, to ensure that all their active sessions are terminated.
Parameters
email
- The email of the user whose sessions should be deleted
Examples
iex> MagicAuth.delete_all_sessions_by_email("user@example.com")
{0, nil} # where n is the number of deleted sessions
Deletes all sessions associated with a given token.
Authenticates the user session by looking into the session and remember me token.
Gets the session with the given token.
Logs the session in.
It renews the session ID and clears the whole session to avoid fixation attacks.
Login attempts are rate limited using a token bucket that allows a maximum of 10 attempts every 10 minutes per email address.
It also sets a :live_socket_id
key in the session,
so LiveView sessions are identified and automatically
disconnected on log out.
On login success:
- Renews the session to prevent fixation attacks
- Sets the token in session and cookie (if remember me is enabled)
- Redirects to original page requested or to
/
(default route)
On error:
- If too many attempts: Redirects to
/sessions/log_in
with rate limit error message - If invalid code: Redirects to
/sessions/password
- If expired code: Redirects to
/sessions/password
- If access denied: Redirects to
/sessions/log_in
with access denied error message
Denying access
To deny access, implement the log_in_requested/1
callback in your callback module
returning :deny
. For example:
def log_in_requested(email) do
case Accounts.get_user_by_email(email) do
%User{active: false} -> :deny # Denies access for inactive users
_ -> :allow
end
end
For more information on denying access, see the comments for the log_in_requested/1
function
in the generated MagicAuth module in your application's codebase.
Parameters
conn
: The Plug.Conn connectionemail
: String containing the user's email addresscode
: String containing the one-time password code
Logs the user out.
It clears all session data for safety. See renew_session.
Mount function for LiveViews that require authentication.
This function:
- Mounts the user session on the socket
- Continues the mount flow if user is authenticated
- Halts and redirects to login page if user is not authenticated
Examples of usage
In LiveView modules:
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
on_mount {MagicAuth, :require_authenticated}
def mount(_params, _session, socket) do
{:ok, socket}
end
end
In router.ex:
live_session :admin,
on_mount: [{MagicAuth, :require_authenticated}] do
live "/dashboard", DashboardLive
end
Used for routes that require the user to not be authenticated.
Plug function that verifies if the user is authenticated.
If the user is not authenticated:
- Stores the current URL in the session for later redirect
- Redirects to the login page (defaults to: /session/log_in)
- Shows unauthorized error message
- Halts request processing
If the user is authenticated:
- Allows the request to continue normally with the session information
- The current_session is available in conn.assigns[:current_session]
Examples of usage
In router.ex routes:
scope "/", MyAppWeb do
pipe_through [:browser, :require_authenticated]
get "/dashboard", DashboardController, :index
live "/profile", ProfileLive
end
Verifies a one-time password for a given email.
Takes an email and password as input and validates the one-time password.
Returns:
{:ok, one_time_password}
if the password is valid{:error, :invalid_code}
if the password is invalid or no password exists for email{:error, :code_expired}
if the password has expired
The function:
- Looks up the one-time password record for the given email
- Returns error if no password exists (with timing attack protection)
- Checks if password has expired based on configured expiration time
- Verifies the provided password matches the stored hash