Add session :domain option conditionally according to environment

I have the following configuration

@session_options [
  store: :cookie,
  key: "_foo_key",
  signing_salt: "6B/1Dvoc",
  same_site: "Lax",
  domain: ".foo.app"
]
plug Plug.Session, @session_options

The :domain option allows me to share the session cookie accross multiple subdomains of my online site (project1.foo.app, project2.foo.app, etc.).

The problem is that this breaks development/localhost (403 Forbidden responses). Without the :domain option, the site on localhost is running fine. But I need the setting for prod.

What would be the best way to add the :domain option conditionally, according to the current environment prod vs dev?

2 Likes

You can wrap the default session plug with your own plug that sets the domain at runtime.

defmodule MyAppWeb.Plug.Session do
  @behaviour Plug

  @impl Plug
  def init(opts), do: opts

  @impl Plug
  def call(conn, opts) do
    opts = Keyword.put(opts, :domain, domain())
    Plug.Session.call(conn, Plug.Session.init(opts))
  end

  defp domain, do: Application.get_env(:my_app, :cookie_domain)
end

Thank you Malian. And what do you think about using Application.compile_env/2 to conditionally add :domain without a plug but in that endpoint file? Would it also work?

session_options =
  if Application.compile_env(:my_app, :env) == :prod,
    do: session_options ++ [domain: ".foo.app"],
    else: session_options

It should work, but most of the time, I am against magic value in my code, and I consider the domain name a magic value that may depend on the environment.

I also try to have my local environment as close as possible to the production env. It’s pretty easy to do with Elixir and tools like asdf.

Considering these points, when I have subdomain, I am not using localhost as it is not a valid domain name for cookies (see https://www.rfc-editor.org/rfc/rfc2965 for more info). Instead, I use localtest.me or equivalent that resolves to localhost. I can now use project1.localtest.me and mimic what happens in prod without having to configure my hosts file.

With the above proposition, I have the following config:

# dev.exs
config :my_app, :cookie_domain, "localtest.me"

# runtime.exs
host =
  System.get_env("PHX_HOST") ||
    raise """
    environment variable PHX_HOST is missing.
    """

config :my_app, :cookie_domain, host

where PHX_HOST is set by my provider depending on the env (prod, uat, stagging, pr, …).

Last time I checked, I wasn’t able to set the domain key with an mfa tuple, so I came up with this solution. @josevalim do you think this could be a nice addition to Plug or is this the way to go. I would be happy to open a pr.

1 Like