Skip to content

Commit

Permalink
Extract User API to handler structure (#37)
Browse files Browse the repository at this point in the history
* refactor: extract user API to handler

* remove endpoints unused file
  • Loading branch information
zoedsoupe authored Nov 22, 2023
1 parent d9237a4 commit 1f54ec8
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 160 deletions.
76 changes: 5 additions & 71 deletions apps/supabase_auth/lib/supabase/go_true.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ defmodule Supabase.GoTrue do
import Supabase.Client, only: [is_client: 1]

alias Supabase.Client
alias Supabase.Fetcher
alias Supabase.GoTrue.Endpoints
alias Supabase.GoTrue.PKCE
alias Supabase.GoTrue.Schemas.SignInRequest
alias Supabase.GoTrue.Schemas.SignInWithPassword
alias Supabase.GoTrue.Schemas.SignUpRequest
alias Supabase.GoTrue.Schemas.SignUpWithPassword
alias Supabase.GoTrue.Session
alias Supabase.GoTrue.User
alias Supabase.GoTrue.UserHandler

@opaque client :: pid | module

Expand All @@ -21,9 +17,7 @@ defmodule Supabase.GoTrue do
@impl true
def get_user(client, %Session{} = session) do
with {:ok, client} <- Client.retrieve_client(client),
uri = Endpoints.user(client),
headers = Fetcher.apply_client_headers(client, session.access_token),
{:ok, response} <- Fetcher.get(uri, headers) do
{:ok, response} <- UserHandler.get_user(client, session.access_token) do
User.parse(response)
end
end
Expand All @@ -32,76 +26,16 @@ defmodule Supabase.GoTrue do
def sign_in_with_password(client, credentials) when is_client(client) do
with {:ok, client} <- Client.retrieve_client(client),
{:ok, credentials} <- SignInWithPassword.parse(credentials),
attrs = %{
email: credentials.email,
phone: credentials.phone,
password: credentials.password
},
{:ok, params} <- SignInRequest.create(attrs, credentials.options) do
sign_in_request(params, client)
end
end

defp sign_in_request(%SignInRequest{} = request, %Client{} = client) do
headers = api_headers(client)
uri = Endpoints.sign_in(client, "password")

with {:ok, response} <- Fetcher.post(uri, request, headers) do
Session.parse(response, response["user"])
{:ok, response} <- UserHandler.sign_in_with_password(client, credentials) do
Session.parse(response)
end
end

@impl true
def sign_up(client, credentials) when is_client(client) do
with {:ok, client} <- Client.retrieve_client(client),
{:ok, credentials} <- SignUpWithPassword.parse(credentials) do
if client.auth.flow_type == :pkce do
sign_up_with_pkce(credentials, client)
else
sign_up_without_pkce(credentials, client)
end
end
end

defp sign_up_with_pkce(%SignUpWithPassword{} = credentials, %Client{} = client) do
code_verifier = PKCE.generate_verifier()
code_challenge = PKCE.generate_challenge(code_verifier)
code_challenge_method = "sha256"

attrs = %{
email: credentials.email,
phone: credentials.phone,
password: credentials.password,
code_challenge: code_challenge,
code_challenge_method: code_challenge_method
}

with {:ok, params} <- SignUpRequest.create(attrs, credentials.options),
{:ok, response} <- sign_up_request(params, client) do
{:ok, response, code_challenge}
end
end

defp sign_up_without_pkce(%SignUpWithPassword{} = credentials, %Client{} = client) do
attrs = %{email: credentials.email, phone: credentials.phone, password: credentials.password}

with {:ok, params} <- SignUpRequest.create(attrs, credentials.options),
{:ok, user} <- sign_up_request(params, client) do
{:ok, user, nil}
end
end

defp sign_up_request(%SignUpRequest{} = request, %Client{} = client) do
# add xform and redirect_to options to request
headers = api_headers(client)
uri = Endpoints.sign_up(client)

with {:ok, response} <- Fetcher.post(uri, request, headers) do
User.parse(response)
UserHandler.sign_up(client, credentials)
end
end

defp api_headers(%Client{} = client) do
Fetcher.apply_headers(client.conn.api_key, client.conn.access_token, client.global.headers)
end
end
45 changes: 0 additions & 45 deletions apps/supabase_auth/lib/supabase/go_true/endpoints.ex

This file was deleted.

Empty file.
36 changes: 23 additions & 13 deletions apps/supabase_auth/lib/supabase/go_true/schemas/sign_in_request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ defmodule Supabase.GoTrue.Schemas.SignInRequest do

alias Supabase.GoTrue.Schemas.SignInWithPassword

@required_fields ~w[password]a
@optional_fields ~w[email phone]a

@derive Jason.Encoder
@primary_key false
embedded_schema do
Expand All @@ -18,24 +15,37 @@ defmodule Supabase.GoTrue.Schemas.SignInRequest do
field(:password, :string)

embeds_one :gotrue_meta_security, GoTrueMetaSecurity, primary_key: false do
@derive Jason.Encoder
field(:captcha_token, :string)
end
end

def create(attrs, nil) do
def create(%SignInWithPassword{} = signin) do
attrs = SignInWithPassword.to_sign_in_params(signin)
gotrue_meta = %__MODULE__.GoTrueMetaSecurity{captcha_token: signin.options.captcha_token}

%__MODULE__{}
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> cast(attrs, [:email, :phone, :password])
|> put_embed(:gotrue_meta_security, gotrue_meta, required: true)
|> validate_required([:password])
|> validate_required_inclusion([:email, :phone])
|> apply_action(:insert)
end

def create(attrs, %SignInWithPassword.Options{} = options) do
gotrue_meta = %__MODULE__.GoTrueMetaSecurity{captcha_token: options.captcha_token}
defp validate_required_inclusion(%{valid?: false} = c, _), do: c

%__MODULE__{}
|> cast(attrs, @required_fields ++ @optional_fields)
|> put_embed(:gotrue_meta_security, gotrue_meta, required: true)
|> validate_required(@required_fields)
|> apply_action(:insert)
defp validate_required_inclusion(changeset, fields) do
if Enum.any?(fields, &present?(changeset, &1)) do
changeset
else
changeset
|> add_error(:email, "at least an email or phone is required")
|> add_error(:phone, "at least an email or phone is required")
end
end

defp present?(changeset, field) do
value = get_change(changeset, field)
value && value != ""
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ defmodule Supabase.GoTrue.Schemas.SignInWithPassword do
end
end

def to_sign_in_params(%__MODULE__{} = signin) do
Map.take(signin, [:email, :phone, :password])
end

def parse(attrs) do
%__MODULE__{}
|> cast(attrs, ~w[email phone password]a)
|> cast_embed(:options, with: &options_changeset/2, required: false)
|> validate_required([:password])
|> maybe_put_default_options()
|> apply_action(:parse)
end

defp maybe_put_default_options(%{valid?: false} = c), do: c

defp maybe_put_default_options(changeset) do
if get_embed(changeset, :options) do
changeset
else
put_embed(changeset, :options, %__MODULE__.Options{})
end
end

defp options_changeset(options, attrs) do
cast(options, attrs, ~w[email_redirect_to data captcha_token]a)
end
Expand Down
24 changes: 15 additions & 9 deletions apps/supabase_auth/lib/supabase/go_true/schemas/sign_up_request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,30 @@ defmodule Supabase.GoTrue.Schemas.SignUpRequest do
field(:code_challenge_method, :string)

embeds_one :gotrue_meta_security, GoTrueMetaSecurity, primary_key: false do
@derive Jason.Encoder
field(:captcha_token, :string)
end
end

def create(attrs, nil) do
%__MODULE__{}
def changeset(signup \\ %__MODULE__{}, attrs, go_true_meta) do
signup
|> cast(attrs, @required_fields ++ @optional_fields)
|> put_embed(:gotrue_meta_security, go_true_meta)
|> validate_required(@required_fields)
|> apply_action(:insert)
end

def create(attrs, %SignUpWithPassword.Options{} = options) do
go_true_meta = %__MODULE__.GoTrueMetaSecurity{captcha_token: options.captcha_token}
def create(%SignUpWithPassword{} = signup) do
attrs = SignUpWithPassword.to_sign_up_params(signup)
go_true_meta = %__MODULE__.GoTrueMetaSecurity{captcha_token: signup.options.captcha_token}

%__MODULE__{}
|> cast(attrs, @required_fields ++ @optional_fields)
|> put_assoc(:data, go_true_meta)
|> validate_required(@required_fields)
|> apply_action(:insert)
changeset(attrs, go_true_meta)
end

def create(%SignUpWithPassword{} = signup, code_challenge, code_method) do
attrs = SignUpWithPassword.to_sign_up_params(signup, code_challenge, code_method)
go_true_meta = %__MODULE__.GoTrueMetaSecurity{captcha_token: signup.options.captcha_token}

changeset(attrs, go_true_meta)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,36 @@ defmodule Supabase.GoTrue.Schemas.SignUpWithPassword do
end
end

def to_sign_up_params(%__MODULE__{} = signup) do
Map.take(signup, [:email, :password, :phone])
end

def to_sign_up_params(%__MODULE__{} = signup, code_challenge, code_method) do
signup
|> to_sign_up_params()
|> Map.merge(%{code_challange: code_challenge, code_challenge_method: code_method})
end

@spec validate(map) :: Ecto.Changeset.t()
def validate(attrs) do
%__MODULE__{}
|> cast(attrs, [:email, :password, :phone])
|> cast_embed(:options, with: &options_changeset/2, required: false)
|> maybe_put_default_options()
|> validate_email_or_phone()
|> validate_required([:password])
end

defp maybe_put_default_options(%{valid?: false} = c), do: c

defp maybe_put_default_options(changeset) do
if get_embed(changeset, :options) do
changeset
else
put_embed(changeset, :options, %__MODULE__.Options{})
end
end

defp options_changeset(options, attrs) do
cast(options, attrs, ~w[email_redirect_to data captcha_token]a)
end
Expand Down
22 changes: 1 addition & 21 deletions apps/supabase_auth/lib/supabase/go_true/session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,7 @@ defmodule Supabase.GoTrue.Session do
%__MODULE__{}
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> cast_embed(:user, required: false)
|> apply_action(:parse)
end

@spec parse(map, User.t() | map) :: {:ok, t} | {:error, Ecto.Changeset.t()}
def parse(attrs, user) do
with {:ok, session} <- parse(attrs) do
session
|> change()
|> maybe_put_user_assoc(user)
|> apply_action(:parse)
end
end

defp maybe_put_user_assoc(changeset, %User{} = user) do
put_embed(changeset, :user, user, required: true)
end

defp maybe_put_user_assoc(changeset, %{} = user) do
case User.parse(user) do
{:ok, user} -> maybe_put_user_assoc(changeset, user)
{:error, changeset} -> changeset
end
end
end
Loading

0 comments on commit 1f54ec8

Please sign in to comment.