2024-09-10 01:40:05 +05:00
|
|
|
|
defmodule ComfycampWeb.OauthController do
|
|
|
|
|
use ComfycampWeb, :controller
|
|
|
|
|
|
2024-09-17 00:07:00 +05:00
|
|
|
|
alias Comfycamp.Accounts
|
2024-09-10 01:40:05 +05:00
|
|
|
|
alias Comfycamp.SSO
|
|
|
|
|
alias Comfycamp.SSO.OIDCApp
|
2024-09-17 00:07:00 +05:00
|
|
|
|
alias Comfycamp.SSO.OIDCCode
|
|
|
|
|
alias Comfycamp.SSO.IDToken
|
|
|
|
|
alias Comfycamp.Token
|
2024-09-10 01:40:05 +05:00
|
|
|
|
|
2024-09-17 00:07:00 +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
|
|
|
|
|
2024-09-17 00:07:00 +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
|
|
|
|
|
|
2024-09-17 00:07:00 +05:00
|
|
|
|
@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
|
2024-09-17 00:07:00 +05:00
|
|
|
|
app = SSO.get_oidc_app!(client_id)
|
|
|
|
|
current_user = conn.assigns.current_user
|
2024-09-10 01:40:05 +05:00
|
|
|
|
|
2024-09-17 00:07:00 +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
|
|
|
|
|
|
2024-09-21 12:47:07 +05:00
|
|
|
|
def token(conn, %{
|
|
|
|
|
"code" => code_value,
|
|
|
|
|
"redirect_uri" => redirect_uri,
|
|
|
|
|
"client_id" => client_id,
|
|
|
|
|
"client_secret" => client_secret
|
|
|
|
|
}) do
|
2024-09-17 00:07:00 +05:00
|
|
|
|
# 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.
|
2024-09-21 12:47:07 +05:00
|
|
|
|
%OIDCApp{enabled: true, client_id: ^client_id} =
|
|
|
|
|
oidc_app = SSO.get_oidc_app_by_secret!(client_secret)
|
2024-09-17 00:07:00 +05:00
|
|
|
|
|
|
|
|
|
# Check that OIDC app is referenced by provided code.
|
2024-09-21 12:47:07 +05:00
|
|
|
|
^client_id = code.oidc_app.client_id
|
2024-09-17 00:07:00 +05:00
|
|
|
|
|
|
|
|
|
# Delete the code.
|
|
|
|
|
SSO.delete_oidc_code(code)
|
|
|
|
|
|
2024-09-21 12:55:16 +05:00
|
|
|
|
{access_token, refresh_token} = Accounts.generate_oauth_tokens(code.user)
|
2024-09-17 00:07:00 +05:00
|
|
|
|
|
2024-09-21 12:55:16 +05:00
|
|
|
|
id_token = IDToken.build_id_token(code.user, oidc_app.client_id)
|
2024-09-21 13:48:22 +05:00
|
|
|
|
{:ok, signed_id_token} = Token.sign(id_token)
|
2024-09-17 00:07:00 +05:00
|
|
|
|
|
|
|
|
|
render(conn, :token,
|
2024-09-21 15:34:22 +05:00
|
|
|
|
access_token: Base.url_encode64(access_token),
|
|
|
|
|
refresh_token: Base.url_encode64(refresh_token),
|
2024-09-17 00:07:00 +05:00
|
|
|
|
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
|