ueberauth / ueberauth_apple

Ueberauth Strategy for Sign In with Apple
MIT License
12 stars 6 forks source link

Favour redirection over `Same-Site: None` #3

Open tomtaylor opened 6 days ago

tomtaylor commented 6 days ago

Problem Statement

The Apple OAuth implementation is a bit odd, in that it performs a cross site form POST request to the callback URL, not a GET redirection. This means that any cookies with anything other than Same-Site: None are not sent on the callback request. Currently the library encourages users to downgrade the security of those cookies to Same-Site: None, but I believe there's a better solution.

Solution Brainstorm

Instead of downgrading the security, ueberauth_apple could implement a POST handler action, which takes the form params and converts them to a GET request to the callback with query params. The GET request will include the cookies with Same-Site: Lax/Strict, so it will behave in the same way to any other Ueberauth callback. I've implemented something similar manually, and it works well, but I think it could be included in the library for a smoother path.

aj-foster commented 5 days ago

Hello there. I'm interested in this proposal. Is there any chance we could see the implementation you have currently? That would help me ensure I understand the flow correctly.

tomtaylor commented 5 days ago

Hey @aj-foster, sure thing.

In my our controller which implements the Ueberauth callback, we also have the following action:

  @doc """
  The Apple provider hits our callback URL as a cross site `POST` request.
  Because we implement `Same-Site: Lax` on our cookies, this means the request
  lands without any cookies. To work around this, we redirect the user to the
  normal `GET` callback URL, using the action below, which passes the original
  form parameters as query params. Following the redirect, the browser sends the
  usual cookies with this request, and the callback can proceed as normal.
  """
  def post_redirector(%Plug.Conn{} = conn, %{"provider" => provider}) do
    conn
    |> redirect(to: ~p"/en-gb/auth/#{provider}/callback?#{conn.body_params}")
  end

The provider is defined like so:

    apple:
      {Ueberauth.Strategy.Apple,
       [
         default_scope: "name email",
         request_path: "/en-gb/auth/apple",
         callback_path: "/en-gb/auth/apple/callback",
         callback_methods: ["POST", "GET"]
       ]}

And then our router is defined similar to:

  scope "/en-gb", PoplarWeb do
    pipe_through :minimal_browser

    post "/auth/:provider/callback", OauthController, :post_redirector
  end

Does that give you enough to go on?