Integrate Internet Identity within iFrame

I’m trying to put Internet Identity within an iframe to integrate it more seemless within our application. I’m facing issues on safari and getting the error:

Refused to display ' https://identity.ic0.app/#authorize ' in a frame because it set 'X-Frame-Options' to 'DENY'.

It works on chrome. From my perspective and understanding this should be possible. But from a security point of view I’m not sure if the protection is justified.

Is there something I’ve overseen? Any help or hints very much appreciated.

1 Like

The spec at https://github.com/dfinity/internet-identity/blob/main/docs/internet-identity-spec.adoc#client-authentication-protocol explicitly says

It loads the url https://identity.ic0.app/#authorize in an <iframe> or separate tab.

So this was certainly intended at some point. I am not sure who is setting the X-Frame-Option here. I think it should be fine to allow that, however. @bjoern, WDYT?

1 Like

The X-Frame-Options header is present on the query calls:

Hmm, that’s the header on the query as done by the service worker. I would expect that this does not apply to the resource as loaded via the service worker. But I am just optimistically guessing.

I assume this header is set by the boundary nodes (if only we had the source…), and not by the canister itself or the service worker. Maybe a Web technology expert can advise us about the interplay of that header and service workers.

Ah, when you open an URL like https://identity.ic0.app/ without the service worker installed, the boundary node’s response (the one that installs the service worker) sets x-frame-options: DENY.

So yes, the boundary node need to change to allow the Internet Identity (or any other canister-hosted frontend!) to be embedded as an iframe.

2 Likes

Restricting use on iframes sometimes makes sense from a security perspective (cf. clickjacking). That said, I do not sufficiently understand the interplay of service workers and X-Frame-Options, that does not seem to be specified in general and may be the reason for the different observations in Safari and Chrome.

@nomeata Thanks for the information, it was very helpful to me!

But same case here
I am trying to use iframe to display one asset-canister inside another and deny header breaks my case. This is a rather critical moment for me(
Are there any ways to change the header x-frame-options: DENY to SAMEORIGIN in custom cases?
Maybe the dfinity team has any plans to improve this?
I tried to change the headers in certified_assets canister (lol) and install code to my canisters, but it didn’t help me (lol_twice).
@kpeacock please help :slight_smile:

Hi 3cL1p5e7,

We realize that it is inconvenient that currently iFrames do not work on the Internet Computer. But adding proper support for iFrames (without introducing any security risks) is a bit of work. It is definitely on our roadmap, but I cannot give promises regarding release dates.

For the time being, we have removed iFrames from the Internet Identity specification to avoid developer confusion.
I hope this helps.

Best regards,
Frederik

3 Likes

Thank you for you reply, Frederik!

Hi @frederikrothenberger any progress on this? This feature would be much needed - to display canister content (like a PDF file) properly within iFrame.

Where can I find this “Internet Identity specification” to see that certain HTML tags like iframe is NOT supported by ICP at the moment?

I’m not entirely sure about that, Herbet. You could give it a try, but I believe you can have iframes on your website and even embed another website hosted on the IC as an iframe. Disabling for example the header X-Frame-Options in the certified asset canister might work with the current Certification v1. If Certification v2 is required, it should be possible in a matter of weeks.

However, what does NOT work is sharing an identity between a main window and an iframe since they are separate contexts.

1 Like

As far as I remember with assets v1 that header was overridden by the boundary node. But you could load in a popup first to init the worker from the boundary node and then later load it in an iframe (which uses the worker).

I’ve used this workaround in combination with postmessage, if my iframe embed didn’t load and send a postmessage after a specific timeout, I showed a temporary popup before retrying the iframe.

As for identities including II, you can always delegate an identity with a delegation chain to another web frame/window.

Though I wouldn’t recommend using iframes to display UI in other apps since the parent window can draw completely different UI on top of it and make users think they’re doing X while they’re doing Y within the iframe.

1 Like

Thanks for the add-ons @sea-snake. In that case, let’s ping @NathanosDev. Is the header X-Frame-Options still overridden by the BN, is that correct? Will it be possible to override it with the certification v2?

Note that I absolutely advocate against iFrames; when it comes to me, I’m inclined to think that they should be deprecated from the web specification, but well, they still exist.

1 Like

There’s two sets of headers that we need to consider here. One is the set of headers present on the “outer” HTTP response that comes from the boundary node. Second is the set of headers present on the “inner” candid response that comes from the canister.

When an HTTP response from the IC network is received by the browser, the browser will see the
“outer” headers set by the boundary nodes. Then the service worker will extract the “inner” headers from the candid response and forward that to the application code and then the browser will see this second set of headers as if it is a second, separate request.

This behavior is independent of the version of asset certification that’s used by the canister. The only way to change this will be to remove the headers that are being sent by the boundary nodes. I believe there are big security concerns with doing this, so I’m not sure how plausible that scenario is. Alternatively if anyone needs/wants to return a different set of headers for their own special use case, a better approach may be to run their own HTTP boundary nodes and take on the security risks themselves.

1 Like

Would the following be a security concern?

Special url that allows iframe embed that initializes the worker and sends a post message to parent window, it renders nothing.

If such an approach would be possible without affecting security, it would make it possible to embed iframes in a two step process. Right now the two step process as mentioned above involves showing a temporary popup to initialize the service worker before loading the iframe.

As for working around it now, running your own server that serves the service worker, should indeed allow you to set any headers you want.

Thanks for the explanation Nathan and agree on the security concern. Just to summarize and for my understanding, the X-Frame-Options is indeed set on the “outer” Http headers, do I get it right?

Special url that allows iframe embed that initializes the worker and sends a post message to parent window, it renders nothing.

I’m not a security expert, but a potential issue that I see with this:
If there is sensitive information included in the post message then a malicious party could wrap your special URL in their own page that looks exactly the same as yours and then steal the information.

What problem are you trying to solve by embedded a page in an iframe?

the X-Frame-Options is indeed set on the “outer” Http headers

Yes!

There may be a way that we can allow for canisters to decide themselves if they want these headers included or not using certification v2. It would require the boundary nodes to decode responses from the canister, read the inner headers, validate the certification and then copy the inner headers onto the outer HTTP request. I’ll add an investigation into this onto our backlog.

1 Like

Previously the wallet implementation I wrote used a postmessage to/from an iframe to communicate with the wallet which was running within the iframe.

This sounds insecure, but different origins will block any access to the iframe (only post message allowed) similar to a popup window or new tab. Only issue was clickjacking, this was resolved by not rendering UI for the url that allowed to be loaded within an iframe. But purposefully rendering content like wallets or II within another site with iframes is indeed not secure due to this.

I do remember there was a web spec in the works a while back for secure embedding auth flows in dialogs but haven’t heard about it since. As for insecure content like e.g. interactive NFTs, I can imagine embedding would be very useful.

Recently I’ve migrated to using delegated identities with a canister scope instead, since this iframe approach wasn’t going to work well with e.g. a mobile app that interacts with the wallet :sweat_smile:

Ooo, I’ve been meaning to do an example app of how to do basic delegation with canister scopes. Do you have any work on this you care to share?

Planning to release the source of the wallet I’ve been working on soon which is built with delegations for it’s signing and encryption. Still waiting on VetKeys to make the encryption secure.

Also I’m using delegations for connecting to dapps, and here I plan to actually use canister scopes. One more tricky thing where I’m still stuck with here is forwarding calls to external apps, I need to reliably get the origin. Maybe I can use the refferer in that case.