diff --git a/lib/supabase/go_true.ex b/lib/supabase/go_true.ex index 621ea45..5492a64 100644 --- a/lib/supabase/go_true.ex +++ b/lib/supabase/go_true.ex @@ -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 @@ -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, "john@example.com", 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: "another@example.com", 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 diff --git a/lib/supabase/go_true/plug.ex b/lib/supabase/go_true/plug.ex index 98f9ca1..6eda149 100644 --- a/lib/supabase/go_true/plug.ex +++ b/lib/supabase/go_true/plug.ex @@ -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) @@ -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 @@ -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 diff --git a/lib/supabase/go_true/schemas/user_params.ex b/lib/supabase/go_true/schemas/user_params.ex new file mode 100644 index 0000000..3d22d81 --- /dev/null +++ b/lib/supabase/go_true/schemas/user_params.ex @@ -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 diff --git a/lib/supabase/go_true/user.ex b/lib/supabase/go_true/user.ex index 679fe04..cff9020 100644 --- a/lib/supabase/go_true/user.ex +++ b/lib/supabase/go_true/user.ex @@ -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) diff --git a/lib/supabase/go_true/user_handler.ex b/lib/supabase/go_true/user_handler.ex index abb62e9..20f6f5d 100644 --- a/lib/supabase/go_true/user_handler.ex +++ b/lib/supabase/go_true/user_handler.ex @@ -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 @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -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() diff --git a/mix.exs b/mix.exs index da429a2..e360305 100644 --- a/mix.exs +++ b/mix.exs @@ -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 diff --git a/mix.lock b/mix.lock index 73e1d3a..37a3994 100644 --- a/mix.lock +++ b/mix.lock @@ -7,16 +7,16 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, + "ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, - "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, + "hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"}, + "mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, @@ -26,8 +26,8 @@ "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"}, - "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, - "supabase_potion": {:hex, :supabase_potion, "0.3.4", "0e9292bd27cc475b7209ba6f01b80b02d7876bb9af271c9c84444ab9078576c5", [:mix], [{:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:ex_doc, ">= 0.0.0", [hex: :ex_doc, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "669c9f443be3129b2eafcafcf7a62408cb3bdb4e5a1b645bb5827f2a5de1a57e"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "supabase_potion": {:hex, :supabase_potion, "0.3.7", "2b535db03ee22c7c30c18b4732f72a435559e6e94c68b5c282cd120311065641", [:mix], [{:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:ex_doc, ">= 0.0.0", [hex: :ex_doc, repo: "hexpm", optional: false]}, {:finch, "~> 0.16", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4048c79b7d091334c95b75dbee1b0ed524c0ff678a62882f92d3c8d81aece611"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"},