frenzy/lib/frenzy_web/controllers/fervor/oauth_controller.ex

192 lines
5.3 KiB
Elixir

defmodule FrenzyWeb.Fervor.OauthController do
use FrenzyWeb, :controller
alias Frenzy.{Repo, FervorClient, User, ApprovedClient}
alias FrenzyWeb.Endpoint
def authorize_get(conn, params) do
user_token = get_session(conn, :user_token)
case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 24 * 60 * 60) do
{:error, _reason} ->
continue = "#{conn.request_path}?#{conn.query_string}"
redirect(conn, to: Routes.login_path(Endpoint, :login, continue: continue))
{:ok, user_id} ->
case Repo.get(User, user_id) do
nil ->
continue = "#{conn.request_path}?#{conn.query_string}"
redirect(conn, to: Routes.login_path(Endpoint, :login, continue: continue))
user ->
conn
|> assign(:user, user)
|> try_render_authorize(params)
end
end
end
def try_render_authorize(
conn,
%{
"response_type" => "code",
"client_id" => client_id,
"redirect_uri" => redirect_uri
} = params
) do
case Repo.get_by(FervorClient, client_id: client_id) do
nil ->
conn
client ->
if redirect_uri == client.redirect_uri do
render(conn, "authorize.html", %{
client: client,
state: Map.get(params, "state")
})
else
conn
|> put_status(400)
|> json(%{error: "mismatched redirect uri"})
end
end
end
def try_render_authorize(conn, _params) do
conn
|> put_status(400)
|> json(%{error: "invalid parameters"})
end
def authorize_post(conn, params) do
user_token = get_session(conn, :user_token)
case Phoenix.Token.verify(Endpoint, "user token", user_token, max_age: 24 * 60 * 60) do
{:error, _reason} ->
continue = "#{conn.request_path}?#{conn.query_string}"
redirect(conn, to: Routes.login_path(Endpoint, :login, continue: continue))
{:ok, user_id} ->
case Repo.get(User, user_id) do
nil ->
continue = "#{conn.request_path}?#{conn.query_string}"
redirect(conn, to: Routes.login_path(Endpoint, :login, continue: continue))
user ->
conn
|> assign(:user, user)
|> try_authorize(params)
end
end
end
def try_authorize(conn, %{"client_id" => client_id} = params) do
user = conn.assigns[:user]
client = Repo.get_by(FervorClient, client_id: client_id)
state = Map.get(params, "state")
auth_code = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
changeset =
Ecto.build_assoc(user, :approved_clients, %{
auth_code: auth_code,
client_id: client_id
})
{:ok, _approved_client} = Repo.insert(changeset)
case client.redirect_uri do
"urn:ietf:wg:oauth:2.0:oob" ->
render(conn, "successfully_authorized.html", %{
auth_code: auth_code
})
redirect_uri ->
parsed = URI.parse(redirect_uri)
query =
URI.encode_query(
if state, do: %{code: auth_code, state: state}, else: %{code: auth_code}
)
uri = %URI{parsed | query: query}
redirect(conn, external: URI.to_string(uri))
end
end
def try_authorize(conn, _params) do
conn
|> put_status(400)
|> json(%{error: "invalid parameters"})
end
def token(
conn,
%{
"redirect_uri" => redirect_uri,
"client_id" => client_id,
"client_secret" => client_secret
} = params
) do
case Repo.get_by(FervorClient, client_id: client_id) do
nil ->
conn
|> put_status(401)
|> json(%{error: "invalid_client", error_description: "incorrect client information"})
client ->
if client_secret == client.client_secret and redirect_uri == client.redirect_uri do
conn
|> assign(:client, client)
|> try_generate_token(params)
else
conn
|> put_status(400)
|> json(%{error: "invalid_grant", error_description: "incorrect client information"})
end
end
end
def token(conn, _params) do
json(conn, %{error: "invalid_request", error_description: "missing parameters"})
end
def try_generate_token(conn, %{
"grant_type" => "authorization_code",
"authorization_code" => auth_code
}) do
case Repo.get_by(ApprovedClient, auth_code: auth_code) do
nil ->
conn
|> put_status(400)
|> json(%{error: "invalid_grant", error_description: "invalid authorization code"})
approved_client ->
access_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
changeset =
ApprovedClient.changeset(approved_client, %{
auth_code: nil,
access_token: access_token
})
{:ok, _approved_client} = Repo.update(changeset)
json(conn, %{
access_token: access_token,
token_type: "bearer",
owner: to_string(approved_client.user_id)
})
end
end
def try_generate_token(conn, _params) do
conn
|> put_status(400)
|> json(%{
error: "unsupported_grant_type",
error_description: "only grant_type=authorization_code is supported"
})
end
end