268 lines
6.9 KiB
Elixir
268 lines
6.9 KiB
Elixir
|
|
|
|
defmodule BrightWeb.AuthController do
|
|
@moduledoc """
|
|
Auth controller responsible for handling Ueberauth responses
|
|
"""
|
|
|
|
require Logger
|
|
use BrightWeb, :controller
|
|
|
|
plug Ueberauth
|
|
|
|
alias Ueberauth.Strategy.Helpers
|
|
alias Bright.{Repo, User}
|
|
|
|
|
|
@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
|
|
|
|
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
|
|
|
|
|
|
|
|
def callback(conn = %{assigns: %{ueberauth_auth: auth}}, _params) do
|
|
|
|
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)
|
|
end
|
|
|
|
# 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
|
|
|
|
|
|
|
|
defp sign_in_and_redirect(conn, user, route) do
|
|
Logger.info("sign_in_and_redirect with user=#{inspect(user)}")
|
|
|
|
user
|
|
|> User.sign_in_changes()
|
|
|> Repo.update()
|
|
|
|
conn
|
|
|> assign(:current_user, user)
|
|
|> put_flash(:success, "Welcome to Futureporn!")
|
|
|> put_session("id", user.id)
|
|
|> configure_session(renew: true)
|
|
|> redirect(to: route)
|
|
end
|
|
|
|
|
|
defp params_from_ueberauth(%{provider: :github, info: info}) do
|
|
%{name: info.name, handle: info.nickname, github_handle: info.nickname, github_id: info.uid}
|
|
end
|
|
|
|
defp params_from_ueberauth(%{provider: :patreon, info: info}) do
|
|
%{name: info.name, handle: info.nickname, patreon_handle: info.full_name, patreon_id: info.id}
|
|
end
|
|
|
|
|
|
defp signed_in_path(_conn), do: ~p"/"
|
|
|
|
end
|