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?

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.