comfycamp/lib/comfycamp_web/controllers/oauth_controller.ex

132 lines
4.4 KiB
Elixir
Raw Normal View History

2024-09-10 01:40:05 +05:00
defmodule ComfycampWeb.OauthController do
use ComfycampWeb, :controller
alias Comfycamp.Accounts
2024-09-10 01:40:05 +05:00
alias Comfycamp.SSO
alias Comfycamp.SSO.OIDCApp
alias Comfycamp.SSO.OIDCCode
alias Comfycamp.SSO.IDToken
alias Comfycamp.Token
2024-09-10 01:40:05 +05:00
@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
2024-09-10 01:40:05 +05:00
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
2024-09-10 01:40:05 +05:00
end
@doc """
Generate an Authorization Code and redirect the user back to Relying Party.
"""
2024-09-10 01:40:05 +05:00
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
2024-09-10 01:40:05 +05:00
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(code.user)
id_token = IDToken.build_id_token(code.user, oidc_app.client_id)
{:ok, signed_id_token} = Token.sign(id_token)
render(conn, :token,
access_token: access_token,
refresh_token: refresh_token,
id_token: signed_id_token
)
2024-09-10 01:40:05 +05:00
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