X-Frame-Options for StreamResource

I am working on displaying a PDF in an IFRAME.

Unfortunately, I am very limited by X-Frame-Options and cannot display the PDF within the iframe.

I have tried to overwrite the HTTP header, but something overwrites it again afterwards.

Code:

                    // Create new StreamResource from File.
                    StreamResource resourceIO = new StreamResource("parts_list_" + partsList.getId() + ".pdf", () -> {
                        try {
                            return new ByteArrayInputStream(FileUtils.readFileToByteArray(fileIO));
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    });

                    // Set Mime Type.
                    resourceIO.setHeader("X-Frame-Options", "SAMEORIGIN");
                    resourceIO.setCacheTime(TimeUnit.MINUTES.toMillis(1));
                    resourceIO.setContentType("application/pdf");

                    // Register StreamResource as Resource of Session.
                    getUI().ifPresent(ui -> {
                        StreamRegistration registrationIO = ui.getSession().getResourceRegistry().registerResource(resourceIO);

                        // Set Registration as Source of IFrame.
                        iframeIO.setSrc(registrationIO.getResourceUri().toString());
                        iframeIO.setSizeFull();
                        iframeIO.setSandbox(IFrame.SandboxType.ALLOW_SAME_ORIGIN);

                        // Print Debug Message.
                        log.info("Streaming PDF to Client Session.");

                        // Show Invoice Dialog.
                        dialogs.createMessageDialog().withContent(iframeIO).open();
                    });

HTTP Response:

HTTP/1.1 200
Cache-Control: max-age=60
Expires: Sat, 02 Dec 2023 14:03:37 GMT
Pragma: cache
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Content-Type: application/pdf
Content-Length: 1055
Date: Sat, 02 Dec 2023 14:02:37 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Can you try this?

I have already tried it, the thing is that the PDF is accessible, but if I put it in an IFrame, this frame is blocked by the browser.

I have already set frame-ancestors and X-Frame-Options, but without success.

IFrame get’s blocked:
image

Loading IFrame URL in new Tab:
image

HTTP/1.1 200
Cache-Control: max-age=60
Expires: Sat, 02 Dec 2023 17:12:30 GMT
Pragma: cache
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Content-Security-Policy: frame-ancestors 'self' localhost
Content-Type: application/pdf
Content-Length: 1055
Date: Sat, 02 Dec 2023 17:11:29 GMT
Keep-Alive: timeout=60
Connection: keep-alive

Sharing this from the paid support… does it solve your problem?
Dear Eduardo!

I did some more research on your issue. I have prepared a more detailed example of a Jmix Application in an iframe.

Preparation

To begin with, I decided to reproduce your network environment.
I ran the Apache web server locally and then put html with a nested iframe into it.

The Jmix app was displayed correctly.

Then I decided to run not from the localhost, but from another named host. (e.g. paramo.com).

I will describe my host environment.
Apache web server with mapped address 127.0.0.2 paramo.com
I did not make any additional settings for the server.

In this case, I ran into the problem that cookies are disabled.

Cookies issue

This is a known problem, I am attaching a link from Vaadin GitHub: Cookie issue when embed vaadin application in iframe. · Issue #7736 · vaadin/flow · GitHub.

There is no solution to this problem, however, you can see some thoughts from an enthusiast who has found a workaround: The Dreaded “Vaadin Session has Expired”/“Cookies Disabled” – Martin Vysny – First Principles Thinking

Quick Fix will look like this:

You should configure the server which serves the application inside the iframe so that it sets SameSite=None for the session cookie.

According to the SameSite Cookie Changes in February 2020
article, setting SameSite=None only works with https (and not with http) since the “Secure” attribute is required with SameSite=None .

Thus, you need to configure the embedded tomcat application server to work with https and set specific the property.

How to Fix:

Now I will give a sequence of steps for configuring the Jmix app.

Step 1:

Allow to open the application in the iframe (we already did this step on the call).
Note that I’m specifying the hostname where the iframe will open: paramo.com.

Code snippet:

Copy

    @EnableWebSecurity
    public static class DefaultFlowuiSecurityConfiguration extends FlowuiSecurityConfiguration {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);

            http.headers(headers ->
                    headers.contentSecurityPolicy(secPolicy ->
                            secPolicy.policyDirectives("frame-ancestors paramo.com")
                    )
            );
        }
    }

Step 2:

Add a property that sets SameSite=None.

Code snippet from application.properties:

Copy

server.servlet.session.cookie.same-site=none

Step 3:

According to the SameSite Cookie Changes in February 2020 property SameSite=None only works with https.

Please note that host server and Jmix application must be accessible via https at the same time.

Therefore, it is required to enable https for the Jmix app.
Here is a link to a guide on how to do it: Enable https in Spring Boot | Java Development Journal

Thus, a keystore is added to the Jmix application, where the Self Signed SSL Certificate is stored.

Now we need to add the follow properties from SSL Certificate to the application.properties:

Copy

server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore/paramo
server.ssl.key-store-password=paramo
server.ssl.key-alias=paramo
server.port=8443

As you can see, this is the path to the keystore.
In the example app, this path is: src\main\resources\keystore\paramo

Thus, it is now possible to open the application via https:
image

I am attaching the test project: testIFrame.zip (437.9 KB)
And test html: test.html (211 Bytes)

As I said earlier, no special settings for the server are required. All I did was mapping 127.0.0.2 to paramo.com.

In addition

I also tried to reproduce the problems we encountered during the call.

I could not reproduce the problem with loading a java-script on a page, just like the problem with redirecting to localhost.
I have some thoughts on this:
I’m assuming that the problems may be due to additional settings on your test servers.

So…

  1. Try to create a pure Apache web server, put an html file with an embedded iframe into it.
  2. Set up a Jmix app according to the given example (or use my test project).
  3. Do not forget that access is made only via https. Both for the host server and for the Jmix application.

After these steps, you will be able to launch the application in the iframe, I assume.
If you have any problems, we can help you with this topic or schedule an additional call. I really hope that my research will help you, or at least bring you closer to the solution.

Best regards,
Dmitriy

P.S. Please let me know if you have any questions regarding the implementation.

2 Likes

Thank you for your great work :slight_smile:

In short, I was able to fix the error by removing iframeIO.setSandbox(IFrame.SandboxType.ALLOW_SAME_ORIGIN);.

Also, as you mentioned, the domain in the frame-anchestor is without http://.