192 lines
5.2 KiB
Elixir
192 lines
5.2 KiB
Elixir
defmodule FrenzyWeb.Fervor.OauthController do
|
|
use FrenzyWeb, :controller
|
|
alias Frenzy.{Repo, FervorClient, User, ApprovedClient}
|
|
alias FrenzyWeb.Router.Helpers, as: Routes
|
|
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, to: 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"
|
|
})
|
|
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
|