Phoenix LiveView in an iframe

:wave:

The idea is to embed a LiveView page in an iframe on a website with different origin.


Has anyone had success running Phoenix LiveView in iframe from different domain? I’m getting LiveView session was misconfigured or the user token is outdated. warning and 403 error response on web socket connect (the server-rendered html gets returned fine) even though csrf_meta_tag/0 is set, most likely since Connect Info: %{session: nil} where session is not a map. The “iframed” page’s headers are:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-XSS-Protection: 1; mode=block
Cache-Control: max-age=0, no-cache, no-store, must-revalidate, post-check=0, pre-check=0
Date: Tue, 24 Mar 2020 18:19:09 GMT
Content-Length: 2905
X-Content-Type-Options: nosniff
Vary: x-requested-with
Server: Cowboy
x-download-options: noopen
x-permitted-cross-domain-policies: none
cross-origin-window-policy: deny
x-request-id: Ff9QXBtfVX-isuEAAOMB

I’m dropping x-frame-options: sameorigin header before sending the response, I’ll go read about the other headers, will also try removing them.

2 Likes

Removed all security headers, the iframed page responds with

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Date: Tue, 24 Mar 2020 18:24:11 GMT
Content-Length: 2905
Cache-Control: max-age=0, no-cache, no-store, must-revalidate, post-check=0, pre-check=0
Vary: x-requested-with
Server: Cowboy
x-request-id: Ff9QojhbQpcCa6kAAQDh

The 403 error persists.

Ok, I removed the connect_info options, and now it seems to work

  socket "/live", Phoenix.LiveView.Socket
-   websocket: [
-     connect_info: [session: @session_options]
-   ]

It’s probably ok for this page, since it doesn’t do any auth.

1 Like

Thanks - I was also looking to put a Liveview in an iFrame and your solution helped. Do you know what the security implications of doing this are? I use Liveview across the site, but for the iframe content have created a new, separate socket without the session info.

Try to remove :put_secure_browser_headers from the :browser pipeline in router.ex. This seems to work for me.

For best security practice I think one must look into modifying the response headers set (by put_secure_browser_headers and others) to make them as restrictive as possible but still allow iframe from other sources. I think Content-Security-Policy header looks like a good place to start.

1 Like

This trick does work for me, but with the price that i have no session info. Has anyone succeeded with LiveView in an IFrame with session available?

For me, adding same_site: "None" to @session_options did the trick.

A few notes:

  • this only works when the site is accessed through ssl. You can read more about this here. Summarized: third party cookies need SameSite=None; Secure
  • this will not work in Safari, as they have an option Prevent cross-site tracking, which I believe is enabled by default
6 Likes

Per @dvic’s advice, we added the following:

  @session_options [
    store: :cookie,
    key: "_app_web_key",
    signing_salt: "ARodNYY",
    same_site: "None",
    secure: true
  ]

et voila!

3 Likes

It should only be necessary to clear the x-frame-options header while keeping the rest of the secure headers. Which you can do by adding a plug after secure headers implemented like so:

defmodule MyApp.Plugs.ClearXFrameOptions do
  import Plug.Conn

  def init(default), do: default

  def call(conn, _opts) do
    delete_resp_header(conn, "x-frame-options")
  end
end