Created
December 6, 2019 11:49
-
-
Save morgz/4976c75ad6cafe62ef8eee0d37bca598 to your computer and use it in GitHub Desktop.
A sample phoenix controller for implementing magic links with POW, Phoenix & JWT (Guardian)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule WildeWeb.MagicLinkController do | |
use WildeWeb, :controller | |
alias Pow.Plug | |
require Logger | |
plug :put_layout, "auth.html" | |
plug :put_layout, "simple.html" when action in [:setup] | |
def send(conn, params) do | |
Wilde.Auth.MagicLinks.deliver_magic_link(params["email"], params["request_path"]) | |
conn | |
|> put_flash(:info, "Check your inbox for your login link") | |
|> redirect(to: Routes.page_path(conn, :landing)) | |
end | |
@doc """ | |
Called from a login link. Attempts to recreate the email address & an optional request_path | |
from the JWT token. The request path to redirect the user to the path they were trying to visit | |
before the auth challenge. | |
""" | |
def callback(conn, %{"magic_token" => magic_token}) do | |
config = Plug.fetch_config(conn) | |
case Wilde.Auth.MagicLinks.email_and_request_path_from_token("#{magic_token}") do | |
{:ok, result} -> | |
conn | |
|> renew_existing_session_or_authenticate_or_create_user(result, config) | |
{:error, reason} -> | |
conn | |
|> put_flash(:error, "Invalid magic link. #{reason}") | |
|> redirect(to: Routes.pow_session_path(conn, :new)) | |
end | |
end | |
# If there's already a Plug user we fetch it. | |
# Otherwise, we get or create a user via the email address from tne token | |
# We then try to auth the user | |
# Finally we maybe redirect to the request_path if it exists. | |
defp renew_existing_session_or_authenticate_or_create_user(conn, %{"email" => email, "request_path" => request_path}, config) do | |
case Pow.Plug.current_user(conn) do | |
# If we don't have a session then either authenticate an existing user or create a new one | |
nil -> get_or_create_user(%{email: email}, config) | |
# if we have a current user session then renew it. Currently won't switch users | |
user -> {:ok, user} | |
end | |
|> maybe_create_auth(conn, config) | |
|> maybe_redirect(request_path) | |
end | |
def maybe_redirect({:ok, _user, conn}, nil) do | |
conn | |
|> put_flash(:info, "Successfully Logged in") | |
|> redirect(to: Routes.page_path(conn, :landing)) | |
end | |
def maybe_redirect({:ok, _user, conn}, request_path) do | |
conn | |
|> put_flash(:info, "Successfully Logged in") | |
|> redirect(to: request_path) | |
end | |
@doc """ | |
If we've got changeset errors, then render a specific form: setup.html | |
""" | |
def maybe_redirect({:error, changeset, conn}, request_path) do | |
conn | |
|> assign(:changeset, changeset) | |
|> assign(:request_path, request_path) | |
|> render("setup.html") | |
end | |
def setup(conn, %{"user" => user_params, "request_path" => ""}), do: setup(conn, %{"user" => user_params, "request_path" => nil}) | |
def setup(conn, %{"user" => user_params, "request_path" => request_path}) do | |
config = Plug.fetch_config(conn) | |
create_user(user_params, config) | |
|> maybe_create_auth(conn, config) | |
|> maybe_redirect(request_path) | |
end | |
defp get_or_create_user(%{email: email} = user_params, config) do | |
case Pow.Ecto.Context.get_by([email: email], config) do | |
nil -> create_user(user_params, config) | |
user -> {:ok, user} | |
end | |
end | |
defp create_user(params, _config) do | |
Wilde.Accounts.create_user_passwordless(params) | |
end | |
defp maybe_create_auth({:ok, user}, conn, config) do | |
{:ok, user, Plug.get_plug(config).do_create(conn, user, config)} | |
end | |
defp maybe_create_auth({:error, changeset}, conn, _config) do | |
{:error, changeset, conn} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment