Migrating from API only to accept HTML - setting the secret_key_base on connections


I’m in the process of converting an API only application to accept browser requests, with cookies and sessions.

My router currently looks like this:

  pipeline :browser do

  scope "/admin" do

    resources("/login", LoginController, only: [:show], singleton: true)

Visiting this URL errors out with

** (ArgumentError) cookie store expects conn.secret_key_base to be at least 64 bytes
        (plug 1.14.0) lib/plug/session/cookie.ex:185: Plug.Session.COOKIE.validate_secret_key_base/1`

After googling, this gets set in the config, and as such my dev config is:

secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 70)

config :myapp_web, MyApp.Endpoint,
  http: [port: 9000],
  debug_errors: true,
  code_reloader: true,
  check_origin: false,
  live_view: [signing_salt: secret],
  secret_key_base: secret,
  watchers: []

When I call Application.get_env(:myapp_web, MyApp.Endpoint), I can see all the above config is correctly being set, and when I call byte_size on the resulting string it is of course 70 bytes.

And yet I’m still seeing

** (ArgumentError) cookie store expects conn.secret_key_base to be at least 64 bytes

Do I need to manually set this? If so would I make a plug and just assign it to the conn on every request? I can see documentation around assigning the value in the config, but nothing about having to manually assign, although that could be a result of this originally being an API only app and missing some config somewhere else.

Any help for how to set the secret_key_base on connections is appreciated

    * `:secret_key_base` - the secret key base to built the cookie
      signing/encryption on top of. If one is given on initialization,
      the cookie store can precompute all relevant values at compilation
      time. Otherwise, the value is taken from `conn.secret_key_base`
      and cached.

Do you maybe provide one at compile time?

Thanks for the pointer - the same file you got that comment from says:

Since this store uses crypto features, it requires you to
set the :secret_key_base field in your connection. This
can be easily achieved with a plug:
plug :put_secret_key_base
def put_secret_key_base(conn, _) do
put_in conn.secret_key_base, “-- LONG STRING WITH AT LEAST 64 BYTES --”