Skip to content

Commit

Permalink
feat: reset password and update user
Browse files Browse the repository at this point in the history
  • Loading branch information
zoedsoupe committed May 14, 2024
1 parent d3222b0 commit 163d6b6
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 20 deletions.
49 changes: 49 additions & 0 deletions lib/supabase/go_true.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule Supabase.GoTrue do
alias Supabase.GoTrue.Schemas.SignInWithPassword
alias Supabase.GoTrue.Schemas.SignInWithSSO
alias Supabase.GoTrue.Schemas.SignUpWithPassword
alias Supabase.GoTrue.Schemas.UserParams
alias Supabase.GoTrue.Session
alias Supabase.GoTrue.User
alias Supabase.GoTrue.UserHandler
Expand Down Expand Up @@ -189,6 +190,54 @@ defmodule Supabase.GoTrue do
end
end

@doc """
Sends a recovery password email for the given email address.
## Parameters
- `client` - The `Supabase` client to use for the request.
- `email` - A valid user email address to recover password
- `opts`:
- `redirect_to`: the url where the user should be redirected to reset their password
- `captcha_token`
## Examples
iex> Supabase.GoTrue.reset_password_for_email(client, "[email protected]", redirect_to: "http://localohst:4000/reset-pass")
:ok
"""
@spec reset_password_for_email(Client.client(), String.t, opts) :: :ok | {:error, term}
when opts: [redirect_to: String.t] | [captcha_token: String.t] | [redirect_to: String.t, captcha_token: String.t]
def reset_password_for_email(client, email, opts) when is_client(client) do
with {:ok, client} <- Client.retrieve_client(client) do
UserHandler.recover_password(client, email, Map.new(opts))
end
end

@doc """
Updates the current logged in user.
## Parameters
- `client` - The `Supabase` client to use for the request.
- `conn` - The current `Plug.Conn` or `Phoenix.LiveView.Socket` to get current user
- `attrs` - Check `UserParams`
## Examples
iex> params = %{email: "[email protected]", password: "new-pass"}
iex> Supabase.GoTrue.update_user(client, conn, params)
{:ok, conn}
"""
@spec update_user(Client.client, conn, UserParams.t) :: {:ok, conn} | {:error, term}
when conn: Plug.Conn.t | Phoenix.LiveView.Socket.t
def update_user(client, conn, attrs) when is_client(client) do
with {:ok, client} <- Client.retrieve_client(client),
{:ok, params} <- UserParams.parse(attrs) do
if conn.assigns.current_user do
UserHandler.update_user(client, conn, params)
else
{:error, :no_user_logged_in}
end
end
end

defmacrop wrap_gotrue_functions(module) do
quote unquote: false, bind_quoted: [module: module] do
for {fun, arity} <- module.__info__(:functions) do
Expand Down
6 changes: 4 additions & 2 deletions lib/supabase/go_true/plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ defmodule Supabase.GoTrue.Plug do
defp do_login(conn, session, params) do
user_return_to = get_session(conn, :user_return_to)

:ok = Supabase.Client.update_access_token(@client, session.access_token)

conn
|> renew_session()
|> put_token_in_session(session.access_token)
Expand Down Expand Up @@ -108,7 +110,7 @@ defmodule Supabase.GoTrue.Plug do
session = %Session{access_token: user_token}
user_token && Admin.sign_out(@client, session, scope)

live_socket_id = get_session(conn, :live_socket_id)
live_socket_id = get_session(conn, :live_socket_id)
endpoint = Application.get_env(:supabase_gotrue, :endpoint)

if live_socket_id && endpoint do
Expand Down Expand Up @@ -214,7 +216,7 @@ live_socket_id = get_session(conn, :live_socket_id)

defp maybe_store_return_to(conn), do: conn

defp put_token_in_session(conn, token) do
def put_token_in_session(conn, token) do
base64_token = Base.url_encode64(token)

conn
Expand Down
36 changes: 36 additions & 0 deletions lib/supabase/go_true/schemas/user_params.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Supabase.GoTrue.Schemas.UserParams do
@moduledoc """
Uuser params schema. This schema is used to validate and parse the parameters for creating a new admin user.
## Fields
* `data` - The metadata to associate with the user.
* `email` - The user's email.
* `phone` - The user's phone.
* `password` - The user's password.
* `nonce` - The user's nonce.
* `email_redirect_to` - The user's nonce.
"""

import Ecto.Changeset

@types %{
data: :map,
email: :string,
phone: :string,
password: :string,
nonce: :string,
email_redirect_to: :string
}

def parse(attrs) do
{%{}, @types}
|> cast(attrs, Map.keys(@types))
|> apply_action(:parse)
end

def parse_update(attrs) do
{%{}, @types}
|> cast(attrs, Map.keys(@types))
|> apply_action(:parse)
end
end
1 change: 1 addition & 0 deletions lib/supabase/go_true/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ defmodule Supabase.GoTrue.User do
field(:email_confirmed_at, :naive_datetime)
field(:phone_confirmed_at, :naive_datetime)
field(:last_sign_in_at, :naive_datetime)
field :encrypted_password, :string
field(:role, :string)

embeds_many(:factors, Supabase.GoTrue.User.Factor)
Expand Down
101 changes: 91 additions & 10 deletions lib/supabase/go_true/user_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Supabase.GoTrue.UserHandler do

alias Supabase.Client
alias Supabase.Fetcher
alias Supabase.GoTrue
alias Supabase.GoTrue.LiveView
alias Supabase.GoTrue.PKCE
alias Supabase.GoTrue.Schemas.SignInRequest
alias Supabase.GoTrue.Schemas.SignInWithIdToken
Expand All @@ -22,6 +24,7 @@ defmodule Supabase.GoTrue.UserHandler do
@sso_uri "/sso"
@otp_uri "/otp"
@verify_otp_uri "/verify"
@reset_pass_uri "/recover"

def get_user(%Client{} = client, access_token) do
headers = Fetcher.apply_client_headers(client, access_token)
Expand All @@ -35,9 +38,8 @@ defmodule Supabase.GoTrue.UserHandler do
with {:ok, request} <- VerifyOTP.to_request(params),
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @verify_otp_uri),
endpoint = append_query(endpoint, %{redirect_to: get_in(request, [:options, :redirect_to])}),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response["data"]["session"]}
endpoint = append_query(endpoint, %{redirect_to: get_in(request, [:options, :redirect_to])}) do
Fetcher.post(endpoint, request, headers, resolve_json: true)
end
end

Expand All @@ -49,7 +51,7 @@ defmodule Supabase.GoTrue.UserHandler do
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @otp_uri),
endpoint = append_query(endpoint, %{redirect_to: request.redirect_to}),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true) do
if is_nil(signin.email), do: {:ok, response["data"]["message_id"]}, else: :ok
end
end
Expand All @@ -59,7 +61,7 @@ defmodule Supabase.GoTrue.UserHandler do
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @otp_uri),
endpoint = append_query(endpoint, %{redirect_to: request.redirect_to}),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true) do
if is_nil(signin.email), do: {:ok, response["data"]["message_id"]}, else: :ok
end
end
Expand All @@ -72,7 +74,7 @@ defmodule Supabase.GoTrue.UserHandler do
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @sso_uri),
endpoint = append_query(endpoint, %{redirect_to: request.redirect_to}),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true) do
{:ok, response["data"]["url"]}
end
end
Expand All @@ -82,7 +84,7 @@ defmodule Supabase.GoTrue.UserHandler do
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @sso_uri),
endpoint = append_query(endpoint, %{redirect_to: request.redirect_to}),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true) do
{:ok, response["data"]["url"]}
end
end
Expand All @@ -108,7 +110,7 @@ defmodule Supabase.GoTrue.UserHandler do
client
|> Client.retrieve_auth_url(@sign_in_uri)
|> append_query(%{grant_type: grant_type, redirect_to: request.redirect_to})
|> Fetcher.post(request, headers)
|> Fetcher.post(request, headers, resolve_json: true)
end

def sign_up(%Client{} = client, %SignUpWithPassword{} = signup)
Expand All @@ -118,7 +120,7 @@ defmodule Supabase.GoTrue.UserHandler do
with {:ok, request} <- SignUpRequest.create(signup, challenge, method),
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @sign_up_uri),
{:ok, response} <- Fetcher.post(endpoint, request, headers),
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true),
{:ok, user} <- User.parse(response) do
{:ok, user, challenge}
end
Expand All @@ -128,11 +130,90 @@ defmodule Supabase.GoTrue.UserHandler do
with {:ok, request} <- SignUpRequest.create(signup),
headers = Fetcher.apply_client_headers(client),
endpoint = Client.retrieve_auth_url(client, @sign_up_uri),
{:ok, response} <- Fetcher.post(endpoint, request, headers) do
{:ok, response} <- Fetcher.post(endpoint, request, headers, resolve_json: true) do
User.parse(response)
end
end

def recover_password(%Client{} = client, email, %{} = opts)
when client.auth.flow_type == :pkce do
{challenge, method} = generate_pkce()

body = %{
email: email,
code_challenge: challenge,
code_challenge_method: method,
go_true_meta_security: %{captcha_token: opts[:captcha_token]}
}

headers = Fetcher.apply_client_headers(client)
endpoint = Client.retrieve_auth_url(client, @reset_pass_uri)
endpoint = append_query(endpoint, %{redirect_to: opts[:redirect_to]})

with {:ok, _} <- Fetcher.post(endpoint, body, headers) do
:ok
end
end

def recover_password(%Client{} = client, email, %{} = opts) do
body = %{
email: email,
go_true_meta_security: %{captcha_token: opts[:captcha_token]}
}

headers = Fetcher.apply_client_headers(client)
endpoint = Client.retrieve_auth_url(client, @reset_pass_uri)
endpoint = append_query(endpoint, %{redirect_to: opts[:redirect_to]})

with {:ok, _} <- Fetcher.post(endpoint, body, headers) do
:ok
end
end

def update_user(%Client{} = client, conn, %{} = params)
when client.auth.flow_type == :pkce do
{challenge, method} = generate_pkce()

access_token = case conn do
%Plug.Conn{} -> Plug.Conn.get_session(conn, :user_token)
%Phoenix.LiveView.Socket{} -> conn.assigns.user_token
end

body = Map.merge(params, %{code_challenge: challenge, code_challenge_method: method})
headers = Fetcher.apply_client_headers(client, access_token)
endpoint = Client.retrieve_auth_url(client, @single_user_uri)
endpoint = append_query(endpoint, %{redirect_to: params[:email_redirect_to]})

session = %{"user_token" => access_token}

with {:ok, _} <- Fetcher.put(endpoint, body, headers) do
case conn do
%Plug.Conn{} -> {:ok, GoTrue.Plug.fetch_current_user(conn, nil)}
%Phoenix.LiveView.Socket{} -> {:ok, LiveView.mount_current_user(session, conn)}
end
end
end

def update_user(%Client{} = client, conn, %{} = params) do
access_token = case conn do
%Plug.Conn{} -> Plug.Conn.get_session(conn, :user_token)
%Phoenix.LiveView.Socket{} -> conn.assigns.user_token
end

headers = Fetcher.apply_client_headers(client, access_token)
endpoint = Client.retrieve_auth_url(client, @single_user_uri)
endpoint = append_query(endpoint, %{redirect_to: params[:email_redirect_to]})

session = %{"user_token" => access_token}

with {:ok, _} <- Fetcher.put(endpoint, params, headers) do
case conn do
%Plug.Conn{} -> {:ok, GoTrue.Plug.fetch_current_user(conn, nil)}
%Phoenix.LiveView.Socket{} -> {:ok, LiveView.mount_current_user(session, conn)}
end
end
end

def get_url_for_provider(%Client{} = client, %SignInWithOauth{} = oauth)
when client.auth.flow_type == :pkce do
{challenge, method} = generate_pkce()
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule SupabaseAuth.MixProject do
use Mix.Project

@version "0.3.3"
@version "0.3.4"
@source_url "https://github.com/zoedsoupe/gotrue-ex"

def project do
Expand Down
Loading

0 comments on commit 163d6b6

Please sign in to comment.