Is it an anti-pattern to call a function plug inside an action?

Hello!
I’m currently setting up my phoenix backend to allow for signup with google oauth. After the user has successfully authenticated with google I create the user in the DB. The last step is to create a refresh token, place it in the DB, place the user id in their session and add the refresh token to a cookie.

This last step seems like a good candidate for a plug since it’s taking a connection, performing some work, modifying the connection session and then returning it. However it has to run after the action and not before which doesn’t seem to be the normal use case for plugs.

I had looked into Plug.Conn.register_before_send however this doesn’t work as my plug needs the user ID and I can’t find a way to pass that information from the action to my plug (conn.assigns didn’t work since I need to call register_before_send before I can add the value to conn.assigns)

So my question is:
Should I just put this work in a function plug and invoke it directly inside my controller action, or is there a nicer way to accomplish it?

Extra info:

  • This functionality has to be reused in a few places so I can’t just put it in my controller action.

Thanks in advance for the help

You can use controllers as plugs https://hexdocs.pm/phoenix/Phoenix.Controller.html#module-controllers-are-plugs

Why does it have to be after the action completion. Can you not do something like this?

# Your existing plug
def sign_in(conn, credentials) do
  #...
  {:ok, token, user} = do_google_sign_in(credentials) # creates user in db returning the user and token
  
  conn
  |> ... # any prior plugs
  |> MyHelpers.Auth.do_last_stuff(%{refresh_token: token, user_id: user.id}))
  |> ... # any other plugs needed
  |> render(:sign_in)
end

# separate module somewhere (Depending on you app probably under the AppNameWeb dir)
defmodule MyAppWeb.Auth do 
  @import Plug.Conn

  def do_last_stuff(conn, %{user_id: user_id, refresh_token: refresh_token}) do
    conn
    |> put_session(:user_id, user_id)
    |> put_resp_cookie(:refresh_token, refresh_token)
  end
end

Otherwise I think you would likely have to do a redirect to another route that contains something like the above. If you post your controller would be able to give you some more info perhaps.

1 Like

This is pretty much what I ended up going with in the end. I’m still very new to elixir/phoenix so it felt wierd to use a plug within a function and not use the plug macro, but it definitely makes the most sense.