Reputation: 47
I was wondering how I could implement having two pages in the same root "/" using Phoenix? One for unauthenticated users, and one for authenticated users. Examples of use cases where this happens are LinkedIn and Facebook, where a login-page is served on "/" and an application on "/" after logging in.
I use Guardian for authentication, and have my router set up as:
pipeline :auth do
plug Guardian.Plug.EnsureAuthenticated, handler: App.AuthHandler
end
pipeline :unauth do
plug Guardian.Plug.EnsureNotAuthenticated, handler: App.AuthHandler
end
scope "/", App.Web do
pipe_through [:browser, :unauth]
get "/", FrontController, :index
end
scope "/", App.Web do
pipe_through [:browser, :auth]
get "/page", ApplicationController, :index
end
Where FrontController serves the pages accessible by unauthenticated users (e.g. a login-page), and ApplicationController serves the actual application.
When ApplicationController serves "/" instead of "/page", a "this clause cannot match because a previous clause at line 1 always matches" error is thrown.
I imagine using if-statements and one controller for serving both pages, unfortunately I couldn't find documentation on how to implement such a strategy.
Upvotes: 1
Views: 532
Reputation: 6571
This isn't what you asked, but in my humble opinion a better way to handle this is to use two different namespaces, "/"
for unauthenticated users and "/app"
(or something) for authenticated users. This makes it easier for you and your users to distinguish between the two, and reduces system complexity. Then if an unauthenticated users tries to go to an "/app"
route you can return a 401 Unauthorized
response (from inside your App.AuthHandler
along with a proper error message, such as "You need to be logged in to do that."
BUT if you insist on doing what you wrote, then I would just handle that scenario simply in ApplicationController.index
- no need for a FrontController
. That index action might look something like this:
defmodule MyApp.Web.ApplicationController do
def index(conn, _) do
case logged_in?(conn) do
true ->
user = Guardian.Plug.current_resource(conn)
render conn, "logged_in_page.html", user: user
false ->
render conn, "front_page.html"
end
end
# Note: This could be in a helper module
defp logged_in?(conn) do
Guardian.Plug.authenticated?(conn)
end
end
This way, the user is served a different page when they go to "/"
depending on whether they have a valid JWT.
Again though this approach makes your system more complicated. And generally speaking, you should avoid complexity unless you have a really good reason. Follow conventions whenever possible and you'll be grateful later. :)
Upvotes: 3
Reputation: 13106
Most likely, what you want to do is handle this at the controller & view render layer, not in the router.
Inside your "root" or home controller (whatever one serves up "/" as the handler) you can check in your action if you have a valid auth'd user or not, and render the view you want based on that.
Upvotes: 0