Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
82 lines
2.7 KiB
Elixir
82 lines
2.7 KiB
Elixir
defmodule TarakanWeb.GitHubAuthController do
|
|
use TarakanWeb, :controller
|
|
|
|
alias Tarakan.Accounts
|
|
alias Tarakan.GitHub.OAuth
|
|
alias TarakanWeb.AccountAuth
|
|
alias TarakanWeb.SafeRedirect
|
|
|
|
def request(conn, params) do
|
|
return_to = safe_return_to(params["return_to"])
|
|
|
|
if OAuth.configured?() do
|
|
state = OAuth.generate_state()
|
|
{verifier, challenge} = OAuth.generate_pkce()
|
|
redirect_uri = url(~p"/auth/github/callback")
|
|
|
|
conn
|
|
|> put_session(:github_oauth_state, state)
|
|
|> put_session(:github_oauth_verifier, verifier)
|
|
|> put_session(:github_oauth_return_to, return_to)
|
|
|> redirect(external: OAuth.authorize_url(state, challenge, redirect_uri))
|
|
else
|
|
conn
|
|
|> put_flash(:error, "GitHub login has not been configured yet.")
|
|
|> redirect(to: return_to)
|
|
end
|
|
end
|
|
|
|
def callback(conn, %{"code" => code, "state" => state}) do
|
|
expected_state = get_session(conn, :github_oauth_state)
|
|
verifier = get_session(conn, :github_oauth_verifier)
|
|
return_to = safe_return_to(get_session(conn, :github_oauth_return_to))
|
|
redirect_uri = url(~p"/auth/github/callback")
|
|
current_account = conn.assigns.current_scope && conn.assigns.current_scope.account
|
|
|
|
with true <- OAuth.valid_state?(expected_state, state),
|
|
true <- is_binary(verifier),
|
|
:ok <- authorize_identity_link(current_account),
|
|
{:ok, token} <- OAuth.exchange_code(code, verifier, redirect_uri),
|
|
{:ok, profile} <- OAuth.fetch_user(token),
|
|
{:ok, account} <- Accounts.upsert_external_identity(:github, profile, current_account),
|
|
true <- Accounts.access_allowed?(account) do
|
|
conn
|
|
|> clear_oauth_session()
|
|
|> put_session(:account_return_to, return_to)
|
|
|> put_flash(:info, "Signed in as @#{account.handle}.")
|
|
|> AccountAuth.log_in_account(account)
|
|
else
|
|
_error -> authorization_failed(conn, return_to)
|
|
end
|
|
end
|
|
|
|
def callback(conn, _params) do
|
|
authorization_failed(conn, safe_return_to(get_session(conn, :github_oauth_return_to)))
|
|
end
|
|
|
|
defp authorization_failed(conn, return_to) do
|
|
conn
|
|
|> clear_oauth_session()
|
|
|> put_flash(:error, "GitHub authentication failed. Please try again.")
|
|
|> redirect(to: return_to)
|
|
end
|
|
|
|
defp clear_oauth_session(conn) do
|
|
conn
|
|
|> delete_session(:github_oauth_state)
|
|
|> delete_session(:github_oauth_verifier)
|
|
|> delete_session(:github_oauth_return_to)
|
|
end
|
|
|
|
defp safe_return_to(path) when is_binary(path) do
|
|
SafeRedirect.local_path(path)
|
|
end
|
|
|
|
defp safe_return_to(_path), do: "/"
|
|
|
|
defp authorize_identity_link(nil), do: :ok
|
|
|
|
defp authorize_identity_link(account) do
|
|
if Accounts.sudo_mode?(account), do: :ok, else: {:error, :recent_authentication_required}
|
|
end
|
|
end
|