The @session_options module attribute used Application.compile_env, which baked compile-time placeholders into the endpoint while runtime.exs set the real values from SESSION_SIGNING_SALT / SESSION_ENCRYPTION_SALT env vars. Phoenix detected the mismatch and refused to start (failing migrate Job in k8s). - Remove hardcoded salts from config/config.exs (no compile-time binding) - Add stable per-env salts in dev.exs / test.exs so local + CI don't need the env vars - Split static cookie opts (@static_session_options) from runtime-resolved opts in endpoint.ex; expose session_options/0 as an MFA tuple in socket connect_info so LiveView decodes sessions with the same runtime salts - New ToweropsWeb.Plugs.RuntimeSession wraps Plug.Session, fetches salts from app env on first request, and caches the initialized opts in :persistent_term (zero per-request overhead after warm-up)
33 lines
992 B
Elixir
33 lines
992 B
Elixir
defmodule ToweropsWeb.Plugs.RuntimeSession do
|
|
@moduledoc """
|
|
Wrapper around `Plug.Session` that resolves session salts at runtime.
|
|
|
|
The signing and encryption salts come from environment variables
|
|
(`SESSION_SIGNING_SALT` / `SESSION_ENCRYPTION_SALT`) via `config/runtime.exs`.
|
|
Because `Plug.Session` is compiled into the endpoint pipeline with its
|
|
options baked in, this plug defers `Plug.Session.init/1` until the first
|
|
request and caches the result in `:persistent_term`.
|
|
"""
|
|
|
|
@behaviour Plug
|
|
|
|
@impl true
|
|
def init(static_opts), do: static_opts
|
|
|
|
@impl true
|
|
def call(conn, static_opts) do
|
|
Plug.Session.call(conn, session_opts(static_opts))
|
|
end
|
|
|
|
defp session_opts(static_opts) do
|
|
case :persistent_term.get({__MODULE__, :opts}, nil) do
|
|
nil ->
|
|
opts = Plug.Session.init(static_opts ++ ToweropsWeb.Endpoint.runtime_session_opts())
|
|
:persistent_term.put({__MODULE__, :opts}, opts)
|
|
opts
|
|
|
|
opts ->
|
|
opts
|
|
end
|
|
end
|
|
end
|