andrewb
Modify Conn.host to include a subdomain
I have a multi-tenant application where my Users can access more than one tenant. I am authenticating the User at the top level URL (example.com) and then adding the tenants they have access to using a plug. Initially, I am taking the first tenants subdomain and storing that in the conn conn.assigns.subdomain. This all happens in the :browser pipeline in the router.
I am looking for a way I can add the required subdomain to the host so that when I route them to the Dashboard the URL is:
I am using Phoenix 1.4 Dev and used the put_router_url (https://bit.ly/2IxXMDX) function:
def index(conn, _params) do
url = %URI{host: conn.assigns.subdomain <> "." <> conn.host}
conn
|> put_router_url(url)
|> render(to: Routes.dashboard_path(conn, :index))
end
Looking at the source I thought this would then ensure that the generated path would include the subdomain.
Looking for some guidance on the best way to do this, any thoughts appreciated.
Andrew
Marked As Solved
andrewb
Chris,
Thank you very much for your time but it was a user error!
I figured it must be to do with the session cookie and sharing it across the main domain and subdomain and after a lot of reading, I ran across an article that mentioned that browsers do not allow cross-domain session sharing for .com and localhost!
One of the many things I have forgotten over the years. Anyway … changed my host file and the config to use local.host and it all worked as planned. The plus side is I have a much better understanding of the conn lifecycle and how Phoenix handles routing.
Again thanks for your time. I love Phoenix and can’t wait to see what happens with LiveView.
All the best.
Andrew
Also Liked
Gazler
I think you are looking for the _url helpers instead of the _path helpers. _path will be a relative path whereas _url will be the full URL.
def index(conn, _params) do
#...
|> render(to: Routes.dashboard_url(conn, :index))
end
chrismccord
Note that your code uses the non-put-router-url’d connection when call dashboard_url. So you need to rebind it, otherwise it is ignoring the previous plug calls:
def index(conn, _params) do
url = %URI{host: conn.assigns.subdomain <> "." <> conn.host}
conn = put_router_url(conn)
redirect(conn, to: Routes.dashboard_url(conn, :index))
end
andrewb
Thanks, Chris. Changing that has me a bit further. I am sure you knew I would run into problems once I started redirecting. I hope you might be able to help a little bit more.
As I mentioned above I am trying to add a subdomain after a user logs in (As they may be able to access multiple subdomains). By default I am directing them to the first subdomain.
I have a Plug.check_session to see if there is a user_id in the session cookie. If there is an there is no subdomain on the host I add in default subdomain and redirect to the new url.
If there is a subdomain I pass it through adding the user as the current_user.
defmodule BookingCentralWeb.Plugs.CheckSession do
import Plug.Conn
alias BookingCentral.Accounts
def init(opts), do: opts
def call(conn, _opts) do
user_id = get_session(conn, :user_id)
case user_id && Accounts.get_user_by_id(user_id) do
nil ->
assign(conn, :current_user, nil)
user ->
orgs = Enum.map(user.organisations, fn (x) -> x.subdomain end)
case get_subdomain(conn.host) do
"" ->
subdomain = get_first_subdomain(orgs)
url = %URI{host: subdomain <> "." <> conn.host, port: 4000}
conn = Phoenix.Controller.put_router_url(conn, url)
conn = assign(conn, :current_user, user)
Phoenix.Controller.redirect(conn, external: BookingCentralWeb.Router.Helpers.dashboard_url(conn, :index))
_ ->
assign(conn, :current_user, user)
end
end
end
defp get_subdomain(host) do
root_host = BookingCentralWeb.Endpoint.config(:url)[:host]
String.replace(host, ~r/.?#{root_host}/, "")
end
defp get_first_subdomain([head | _tail]) do
head
end
I changed my index action on the Dashboard Controller on the basis that the host will include the subdomain by this stage:
def index(conn, _params) do
render(conn, "index.html")
end
Running this when I authenticate it adds the subdomain and redirects but then I end up at the login page, presumably because I get a new session for eth subdomain. I modified the config in my endpoint.ex:
plug Plug.Session,
store: :cookie,
key: "_booking_central_key",
signing_salt: "H4nzWhe9",
allow_hosts: [".localhost"],
domain: ".localhost"
But now I get a CSRF error:
invalid CSRF (Cross Site Request Forgery) token, make sure all requests include a valid '_csrf_token' param or 'x-csrf-token' header
Any direction or examples will be greatly appreciated.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








