mattei
Hi! I’m working on changing phx.gen.auth to work with JSON only. It’s going stellar, but I’ve faced an issue and I was wondering if someone could tell me what’s going on under the hood.
I’ve modified the :api plug to use sessions, and that’s going fine. I intend to use session tokens to authenticate. All views were changed to respond with JSON (using controller_json.ex files). My plug looks like this:
pipeline :api do
plug :accepts, ["json"]
# Use the same plugs for authentication as the browser pipeline.
plug :fetch_session
plug :put_secure_browser_headers
plug :fetch_current_user
end
Note the absence of any flash plugs–being an API, this isn’t needed here.
Now, while transitioning a controller to return JSON, everything was going fine until this code:
def create(conn, %{"user" => %{"email" => email}}) do
if user = Accounts.get_user_by_email(email) do
Accounts.deliver_user_confirmation_instructions(
user,
&url(~p"/users/confirm/#{&1}")
)
end
render(conn, :create)
end
When calling this controller, the request went through, but I ended up with this error:
Server: localhost:4000 (http)
Request: POST /accounts/confirm/
** (exit) an exception was raised:
** (ArgumentError) flash not fetched, call fetch_flash/2
(phoenix 1.7.10) lib/phoenix/controller.ex:1686: Phoenix.Controller.put_flash/3
(timeline 0.1.0) lib/timeline_web/controllers/user_confirmation_controller.ex:19: TimelineWeb.UserConfirmationController.create/2
(timeline 0.1.0) lib/timeline_web/controllers/user_confirmation_controller.ex:1: TimelineWeb.UserConfirmationController.action/2
(timeline 0.1.0) lib/timeline_web/controllers/user_confirmation_controller.ex:1: TimelineWeb.UserConfirmationController.phoenix_controller_pipeline/2
(phoenix 1.7.10) lib/phoenix/router.ex:432: Phoenix.Router.__call__/5
(timeline 0.1.0) lib/timeline_web/endpoint.ex:1: TimelineWeb.Endpoint.plug_builder_call/2
(timeline 0.1.0) /Users/sergio/Projects/timeline-stack/timeline/deps/plug/lib/plug/debugger.ex:136: TimelineWeb.Endpoint."call (overridable 3)"/2
(timeline 0.1.0) lib/timeline_web/endpoint.ex:1: TimelineWeb.Endpoint.call/2
(phoenix 1.7.10) lib/phoenix/endpoint/sync_code_reload_plug.ex:22: Phoenix.Endpoint.SyncCodeReloadPlug.do_call/4
(plug_cowboy 2.6.1) lib/plug/cowboy/handler.ex:11: Plug.Cowboy.Handler.init/2
(cowboy 2.10.0) /Users/sergio/Projects/timeline-stack/timeline/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
(cowboy 2.10.0) /Users/sergio/Projects/timeline-stack/timeline/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
(cowboy 2.10.0) /Users/sergio/Projects/timeline-stack/timeline/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
(stdlib 5.1.1) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
I looked through the pipeline & other related places, and found no references to the usual flash plugs, so I had no idea why flash was being referenced here at all. Only later on I realized that:
- The create function had a verified route for another function in the same controller, which was update
- That function did call put_flash/2
def update(conn, %{"token" => token}) do
case Accounts.confirm_user(token) do
{:ok, _} ->
conn
# |> put_flash(:info, "User confirmed successfully.")
|> redirect(to: ~p"/")
:error ->
# If there is a current user and the account was already confirmed,
# then odds are that the confirmation link was already visited, either
# by some automation or by the user themselves, so we redirect without
# a warning message.
case conn.assigns do
%{current_user: %{confirmed_at: confirmed_at}} when not is_nil(confirmed_at) ->
redirect(conn, to: ~p"/")
%{} ->
conn
# |> put_flash(:error, "User confirmation link is invalid or it has expired.")
|> redirect(to: ~p"/")
end
end
end
After removing that reference to put_flash, the error was gone (reasonably).
What I’m wondering is the mechanism that caused that other controller function to emit this error in the first place? I suspect the compiler is doing a check on that verified route, but I thought they only checked the validity of the route, not the plugs called within that route?
It’s behaving as if it did a runtime check of the controller method itself, and I’m unclear on how that works. Why does “put_flash/2” in a verified route seem to be triggered when running that verification?
Thank you! Note that I’ve fixed the error, just curious about how the framework works in this regard.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All Posts (oldest first)
- Show All Posts (newest first)
sodapopcan
It’s emitted from the
put_flashfunction, nothing to do with verified routes. See here.mattei
Understood, but why is that function being triggered, if it’s in a completely different route?
That’s the question at hand: the put_flash function is in a different function in that module, so there is no reason that error should trigger?
beepbeepbopbop
I would argue that you shouldn’t use flash at all if you’re trying to port
phx.gen.authas a JSON endpoint. Yes, you do need to ensure that the HTTP response headers are and that the cookie is provided, but flash is really something that’s dedicated to inform the user that something happened in one instance.In this instance, I don’t think it’s entirely unreasonable to push the flash responsibility to something else.
mattei
I’m not going to use flash at all! I’m aware it’s not useful and in the process of removing it all, as stated in the post. I’m curious however about the mechanism as to why a flash function (let behind by mistake) in another route would cause an error elsewhere.
The curious behavior is this: I called route_1 which seemingly has no relation to route_2, but got an error related to a function in route_2. It’s not a compile time error too, rather a runtime one. Why?
LostKobrakai
This seems to suggest you update route to be accessed via http. Hard to say what called it though.
sodapopcan
Ah crap, sorry, I didn’t read your question properly.
Is it the exact same stack trace or you were simply seeing a warning?
adw632
That was what I noticed too, something is POSTing to the confirmation endpoint route, might it be phx.gen.auth tests?
mattei
Nope–that was me
that’s the endpoint I was hitting to test out. But it’s a separate view from the one the function put_flash was used in.
mattei
That’s the stack trace, yes
sodapopcan
But is it the exact stacktrace with line numbers and everything? In a fresh, non-LiveView phx_gen_auth invocation, line 19 of
user_confirmation_controller.exis a call toput_flashwhich is not present in your first snippet. I assume you removed it, but is it possible you just hadn’t saved the file or something when you originally got the error? Otherwise,put_flashin theupdateaction is line 37.Just trying to understand because otherwise it makes no sense. There is nothing I can see about verified routes that would rewrite code in such a crazy way that it would cause a runtime exception in a function that isn’t being called.