Hi!
I wonder if there are any additional changes required to make custom controllers work with extensions like persistent session plug.
However, to make it easier to integrate extension, you can add callbacks to the controllers that do some light pre/post-processing of the request:
From https://github.com/danschultzer/pow#phoenix-controllers, seems to imply that using custom controllers breaks Pow.Extension.Phoenix.ControllerCallbacks, that is, there are no traces of any function in Pow.Extension.Phoenix.ControllerCallbacks being called during login/logout when using custom controllers.
Yeah, and this is for a good reason. Controller callbacks is mostly a compromise to make Pow plug n’ play, but ideally developers take control of everything they change rather than having implicit callbacks. I have written about it in the custom controller guide, and I’m also thinking of redoing how the callbacks work in #66.
I would recommend writing the controllers as explicit as possible. However, you can build a controller that uses the Pow.Phoenix.Controller macro, and will use the controller callbacks from the config. Take a look at the registration controller to get an idea of how it works:
https://github.com/danschultzer/pow/blob/master/lib/pow/phoenix/controllers/registration_controller.ex
Controllers are very thin in Pow with a process and response separation that makes it possible to hook in callbacks.
@danschultzer so how would one go about making a custom controller work with persisted sessions plug?
I would just call the plug actions directly in the individual actions rather than using the controller callbacks:
# In session create action
PowPersistentSession.Plug.create(conn, user)
# In session delete action
PowPersistentSession.Plug.delete(conn)
In Pow most important methods can be found in the plug modules, controller callbacks are usually very light.
@danschultzer thank you! It seems to have worked.
I would appreciate fuller documentation regarding how to integrate PowPersistentSession with custom controllers. For instance, how do you properly RequireAuthenticated in the router? As a test, I set persistent_session_ttl to just 30 seconds, but after the ttl I see that my user is still able to access protected routes, when I expected that their session would have been purged from the store and the cookie invalidated by that time.
You have to use plug PowPersistentSession.Plug.Cookie as described in https://hexdocs.pm/pow/1.0.19/pow_persistent_session.html#content
It's not a replacement for the session. It's there to ensure that the session can be recreated once it has expired. PowPersistentSession sets a cookie that can be used once to create a new session for the user. In your case the normal session works as expected. Let me know if this could be better described in the docs!
Oh, I see! So setting a ttl for the persistent session lower than the regular session will not result in sessions being invalidated after the persistent ttl. In that case the regular session TTLs will dictate when the session expires. But when the persistent session is in place with a longer ttl than the regular sessions, it will allow sessions that have expired to be renewed.
In that case, the only changes I need to make are to plug in the cookie, and to call PowPersistentSession.create/3 in my custom controller actions, and otherwise the use of pow remains the same. Is that right?
But when the persistent session is in place with a longer ttl than the regular sessions, it will allow sessions that have expired to be renewed.
In that case, the only changes I need to make are to plug in the cookie, and to call
PowPersistentSession.create/3in my custom controller actions, and otherwise the use of pow remains the same. Is that right?
Yeah, you are correct. You need to call PowPersistentSession.Plug.create/2 when you create the session with Pow.Plug.create/2.
Thanks Dan. I think I have things working now. Here is how I finally ended up in my controller actions:
def signup(conn, %{"user" => user_params}) do
conn
|> Pow.Plug.create_user(user_params)
|> case do
{:ok, user, conn} ->
conn
|> PowPersistentSession.Plug.create(user)
|> redirect(to: Routes.page_path(conn, :show))
{:error, changeset, conn} ->
render(conn, "index.html", changeset: changeset)
end
end
def login(conn, %{"user" => user_params}) do
conn
|> Pow.Plug.authenticate_user(user_params)
|> case do
{:ok, conn} ->
conn
|> PowPersistentSession.Plug.create(conn.assigns.current_user)
|> redirect(to: Routes.page_path(conn, :show))
{:error, conn} ->
changeset = Pow.Plug.change_user(conn, conn.params["user"])
conn
|> put_flash(:info, "Invalid email or password")
|> render("index.html", changeset: changeset)
end
end
Thanks @vaer-k for your code :+1:
This is definitely related to #516 which I just opened this morning.
Most helpful comment
I would just call the plug actions directly in the individual actions rather than using the controller callbacks:
In Pow most important methods can be found in the plug modules, controller callbacks are usually very light.