Keeping custom state across the Überauth OAuth flow in Elixir
Überauth now uses the OAuth state parameter for CSRF protection, so custom state gets overwritten. Keep it in the session between request and callback instead.
Überauth is probably the go-to OAuth login library for Elixir projects.
The team behind the library recently improved its protection against CSRF attacks. Unfortunately, the improvement comes at a cost for developers: it is no longer possible to keep custom state between the request and callback phases of the OAuth flow using the state parameter.
Background
Most OAuth providers (Google, for example) let developers pass custom state in a state query parameter as part of the request URL. The provider then passes that state back when it calls our callback endpoint.
Überauth now uses the state query parameter to carry its CSRF token, overwriting whatever custom state developers put in the request URL.
Workaround
Luckily, there is another way. We can put our custom state in the session cookie during the request phase and read it back from the session in the callback phase.
Code example
The following snippet shows how to save some custom state in the session and retrieve it in the callback phase of the OAuth flow:
defmodule MyAppWeb.AuthController do
use MyAppWeb, :controller
plug(Ueberauth, providers: [:google_custom])
@provider_config {Ueberauth.Strategy.Google, [default_scope: "email profile"]}
def request(conn, %{"provider" => "google", "custom_state" => custom_state}) do
# Store custom state in the session
conn
|> put_session(:auth_custom_state, custom_state)
|> Ueberauth.run_request("google", @provider_config)
end
def callback(conn, _params) do
%{assigns: %{ueberauth_auth: auth}} =
conn
|> Ueberauth.run_callback("google", @provider_config)
# Get custom state back from the session
auth_custom_state = get_session(conn, :auth_custom_state)
IO.inspect(auth_custom_state, label: "Auth custom state")
end
end
With that code, I can start the OAuth flow passing some custom state in the URL (e.g. https://localhost:4000/auth/google?custom_state=some_values_here) and then get it back from the session in the callback function.