I got "request_password_reset_action_name not found in %AshAuthentication.Strategy.Password.Resettable" Error

I got this error when I get “http://localhost:4000/sign-in
but I don’t know how could I solve it.

key :request_password_reset_action_name not found in: [
  %AshAuthentication.Strategy.Password.Resettable{
    token_lifetime: 72,
    request_password_reset_action_name: :request_password_reset_with_password,
    password_reset_action_name: :password_reset_with_password,
    sender: {Dentallog.Accounts.User.Senders.SendPasswordResetEmail, []}
  }
]

If you are using the dot syntax, such as map.field, make sure the left-hand side of the dot is a map

user.ex

...
code_interface do
    define_for Dentallog.Accounts
    define :register_with_password, args: [:email, :password, :password_confirmation]
end

...

changes do
    change Dentallog.Accounts.User.Changes.RemoveAllTokens,
      where: [action_is(:password_reset_with_password)]
end

authentication do
    api Dentallog.Accounts

    strategies do
      password :password do
        identity_field(:email)
        hashed_password_field(:hashed_password)
        sign_in_tokens_enabled?(true)

        resettable do
          sender(Dentallog.Accounts.User.Senders.SendPasswordResetEmail)
        end
      end
    end

    tokens do
      enabled?(true)
      token_resource(Dentallog.Accounts.UserToken)
      signing_secret(Dentallog.Accounts.Secrets)
      store_all_tokens?(true)
      require_token_presence_for_authentication?(true)
    end

    add_ons do
      confirmation :confirm do
        monitor_fields([:email])

        sender(Dentallog.Accounts.User.Senders.SendConfirmationEmail)
      end
    end
  end

  identities do
    identity :unique_email, [:email] do
      eager_check_with Dentallog.Accounts
    end
  end
end

router.ex

scope "/", DentallogWeb do
    pipe_through(:browser)
    reset_route([])

    sign_in_route(register_path: "/register", reset_path: "/reset")
    sign_out_route(AuthController)
    auth_routes_for(Dentallog.Accounts.User, to: AuthController)

    get("/", PageController, :home)
  end

This looks like a simple bug somewhere in ash_authentication. Can you provide the full stacktrace for the exception?

Okay, here’s the error log.

# KeyError at GET /sign-in

Exception:

    ** (KeyError) key :request_password_reset_action_name not found in: [
      %AshAuthentication.Strategy.Password.Resettable{
        token_lifetime: 72,
        request_password_reset_action_name: :request_password_reset_with_password,
        password_reset_action_name: :password_reset_with_password,
        sender: {Dentallog.Accounts.User.Senders.SendPasswordResetEmail, []}
      }
    ]
    
    If you are using the dot syntax, such as map.field, make sure the left-hand side of the dot is a map
        (ash_authentication_phoenix 1.9.2) lib/ash_authentication_phoenix/components/password.ex:122: AshAuthentication.Phoenix.Components.Password."render (overridable 1)"/1
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/renderer.ex:79: Phoenix.LiveView.Renderer.to_rendered/2
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:335: Phoenix.LiveView.Diff.component_to_rendered/3
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:767: Phoenix.LiveView.Diff.render_component/8
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:711: Phoenix.LiveView.Diff.zip_components/5
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:694: anonymous fn/4 in Phoenix.LiveView.Diff.render_pending_components/6
        (stdlib 5.2) maps.erl:416: :maps.fold_1/4
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:639: Phoenix.LiveView.Diff.render_pending_components/6
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/diff.ex:143: Phoenix.LiveView.Diff.render/3
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/static.ex:249: Phoenix.LiveView.Static.to_rendered_content_tag/4
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/static.ex:132: Phoenix.LiveView.Static.render/3
        (phoenix_live_view 0.20.7) lib/phoenix_live_view/controller.ex:39: Phoenix.LiveView.Controller.live_render/3
        (phoenix 1.7.11) lib/phoenix/router.ex:484: Phoenix.Router.__call__/5
        (dentallog 0.1.0) lib/dentallog_web/endpoint.ex:1: DentallogWeb.Endpoint.plug_builder_call/2
        (dentallog 0.1.0) deps/plug/lib/plug/debugger.ex:136: DentallogWeb.Endpoint."call (overridable 3)"/2
        (dentallog 0.1.0) lib/dentallog_web/endpoint.ex:1: DentallogWeb.Endpoint.call/2
        (phoenix 1.7.11) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
        (plug_cowboy 2.7.0) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
        (cowboy 2.10.0) /Users/dwlee/Documents/dentallog/dentallog-server/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.10.0) /Users/dwlee/Documents/dentallog/dentallog-server/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    

Code:

`lib/ash_authentication_phoenix/components/password.ex`

    117       reset_id =
    118         strategy.resettable &&
    119           generate_id(
    120             subject_name,
    121             strategy_name,
    122>            strategy.resettable.request_password_reset_action_name
    123           )
    124   
    125       assigns =
    126         assigns
    127         |> assign(
    
`lib/phoenix_live_view/renderer.ex`

    74             |> check_rendered!(render_with)
    75   
    76           %{} ->
    77             if function_exported?(view, :render, 1) do
    78               assigns
    79>              |> view.render()
    80               |> check_rendered!(view)
    81             else
    82               template =
    83                 view.__info__(:compile)[:source]
    84                 |> Path.dirname()
    
`lib/phoenix_live_view/diff.ex`

    330       |> Utils.maybe_call_update!(component, assigns)
    331       |> component_to_rendered(component, assigns[:id])
    332     end
    333   
    334     defp component_to_rendered(socket, component, id) do
    335>      rendered = Phoenix.LiveView.Renderer.to_rendered(socket, component)
    336   
    337       if rendered.root != true and id != nil do
    338         reason =
    339           case rendered.root do
    340             nil -> "Stateful components must return a HEEx template (~H sigil or .heex extension)"
    
`lib/phoenix_live_view/diff.ex`

    762     defp render_component(socket, component, id, cid, new?, cids, diffs, components) do
    763       changed? = new? or Utils.changed?(socket)
    764   
    765       {socket, pending, diff, {cid_to_component, id_to_cid, uuids}} =
    766         if changed? do
    767>          rendered = component_to_rendered(socket, component, id)
    768   
    769           {changed?, linked_cid, prints} =
    770             maybe_reuse_static(rendered, socket, component, cids, components)
    771   
    772           {diff, component_prints, pending, components, nil} =
    
`lib/phoenix_live_view/diff.ex`

    706            {pending, diffs, components}
    707          ) do
    708       diffs = maybe_put_events(diffs, socket)
    709   
    710       {new_pending, diffs, compnents} =
    711>        render_component(socket, component, id, cid, new?, cids, diffs, components)
    712   
    713       pending = Map.merge(pending, new_pending, fn _, v1, v2 -> v2 ++ v1 end)
    714       zip_components(sockets, metadata, component, cids, {pending, diffs, compnents})
    715     end
    716   
    
`lib/phoenix_live_view/diff.ex`

    689   
    690               {sockets, Map.put(telemetry_metadata, :sockets, sockets)}
    691             end)
    692   
    693           metadata = Enum.reverse(metadata)
    694>          triplet = zip_components(sockets, metadata, component, cids, {pending, diffs, components})
    695           {triplet, seen_ids}
    696         end)
    697   
    698       render_pending_components(socket, pending, seen_ids, cids, diffs, components)
    699     end
    
`maps.erl`

    No code available.

`lib/phoenix_live_view/diff.ex`

    634   
    635     defp render_pending_components(socket, pending, seen_ids, cids, diffs, components) do
    636       acc = {{%{}, diffs, components}, seen_ids}
    637   
    638       {{pending, diffs, components}, seen_ids} =
    639>        Enum.reduce(pending, acc, fn {component, entries}, acc ->
    640           {{pending, diffs, components}, seen_ids} = acc
    641           update_many? = function_exported?(component, :update_many, 1)
    642           entries = maybe_preload_components(component, Enum.reverse(entries))
    643   
    644           {assigns_sockets, metadata, components, seen_ids} =
    
`lib/phoenix_live_view/diff.ex`

    138       # cid_to_component is used by maybe_reuse_static and it must be a copy before changes.
    139       # However, given traverse does not change cid_to_component, we can read it now.
    140       {cid_to_component, _, _} = components
    141   
    142       {cdiffs, components} =
    143>        render_pending_components(socket, pending, cid_to_component, %{}, components)
    144   
    145       socket = %{socket | fingerprints: prints}
    146       diff = maybe_put_title(diff, socket)
    147       {diff, cdiffs} = extract_events({diff, cdiffs})
    148       {socket, maybe_put_cdiffs(diff, cdiffs), components}
    
`lib/phoenix_live_view/static.ex`

    244       content_tag(tag, attrs, "")
    245     end
    246   
    247     defp to_rendered_content_tag(socket, tag, view, attrs) do
    248       rendered = Phoenix.LiveView.Renderer.to_rendered(socket, view)
    249>      {_, diff, _} = Diff.render(socket, rendered, Diff.new_components())
    250       content_tag(tag, attrs, Diff.to_iodata(diff))
    251     end
    252   
    253     defp content_tag(tag, attrs, content) do
    254       tag = to_string(tag)
    
`lib/phoenix_live_view/static.ex`

    127             {:data, data_attrs}
    128             | extended_attrs
    129           ]
    130   
    131           try do
    132>            {:ok, to_rendered_content_tag(socket, tag, view, attrs), socket.assigns}
    133           catch
    134             :throw, {:phoenix, :child_redirect, redirected, flash} ->
    135               {:stop, Utils.replace_flash(%{socket | redirected: redirected}, flash)}
    136           end
    137   
    
`lib/phoenix_live_view/controller.ex`

    34           end
    35         end
    36   
    37     """
    38     def live_render(%Plug.Conn{} = conn, view, opts \\ []) do
    39>      case LiveView.Static.render(conn, view, opts) do
    40         {:ok, content, socket_assigns} ->
    41           conn
    42           |> Plug.Conn.fetch_query_params()
    43           |> ensure_format()
    44           |> Phoenix.Controller.put_view(LiveView.Static)
    
`lib/phoenix/router.ex`

    479           :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    480           halted_conn
    481   
    482         %Plug.Conn{} = piped_conn ->
    483           try do
    484>            plug.call(piped_conn, plug.init(opts))
    485           else
    486             conn ->
    487               measurements = %{duration: System.monotonic_time() - start}
    488               metadata = %{metadata | conn: conn}
    489               :telemetry.execute([:phoenix, :router_dispatch, :stop], measurements, metadata)
    
`lib/dentallog_web/endpoint.ex`

    1>  defmodule DentallogWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :dentallog
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`deps/plug/lib/plug/debugger.ex`

    131             case conn do
    132               %Plug.Conn{path_info: ["__plug__", "debugger", "action"], method: "POST"} ->
    133                 Plug.Debugger.run_action(conn)
    134   
    135               %Plug.Conn{} ->
    136>                super(conn, opts)
    137             end
    138           rescue
    139             e in Plug.Conn.WrapperError ->
    140               %{conn: conn, kind: kind, reason: reason, stack: stack} = e
    141               Plug.Debugger.__catch__(conn, kind, reason, stack, @plug_debugger)
    
`lib/dentallog_web/endpoint.ex`

    1>  defmodule DentallogWeb.Endpoint do
    2     use Phoenix.Endpoint, otp_app: :dentallog
    3   
    4     # The session will be stored in the cookie and signed,
    5     # this means its contents can be read but not tampered with.
    6     # Set :encryption_salt if you would also like to encrypt it.
    
`lib/phoenix/endpoint/sync_code_reload_plug.ex`

    17   
    18     def call(conn, {endpoint, opts}), do: do_call(conn, endpoint, opts, true)
    19   
    20     defp do_call(conn, endpoint, opts, retry?) do
    21       try do
    22>        endpoint.call(conn, opts)
    23       rescue
    24         exception in [UndefinedFunctionError] ->
    25           case exception do
    26             %UndefinedFunctionError{module: ^endpoint} when retry? ->
    27               # Sync with the code reloader and retry once
    
`lib/plug/cowboy/handler.ex`

    6     def init(req, {plug, opts}) do
    7       conn = @connection.conn(req)
    8   
    9       try do
    10         conn
    11>        |> plug.call(opts)
    12         |> maybe_send(plug)
    13         |> case do
    14           %Plug.Conn{adapter: {@connection, %{upgrade: {:websocket, websocket_args}} = req}} = conn ->
    15             {handler, state, cowboy_opts} = websocket_args
    16             {__MODULE__, copy_resp_headers(conn, req), {handler, state}, cowboy_opts}
    
`/Users/dwlee/Documents/dentallog/dentallog-server/deps/cowboy/src/cowboy_handler.erl`

    32   -optional_callbacks([terminate/3]).
    33   
    34   -spec execute(Req, Env) -> {ok, Req, Env}
    35   	when Req::cowboy_req:req(), Env::cowboy_middleware:env().
    36   execute(Req, Env=#{handler := Handler, handler_opts := HandlerOpts}) ->
    37>  	try Handler:init(Req, HandlerOpts) of
    38   		{ok, Req2, State} ->
    39   			Result = terminate(normal, Req2, State, Handler),
    40   			{ok, Req2, Env#{result => Result}};
    41   		{Mod, Req2, State} ->
    42   			Mod:upgrade(Req2, Env, Handler, State);
    
`/Users/dwlee/Documents/dentallog/dentallog-server/deps/cowboy/src/cowboy_stream_h.erl`

    301   	end.
    302   
    303   execute(_, _, []) ->
    304   	ok;
    305   execute(Req, Env, [Middleware|Tail]) ->
    306>  	case Middleware:execute(Req, Env) of
    307   		{ok, Req2, Env2} ->
    308   			execute(Req2, Env2, Tail);
    309   		{suspend, Module, Function, Args} ->
    310   			proc_lib:hibernate(?MODULE, resume, [Env, Tail, Module, Function, Args]);
    311   		{stop, _Req2} ->
    

## Connection details

### Params

    %{}

### Request info

  * URI: http://localhost:4000/sign-in
  * Query string: 

### Headers
  
  * accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
  * accept-encoding: gzip, deflate, br
  * accept-language: ko-KR,ko;q=0.9,en;q=0.8
  * connection: keep-alive
  * cookie: _ga=GA1.1.1985068180.1699638072; _demo_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYaGtuSWpBdnVBREsxM0xFakhzVkdhdXEw.gsmknfnuJunbSOMqHNoBaFJ2UZ6HYpiQkRIAyFiP6sI; request_logger=SFMyNTY.g2gDbQAAAAR3S09tbgYA4xNrFIwBYgABUYA.2URPJUSwSI2G_V4HNqkAasyNZUPbEd_PsHBxo7ItOMY; lb:probe_cookie=; _bmvp_key=SFMyNTY.g3QAAAADbQAAAAtfY3NyZl90b2tlbm0AAAAYWk1ZTGVCNTN4ajQ3RmJONEFpcGxiaDVVbQAAAA5saXZlX3NvY2tldF9pZG0AAAA7dXNlcnNfc2Vzc2lvbnM6LTRGb2xWZDBxdWVqcFdvT3A5RzczTlhaQXVRc2dxZmt1MTZwN2RNZ01qZz1tAAAACnVzZXJfdG9rZW5tAAAAIPuBaJVXdKrno6VqDqfRu9zV2QLkLIKn5Lteqe3TIDI4.qJZd1-eXfXAX3cbjKMYuQWoSdqtnGAyCLpZkwgxFLX4; _ga_R3ZRQKE94S=GS1.1.1705585069.24.1.1705587336.0.0.0; _homepage_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYbWctRXI5UTJFbUFSaFV1a2pkTFBrTHJj.vW0EnxVfMD4cLj9n3N9e0CTsEwZJPzDcfavfMW7teZc; lb:user_data=eyJuYW1lIjoiRFdMRUUiLCJoZXhfY29sb3IiOiIjRkJCRjI0In0=; locale=ko; lb:session=SFMyNTY.g3QAAAAFbQAAAAs1NjQwOTp0b2tlbm0AAAAgRW9B9T59bi0LKlXWIy_GA2mzeflAJH2eh3_738oOUYltAAAAC19jc3JmX3Rva2VubQAAABhWZFE4Nm9QM1ZRdzNmUzR3TElkN1VnVUltAAAAD2N1cnJlbnRfdXNlcl9pZG0AAAAgeHgzeW4zdW1xZWprY29wNnB5MnhqcGpubjRjZGlvZTJtAAAADWlkZW50aXR5X2RhdGF0AAAAAXcCaWRtAAAAIHh4M3luM3VtcWVqa2NvcDZweTJ4anBqbm40Y2Rpb2UybQAAAAl1c2VyX2RhdGF0AAAAAm0AAAAJaGV4X2NvbG9ybQAAAAcjRkJCRjI0bQAAAARuYW1lbQAAAAVEV0xFRQ.YgnLHmDp_gCXP6xUQz6yAFqdH5GOwJeDJLWL9LDM-_c; _meerkat_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYV0hBejNCdWJpbEZDY0JPWmM0M1Z6SU5P.AHsA2wIdqCxhOIUd7q75ZpF2tyD-DRTbzhbHEC1mEZ8; _foo_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYT2N6bXl5WnFoazN4allUcTctWk03QlE3.uKWtoN0ub9DeSUxCrB35lvy0T0svCypZEi9P-YDtUWA; _meow_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYNV94TkpzSGhJS3NlT1ZBTWs0ODZPQ3ZQ.tRSQUqHUSy1Y0lbx_7ceA2QmTSLgaD37HW0-VZ0jXss; _dentallog_key=SFMyNTY.g3QAAAABbQAAAAtfY3NyZl90b2tlbm0AAAAYdEo3T0c0OEt2QjJWanRuVGhSeFljY1lv.G3yGJ1ZlMTC4CmhcCnhBsAwGdVoG6ndfKTX2OYJORZk
  * host: localhost:4000
  * referer: http://localhost:4000/
  * sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"
  * sec-ch-ua-mobile: ?0
  * sec-ch-ua-platform: "macOS"
  * sec-fetch-dest: document
  * sec-fetch-mode: navigate
  * sec-fetch-site: same-origin
  * sec-fetch-user: ?1
  * upgrade-insecure-requests: 1
  * user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36

### Session

    %{"_csrf_token" => "tJ7OG48KvB2VjtnThRxYccYo"}

And this is my mix.exs

defp deps do
    [
      {:phoenix, "~> 1.7.10"},
      {:phoenix_ecto, "~> 4.4"},
      {:ecto_sql, "~> 3.10"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 4.0"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.20.1"},
      {:floki, ">= 0.30.0", only: :test},
      {:phoenix_live_dashboard, "~> 0.8.2"},
      {:esbuild, "~> 0.8", runtime: Mix.env() == :dev},
      {:tailwind, "~> 0.2.0", runtime: Mix.env() == :dev},
      {:swoosh, "~> 1.3"},
      {:finch, "~> 0.13"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.20"},
      {:jason, "~> 1.2"},
      {:dns_cluster, "~> 0.1.1"},
      {:plug_cowboy, "~> 2.5"},
      {:ash, "~> 2.18"},
      {:ash_postgres, "~> 1.3.66"},
      {:ash_phoenix, "~> 1.2.24"},
      {:ash_json_api, "~> 0.34.1"},
      {:ash_archival, "~> 0.1"},
      {:ash_paper_trail, "~> 0.1.0"},
      {:ash_graphql, "~> 0.26.9"},
      {:ash_authentication, "~> 3.11"},
      {:ash_authentication_phoenix, "~> 1.9.2"}
    ]
  end

I apologize for any inconvenience caused.
It seems that upgrading to {:ash_authentication, "~> 3.11"}{:ash_authentication, "~> 3.12.2"} has resolved the issue and it’s functioning properly now.

1 Like