How do I pass parameters with redirect

Hello,
as the title says, I’m struggling to cleanly redirect with parameters.
And by making cleanly italic, I mean to redirect not with /target?first=param, but with just /target

What I’m doing is, if a user tries to access a page, that is restricted to logged in users only, redirect it to login, and after the user logs back in, redirect them to the page they were sent out from.

The current way, I have that setup, is redirect with a GET variable, with the path of the restricted page. Then storing it in a hidden field, and redirecting there after login. If there’s no GET variable, it just redirects to the set default page.

So, basically, instead of having /login?to=/some_path, I want to have just login, but still have the ?to value somewhere hidden

What is the reason you don’t want to use the regular GET variables in the URL? Also can you explain what you mean by adding leading zeroes the index values?

As for how to do it, the first obvious way would be to render the login form directly instead of redirecting to the login page. You can capture the current path and pass it in the assigns map of the render function.

For example I have a plug that detects unauthorised users loading a page. If they don’t have permission then the following code runs:

def unauthorised(conn) do
    conn
    |> put_flash(:error, "You are not authorised to access that page")
    |> redirect(to: Routes.session_path(conn, :new, return_url: current_path(conn)))
    |> halt()
  end

This is probably similar to what you have now but instead of that redirect you can try:

def unauthorised(conn) do
    conn
    |> put_flash(:error, "You are not authorised to access that page")
    |> render("login.html", %{return_url: current_path(conn)})
    |> halt()
  end

You then have access to the return_url in the login form so you can POST it to the login controller then redirect to the return_url, all without it ever appearing in the URL.

1 Like

I have a similar plug. I save redirect_url in session, like this…

  def authenticate_user(conn, _opts) do
    if conn.assigns.current_user do
      conn
    else
      conn
      |> put_session(:redirect_url, conn.request_path)
      |> put_flash(:error, "You must be logged in to access that page")
      |> redirect(to: Routes.session_path(conn, :new))
      |> halt()
    end
  end

and in the session controller, create action, I use it to redirect after successful login.

  def create(conn, %{"session" => %{"name" => name, "password" => password}}) do
    case Auth.login_by_name_and_password(conn, name, password) do
      {:ok, conn} ->
        path = get_session(conn, :redirect_url) || Routes.page_path(conn, :index)

        conn
        |> put_session(:redirect_url, nil)
        |> put_flash(:info, gettext("Welcome back!"))
        |> redirect(to: path)

      {:error, _reason, conn} ->
        conn
        |> put_flash(:error, gettext("Invalid email/password combination"))
        |> render("new.html")
    end
  end
3 Likes

The session is a way better idea :+1:.

1 Like

Its a bad idea, I hate it…

If I have many tabs open, which will re-open automatically after a brosercrash, all of them will redirect to the same location after I log-in.

And to which of these locations is even prone to a race…

I personally wouldn’t use this method but if @AidasPa wants those clean URLs it’s a tradeoff.

A trade-off that is paid by the user without being able to choose.

2 Likes

Right. I’m sorry for a very late reply, that leading zeroes part was not even supposed to be there.
I apologize for a quick and dirty written question. I’m all for the session way. Thanks for suggesting it. I’ve marked it as a solution.
Thanks to @mainlymortal for a detailed response too.

I just like my URLs to be clean, and that redirection to the same place, if you have a few tabs open doesn’t bother me at all.

Your customers and users really don’t mind what you like, once they get into inconviniences…

Be prepared to loose your customers to this…