Authentication with Supabase Auth in Elixir
View SourceThis guide covers how to use Supabase's authentication service (Auth) with your Elixir applications. It provides examples for the most common authentication scenarios and explains how to integrate authentication with both traditional Phoenix applications and LiveView.
Authentication Methods
Supabase Auth supports multiple authentication methods:
Email and Password Authentication
The most common authentication method is email+password sign-in:
# Sign in with email and password
{:ok, session} = Supabase.Auth.sign_in_with_password(client, %{
email: "user@example.com",
password: "securepassword"
})
# Use the session to get the current user
{:ok, user} = Supabase.Auth.get_user(client, session)Phone Authentication
Phone-based authentication using SMS verification:
# Sign in with phone and password
{:ok, session} = Supabase.Auth.sign_in_with_password(client, %{
phone: "+15555551234",
password: "securepassword"
})One-Time Password (OTP)
OTP is a passwordless authentication method that sends a temporary code to the user's email or phone:
# Request an OTP to be sent
:ok = Supabase.Auth.sign_in_with_otp(client, %{
email: "user@example.com"
})
# Later, verify the OTP to get a session
{:ok, session} = Supabase.Auth.verify_otp(client, %{
email: "user@example.com",
token: "123456",
type: "email"
})OAuth (Social Authentication)
Sign in with social providers like Google, GitHub, etc.:
# Get the OAuth URL to redirect the user to
{:ok, provider, redirect_url} = Supabase.Auth.sign_in_with_oauth(client, %{
provider: :github,
redirect_to: "https://myapp.com/auth/callback"
})
# After OAuth callback, exchange the code for a session
{:ok, session} = Supabase.Auth.exchange_code_for_session(client, auth_code, code_verifier)Single Sign-On (SSO)
Enterprise single sign-on for organizations:
# Start SSO authentication
{:ok, redirect_url} = Supabase.Auth.sign_in_with_sso(client, %{
domain: "example.org",
redirect_to: "https://myapp.com/auth/callback"
})Anonymous Authentication
Create a session without user credentials:
{:ok, session} = Supabase.Auth.sign_in_anonymously(client)Session Management
A successful authentication returns a Session struct containing tokens and user information:
%Supabase.Auth.Session{
access_token: "eyJhbGciOiJ...",
refresh_token: "kIvYW5...",
expires_in: 3600,
expires_at: 1650123456, # Unix timestamp when token expires
token_type: "bearer",
user: %Supabase.Auth.User{...}
}Refreshing Sessions
Manually refresh a session:
{:ok, new_session} = Supabase.Auth.refresh_session(client, session.refresh_token)Or use smart helpers that refresh only when needed:
# Refresh if expiring soon (default: 5 minutes)
{:ok, session} = Supabase.Auth.refresh_if_needed(client, session)
# Validate tokens and refresh if needed
case Supabase.Auth.ensure_valid_session(client, session) do
{:ok, valid_session} -> # Ready to use
{:error, :invalid_session} -> # Missing tokens
{:error, :refresh_failed} -> # Refresh failed
endCheck session state:
Session.expired?(session) # Token expired?
Session.expiring_soon?(session) # Expires in < 5 min?
Session.needs_refresh?(session) # Should refresh?
Session.valid?(session) # Has tokens and not expired?
Session.seconds_until_expiry(session) # Time remainingUser Management
User Registration
Create a new user with email and password:
{:ok, user_or_session} = Supabase.Auth.sign_up(client, %{
email: "new_user@example.com",
password: "securepassword"
})Getting User Information
Retrieve current user details:
{:ok, user} = Supabase.Auth.get_user(client, session)Updating User Information
Update user profile details:
{:ok, updated_conn} = Supabase.Auth.update_user(client, conn, %{
data: %{
display_name: "Jane Doe",
avatar_url: "https://example.com/avatar.png"
}
})Password Recovery
Send a password reset email:
:ok = Supabase.Auth.reset_password_for_email(client,
"user@example.com",
redirect_to: "https://myapp.com/reset-password"
)Multi-factor Authentication (MFA)
Auth supports multi-factor authentication for additional security:
Managing MFA Factors
# Get user's MFA factors
{:ok, user} = Supabase.Auth.get_user(client, session)
factors = user.factors
# Check if user has MFA enabled
has_mfa = length(factors) > 0Identity Management
Auth allows users to link multiple authentication methods to a single account:
Linking New Identity Providers
# Get a URL to link a new provider
{:ok, %{provider: :github, url: redirect_url}} = Supabase.Auth.link_identity(
client,
session,
%{provider: :github}
)
# Redirect user to this URL to link their GitHub accountUnlinking Providers
# Remove a linked identity
:ok = Supabase.Auth.unlink_identity(client, session, identity_id)Listing Linked Identities
# Get all linked identities
{:ok, identities} = Supabase.Auth.get_user_identities(client, session)Server Information
Get information about the Auth server:
# Get server settings
{:ok, settings} = Supabase.Auth.get_server_settings(client)
# Check server health
{:ok, health} = Supabase.Auth.get_server_health(client)Integration with Phoenix
Traditional Phoenix Applications
For Phoenix applications with traditional views, use the Supabase.Auth.Plug module:
# lib/my_app_web/auth.ex
defmodule MyAppWeb.Auth do
use Supabase.Auth.Plug,
client: MyApp.Supabase.Client,
endpoint: MyAppWeb.Endpoint,
signed_in_path: "/app",
not_authenticated_path: "/login",
session_cookie: "my_app_session"
end
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
import MyAppWeb.Auth
pipeline :browser do
plug :fetch_current_user
end
# Public routes
scope "/", MyAppWeb do
pipe_through [:browser, :redirect_if_user_is_authenticated]
get "/login", SessionController, :new
post "/login", SessionController, :create
end
# Protected routes
scope "/app", MyAppWeb do
pipe_through [:browser, :require_authenticated_user]
get "/", DashboardController, :index
end
end
# lib/my_app_web/controllers/session_controller.ex
defmodule MyAppWeb.SessionController do
import MyAppWeb.Auth
def create(conn, %{"email" => email, "password" => password}) do
case log_in_with_password(conn, %{email: email, password: password}) do
{:ok, conn} ->
conn
|> put_flash(:info, "Welcome back!")
|> redirect(to: Routes.dashboard_path(conn, :index))
{:error, _reason} ->
conn
|> put_flash(:error, "Invalid email/password")
|> render("new.html")
end
end
endPhoenix LiveView Applications
For LiveView applications, use the Supabase.Auth.LiveView module:
# lib/my_app_web/auth.ex
defmodule MyAppWeb.Auth do
use Supabase.Auth.LiveView,
client: MyApp.Supabase.Client,
endpoint: MyAppWeb.Endpoint,
signed_in_path: "/app",
not_authenticated_path: "/login"
end
# In your LiveView
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
on_mount {MyAppWeb.Auth, :mount_current_user}
on_mount {MyAppWeb.Auth, :ensure_authenticated}
def mount(_params, _session, socket) do
# socket.assigns.current_user is available here
{:ok, assign(socket, page_title: "Dashboard")}
end
end
# In your router
live_session :authenticated,
on_mount: [
{MyAppWeb.Auth, :mount_current_user},
{MyAppWeb.Auth, :ensure_authenticated}
] do
live "/dashboard", DashboardLive
endBest Practices
Secure Token Storage: Store tokens in HTTP-only cookies to prevent XSS attacks.
Proactive Token Refresh: Check and refresh sessions in your Plug pipeline or LiveView mounts:
# Plug: refresh on each request if needed def fetch_current_user(conn, _opts) do with session <- get_session_from_conn(conn), {:ok, valid} <- Supabase.Auth.ensure_valid_session(client(), session) do assign(conn, :session, valid) else _ -> assign(conn, :session, nil) end end# LiveView: periodic check for long-lived sessions def handle_info(:check_session, socket) do case Supabase.Auth.refresh_if_needed(client(), socket.assigns.session) do {:ok, new_session} -> schedule_next_check() {:noreply, assign(socket, :session, new_session)} {:error, _} -> {:noreply, redirect(socket, to: "/login")} end endSession Validation: Use
Session.valid?/1before authenticated operations.HTTPS: Always use HTTPS in production.
Rate Limiting: Implement rate limiting on auth endpoints.
Conclusion
This guide covered the essentials of using Supabase's Auth authentication service with Elixir applications. For more detailed information on specific functions, refer to the module documentation.