comfycamp/lib/comfycamp_web/controllers/oauth_controller.ex

131 lines
4.5 KiB
Elixir
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defmodule ComfycampWeb.OauthController do
use ComfycampWeb, :controller
alias Comfycamp.Accounts
alias Comfycamp.SSO
alias Comfycamp.SSO.OIDCApp
alias Comfycamp.SSO.OIDCCode
alias Comfycamp.SSO.IDToken
alias Comfycamp.Token
@doc """
Check the request parameters and current user status,
then ask the user to confirm that he wants to share his info with Relying Party.
"""
def authorize(conn, %{"client_id" => client_id, "redirect_uri" => redirect_uri} = params) do
app = SSO.get_oidc_app!(client_id)
current_user = conn.assigns.current_user
with {:is_approved, true} <- {:is_approved, current_user.is_approved},
{:response_type, "code"} <- {:response_type, params["response_type"]},
{:is_enabled, true} <- {:is_enabled, app.enabled},
{:has_uri, true} <- {:has_uri, SSO.has_redirect_uri?(client_id, redirect_uri)} do
render(conn, :authorize,
page_title: "Подтвердите вход",
app_name: app.name,
params: URI.encode_query(params)
)
else
{:is_approved, false} ->
render(conn, :error,
description:
"Ваш аккаунт ещё не был одобрен, подождите немного или свяжитесь с администратором"
)
{:response_type, response_type} ->
render(conn, :error, description: "Неподдерживаемый response type: #{response_type}")
{:is_enabled, false} ->
render(conn, :error, description: "Приложение отключено")
{:has_uri, false} ->
render(conn, :error, description: "Redirect URI не зарегистрирован или отсутствует")
end
end
@doc """
Generate an Authorization Code and redirect the user back to Relying Party.
"""
def generate_code(conn, %{"client_id" => client_id, "redirect_uri" => redirect_uri} = params) do
app = SSO.get_oidc_app!(client_id)
current_user = conn.assigns.current_user
with {:is_approved, true} <- {:is_approved, current_user.is_approved},
{:is_enabled, true} <- {:is_enabled, app.enabled},
{:has_uri, true} <- {:has_uri, SSO.has_redirect_uri?(client_id, redirect_uri)} do
{:ok, code} =
SSO.create_oidc_code(%{
oidc_app_id: client_id,
user_id: conn.assigns.current_user.id,
redirect_uri: redirect_uri
})
uri = build_redirect_uri(redirect_uri, code.value, params["state"])
redirect(conn, external: uri)
else
{:is_approved, false} ->
render(conn, :error,
description:
"Ваш аккаунт ещё не был одобрен, подождите немного или свяжитесь с администратором"
)
{:is_enabled, false} ->
render(conn, :error, description: "Приложение отключено")
{:has_uri, false} ->
render(conn, :error, description: "Redirect URI не зарегистрирован или отсутствует")
end
end
def token(conn, %{
"code" => code_value,
"redirect_uri" => redirect_uri,
"client_id" => client_id,
"client_secret" => client_secret
}) do
# Check that code is still valid and redirect uri has not been altered.
%OIDCCode{redirect_uri: ^redirect_uri} = code = SSO.get_oidc_code!(code_value)
# Check that client provided a valid secret for an active OIDC app.
%OIDCApp{enabled: true, client_id: ^client_id} =
oidc_app = SSO.get_oidc_app_by_secret!(client_secret)
# Check that OIDC app is referenced by provided code.
^client_id = code.oidc_app.client_id
# Delete the code.
SSO.delete_oidc_code(code)
{access_token, refresh_token} = Accounts.generate_oauth_tokens(conn.assigns.current_user)
id_token = IDToken.build_id_token(conn.assigns.current_user, oidc_app.client_id)
{:ok, signed_id_token, _claims} = Token.generate_and_sign(id_token)
render(conn, :token,
access_token: access_token,
refresh_token: refresh_token,
id_token: signed_id_token
)
end
defp build_redirect_uri(redirect_uri, code, state) do
parsed_uri = URI.parse(redirect_uri)
query =
build_query_params(code, state)
|> URI.encode_query()
%{parsed_uri | query: query}
|> URI.to_string()
end
defp build_query_params(code, state) do
params = %{"code" => code}
if state do
Map.put(params, "state", state)
else
params
end
end
end