fp/services/bright/lib/bright_web/controllers/auth_controller.ex

268 lines
6.9 KiB
Elixir
Raw Normal View History

2025-01-16 20:23:58 +00:00
defmodule BrightWeb.AuthController do
@moduledoc """
Auth controller responsible for handling Ueberauth responses
"""
require Logger
use BrightWeb, :controller
plug Ueberauth
alias Ueberauth.Strategy.Helpers
2025-01-20 10:01:08 +00:00
alias Bright.{Repo, User}
2025-01-16 20:23:58 +00:00
2025-01-20 10:01:08 +00:00
@doc """
Logs the user in.
It renews the session ID and clears the whole session
to avoid fixation attacks. See the renew_session
function to customize this behaviour.
It also sets a `:live_socket_id` key in the session,
so LiveView sessions are identified and automatically
disconnected on log out. The line can be safely removed
if you are not using LiveView.
"""
def log_in_user(conn, user, params \\ %{}) do
token = Accounts.generate_user_session_token(user)
user_return_to = get_session(conn, :user_return_to)
conn
|> renew_session()
|> put_token_in_session(token)
|> maybe_write_remember_me_cookie(token, params)
|> redirect(to: user_return_to || signed_in_path(conn))
end
defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do
put_resp_cookie(conn, @remember_me_cookie, token, @remember_me_options)
end
defp maybe_write_remember_me_cookie(conn, _token, _params) do
conn
end
# This function renews the session ID and erases the whole
# session to avoid fixation attacks. If there is any data
# in the session you may want to preserve after log in/log out,
# you must explicitly fetch the session data before clearing
# and then immediately set it after clearing, for example:
#
# defp renew_session(conn) do
# preferred_locale = get_session(conn, :preferred_locale)
#
# conn
# |> configure_session(renew: true)
# |> clear_session()
# |> put_session(:preferred_locale, preferred_locale)
# end
#
defp renew_session(conn) do
delete_csrf_token()
conn
|> configure_session(renew: true)
|> clear_session()
end
@doc """
Used for routes that require the user to be authenticated.
If you want to enforce the user email is confirmed before
they use the application at all, here would be a good place.
"""
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/github")
|> halt()
end
end
@doc """
Used for routes that require the user to be an administrator.
"""
def require_admin_user(conn, _opts) do
Logger.info("con.assigns[:current_user] as follows. #{inspect(conn.assigns)}")
case conn.assigns[:current_user] do
%User{is_admin: true} -> # Assuming the user struct has an `is_admin` field
conn
%User{} ->
conn
|> put_flash(:error, "You do not have permission to access this page.")
|> redirect(to: ~p"/")
|> halt()
nil ->
conn
|> put_flash(:error, "You must log in to access this page.")
|> maybe_store_return_to()
|> redirect(to: ~p"/auth/github")
|> halt()
end
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 """
Logs the user out.
It clears all session data for safety. See renew_session.
"""
def log_out_user(conn) do
user_token = get_session(conn, :user_token)
user_token && Accounts.delete_user_session_token(user_token)
if live_socket_id = get_session(conn, :live_socket_id) do
BrightWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
end
conn
|> renew_session()
|> delete_resp_cookie(@remember_me_cookie)
|> redirect(to: ~p"/")
end
# def fetch_current_user(conn) do
# conn
# |> get_session(:user_id)
# |> case do
# nil -> nil
# user_id -> User.get(user_id)
# end
# end
defp ensure_user_token(conn) do
if token = get_session(conn, :user_token) do
{token, conn}
else
conn = fetch_cookies(conn, signed: [@remember_me_cookie])
if token = conn.cookies[@remember_me_cookie] do
{token, put_token_in_session(conn, token)}
else
{nil, conn}
end
end
end
defp put_token_in_session(conn, token) do
conn
|> put_session(:user_token, token)
|> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}")
end
def create(conn = %{method: "POST"}, %{"token" => token}) do
user = User.get_by_encoded_auth(token)
if user && Timex.before?(Timex.now(), user.auth_token_expires_at) do
sign_in_and_redirect(conn, user, ~p"/~")
else
conn
|> put_flash(:error, "Whoops!")
|> render("new.html", user: nil)
end
end
2025-01-16 20:23:58 +00:00
def delete(conn, _params) do
conn
|> put_flash(:info, "You have been logged out!")
|> clear_session()
|> redirect(to: "/")
end
def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do
conn
|> put_flash(:error, "Failed to authenticate.")
|> redirect(to: "/")
end
2025-01-20 10:01:08 +00:00
2025-01-16 20:23:58 +00:00
def callback(conn = %{assigns: %{ueberauth_auth: auth}}, _params) do
2025-01-20 10:01:08 +00:00
user_params = %{
github_handle: Map.get(auth, "nickname", nil),
patreon_handle: Map.get(auth, "full_name", nil),
name: "test"
}
changeset = User.insert_changeset(%User{}, user_params)
case Repo.insert(changeset) do
{:ok, user} ->
UserAuth.log_in_user(conn, user)
{:error, changeset} ->
conn
|> put_flash(:error, "Something went wrong. 😭")
|> render(:join, changeset: changeset, user: nil)
2025-01-16 20:23:58 +00:00
end
2025-01-20 10:01:08 +00:00
# case User.get_by_ueberauth(auth) do
# %User{} = user ->
# UserAuth.log_in_user(conn, user, %{})
# nil ->
# case User.create_from_ueberauth(auth) do
# {:ok, %User{} = user} ->
# UserAuth.log_in_user(conn, user, %{})
# {:error, changeset} ->
# Logger.error("failed to create user. auth=#{inspect(auth)}")
# conn
# |> put_flash(:error, "Failed to create user")
# |> redirect(to: ~p"/")
# end
# end
end
2025-01-16 20:23:58 +00:00
defp sign_in_and_redirect(conn, user, route) do
2025-01-20 10:01:08 +00:00
Logger.info("sign_in_and_redirect with user=#{inspect(user)}")
2025-01-16 20:23:58 +00:00
user
|> User.sign_in_changes()
|> Repo.update()
conn
|> assign(:current_user, user)
2025-01-20 10:01:08 +00:00
|> put_flash(:success, "Welcome to Futureporn!")
2025-01-16 20:23:58 +00:00
|> put_session("id", user.id)
|> configure_session(renew: true)
|> redirect(to: route)
end
defp params_from_ueberauth(%{provider: :github, info: info}) do
2025-01-20 10:01:08 +00:00
%{name: info.name, handle: info.nickname, github_handle: info.nickname, github_id: info.uid}
2025-01-16 20:23:58 +00:00
end
defp params_from_ueberauth(%{provider: :patreon, info: info}) do
2025-01-20 10:01:08 +00:00
%{name: info.name, handle: info.nickname, patreon_handle: info.full_name, patreon_id: info.id}
2025-01-16 20:23:58 +00:00
end
2025-01-20 10:01:08 +00:00
defp signed_in_path(_conn), do: ~p"/"
2025-01-16 20:23:58 +00:00
end