When we have an application with authorization/authentication functionality in place, there are some cases when we may want redirect a user to the url he was previously visiting before logging in. This article shows how use plugs to achieve that in Phoenix.
Strategy
We are going to use the following strategy:
Always store the last url of a GET request in a session variable.
When a user signs up or logs in redirect him to that stored url.
Storing url in a session
We want to store the url of every get request that is not related to creating a new user or a new session. For this example we will asume we have REST user
, session
and password_reset
routes, such as the ones used by the phauxth library. In this case, we would like to avoid storing the following routes:
GET /sessions/new
GET /password_resets/new
GET /password_resets/edit
For storing the url we are going to use session storage. Session storage only stores data until user closes the tab he is currently browsing. This makes the most sense for our use case as we do not want the user to log in and get redirected to a page he may have visited some days ago.
With all this in mind, we could write a FriendlyRedirect
module that exposes store_path_in_session/2
to act as a plug. In order to do that, the function needs to take a %Plug.Conn{}
struct and an options variable as arguments, and return another Plug.Conn{}
struct, to be passed to the next plug in the pipeline.
defmodule MyApp.FriendlyRedirect do
import Plug.Conn
def store_path_in_session(conn, _) do
# Get HTTP method and url from conn
method = conn.method
path = conn.request_path
# If conditions apply store path in session, else return conn unmodified
case {method, storable?(path)} do
{"GET", true} ->
put_session(conn, :firendly_redirect_path, path)
{_, _} ->
conn
end
end
defp storable?(path) do
!(url =~ r/user|session|password_resets/)
end
end
We can make the module available in the router by importing it in its corresponding function in lib/my_app.ex
.
def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
import MyApp.FriendlyRedirect
end
end
We then call the store_path_in_session/2
function as a plug in the :browser
pipeline of our router.
pipeline :browser do
...
plug(:store_url_for_login_retargeting)
end
Redirecting
Now that we are storing the url, we have to redirect to it when the signup is successful. Let’s add a public method to our previously defined module that gets the stored path or gives a default if no stored path is found.
defmodule MyApp.FriendlyRedirect do
...
def target_path(conn) do
target_path =
get_session(conn, :login_retargeting_path) ||
default_retargeting_path
end
defp default_retargeting_path do
"/"
end
end
Then on sign up/log in, we would redirect to that path inside the corresponding controller action:
redirect conn, to: FriendlyRedirect.target_path(conn)