defmodule BrightWeb.AuthController do use BrightWeb, :controller use BrightWeb, :verified_routes alias Bright.Users alias Bright.Users.User alias Bright.Repo require Logger plug(Ueberauth) import Plug.Conn import Phoenix.Controller alias Phoenix.LiveView def delete(conn, _params) do conn |> clear_session() |> redirect(to: ~p"/") end def callback(conn = %{assigns: %{ueberauth_auth: auth}}, _params) do Logger.debug("callback() with ueberauth_auth defined. Let's get a User!") Logger.debug(inspect(auth)) if user = Users.get_by_ueberauth(auth) do Logger.debug(">>>>>> user has been here before") Logger.debug("user=#{inspect(user)}") Logger.debug("auth=#{inspect(auth)}") ## @todo here we need to update the user's patron_tier attrs = params_from_ueberauth(auth) case Users.update_user(user, attrs) do {:ok, user} -> conn |> sign_in_and_redirect(user, ~p"/profile") {:error, reason} -> Logger.error("error while updating the user's patron_tier. #{inspect(reason)}") conn |> put_flash(:error, "Patreon tier synchronization failed. Please try again.") |> redirect(to: ~p"/") end else Logger.debug(">>>>>> it's user's first time here.") attrs = params_from_ueberauth(auth) case Users.register_user(attrs) do {:ok, user} -> Logger.debug("user=#{inspect(user)}") conn |> put_flash(:success, "Welcome to Futureporn!") |> sign_in_and_redirect(user, ~p"/profile") {:error, reason} -> Logger.error("failed Users.register_user for a first-time user. reason=#{reason}") conn |> put_flash(:error, "Something went wrong. Please try again.") |> redirect(to: ~p"/") end end end def callback(conn = %{assigns: %{ueberauth_failure: fails}}, _params) do Logger.error(inspect(fails)) conn |> put_flash(:error, "Something went wrong; please try again.") |> render("new.html", user: nil) end defp params_from_ueberauth(%{provider: :github, info: info, uid: uid}) do %{name: info.name, handle: info.nickname, github_handle: info.nickname, github_id: uid} end defp params_from_ueberauth(%{ provider: :patreon, info: info, uid: uid, extra: %{raw_info: %{user: user}} }) do Logger.debug( "getting params_from_ueberauth provider: :patreon. info=#{inspect(info)}, uid=#{inspect(uid)}, user=#{inspect(user)}" ) currently_entitled_tiers = Bright.PatreonEntitlements.extract_tiers(user) Logger.debug("currently_entitled_tiers=#{currently_entitled_tiers}") patron_tier = Bright.TierMapper.largest_tier_for_platform("patreon", currently_entitled_tiers) %{ name: info.name, handle: info.nickname, patreon_handle: info.nickname, patreon_id: uid, patron_tier: patron_tier } end defp sign_in_and_redirect(conn, user, route) do Logger.debug("sign_in_and_redirect with user=#{inspect(user)} route=#{route}") user |> User.sign_in_changes() |> Repo.update() conn # |> tap(fn c -> Logger.debug("Before: #{inspect(c.assigns)}") end) |> assign(:current_user, user) # |> tap(fn c -> Logger.debug("After: #{inspect(c.assigns)}") end) |> put_flash(:success, "Authenticated! teehee.") |> put_session("id", user.id) |> configure_session(renew: true) |> redirect(to: route) end defp maybe_store_return_to(%{method: "GET"} = conn) do put_session(conn, :user_return_to, current_path(conn)) end defp maybe_store_return_to(conn), do: conn @doc """ Authenticates the user by looking into the session. """ def fetch_current_user(conn, _opts) do user_id = get_session(conn, :id) user = user_id && Users.get_user!(user_id) Logger.debug( "fetch_current_user attempting to get user. user_id=#{user_id} user=#{inspect(user)}" ) assign(conn, :current_user, user) end def on_mount(:current_user, _params, session, socket) do Logger.debug("~~~~ on_mount with :current_user=#{inspect(:current_user)}") case session do %{"id" => id} -> {:cont, Phoenix.Component.assign_new(socket, :current_user, fn -> Users.get_user!(id) end)} {:cont, assign(socket, :page_title, "DemoWeb")} %{} -> {:cont, Phoenix.Component.assign(socket, :current_user, nil)} end end def on_mount(:ensure_authenticated, _params, session, socket) do Logger.debug("on_mount with session=#{inspect(session)}") case session do %{"id" => id} -> Logger.debug( "~~~~ on_mount with :ensure_authenticated=#{inspect(:ensure_authenticated)}. user id=#{id}" ) new_socket = Phoenix.Component.assign_new(socket, :current_user, fn -> Users.get_user!(id) end) Logger.debug("new_socket=#{inspect(new_socket)}") %Users.User{} = new_socket.assigns.current_user {:cont, new_socket} %{} -> Logger.debug("~~~~ on_mount with ensure_authenticated... user id was FALSY! (bad)") {:halt, redirect_require_login(socket)} end rescue Ecto.NoResultsError -> {:halt, redirect_require_login(socket)} end def on_mount(:ensure_admin, _params, session, socket) do Logger.debug("~~~~ on_mount with :ensure_admin=#{inspect(:ensure_admin)}") case session do %{"id" => id} -> user = Users.get_user!(id) if Users.admin?(user) do {:cont, socket} else {:halt, LiveView.redirect(socket, to: ~p"/")} end %{} -> {:halt, redirect_require_login(socket)} end rescue Ecto.NoResultsError -> {:halt, redirect_require_login(socket)} end @doc """ Used for routes that require the user to be authenticated. """ def require_authenticated_user(conn, _opts) do if conn.assigns[:current_user] do conn else conn |> put_flash(:error, "You must log in to access this page.") |> maybe_store_return_to() |> redirect(to: ~p"/auth/patreon") |> halt() end end @doc """ Used for routes that require the user to have admin privs. """ def require_admin_user(conn, _opts) do case conn.assigns[:current_user] do %{role: "admin"} -> conn _ -> conn |> put_flash(:error, "Only admins can do that.") |> maybe_store_return_to() |> redirect(to: ~p"/") |> halt() end end defp redirect_require_login(socket) do socket |> LiveView.put_flash(:error, "Please sign in") |> LiveView.redirect(to: ~p"/auth/patreon") end end