Skip to content
This repository has been archived by the owner on Mar 27, 2021. It is now read-only.

Support <Elements> within a shadow DOM #216

Closed
bcanseco opened this issue Jun 23, 2018 · 24 comments
Closed

Support <Elements> within a shadow DOM #216

bcanseco opened this issue Jun 23, 2018 · 24 comments

Comments

@bcanseco
Copy link

bcanseco commented Jun 23, 2018

This issue was originally reporting a bug when using custom fonts within a shadow DOM. I've changed the title to more accurately reflect the root cause - which is no shadow DOM support whatsoever.


Minimal reproduction (use Chrome)

Environment

Summary

When using stripe elements within a shadow root, passing external fonts to the <Elements> component will result in the iframe form getting an inline visibility: hidden style.

chrome_2018-06-23_14-24-42

Even if this style is overriden, creating tokens will later throw the following error:

We could not retrieve data from the specified Element.
Please make sure the Element you are attempting to use is still mounted.

Other information

The repro above may take a few moments to load. Notice that the fourth box will stay empty.
I'm aware that the fonts are not actually applied to the inner <CardElement> in the repro above.

@matt-stripe
Copy link
Contributor

@bcanseco Hi! Thanks for writing in. I'm Matt and I work at Stripe. You are totally right here--Elements is currently not compatible with shadow DOM. We have this on our backlog and are looking forward to addressing it in the future (no concrete timeline at the moment unfortunately). I will be sure to let you know when the fix does roll out!

@bennypowers
Copy link

Not just Chrome, shadow dom is supported in a number of browsers. For example, firefox support is about to land.

https://caniuse.com/#feat=shadowdomv1

@bennypowers
Copy link

FYI, the way we solve this in stripe-elements custom element is to walk the dom tree until we find the document.

@kaufmann42
Copy link

+1 we also really need this for our solution. As web pages become more plugin focused this is going to be important.

@bcanseco bcanseco changed the title Passing fonts to <Elements> breaks within a shadow DOM in Chrome Support <Elements> within a shadow DOM Aug 8, 2018
@redaben
Copy link

redaben commented Nov 11, 2018

+1

@bcanseco
Copy link
Author

For anyone considering workarounds like tokenizing cards using your own CC forms on the frontend and then capturing charges from your backend:

Keep in mind that you eventually need to validate PCI compliance. Yes, your backend never sees credit card information with this approach. HOWEVER, since you're not using the Elements library - you will have to fill in 40 pages of PCI forms every year.

image

More info on that here.

@bennypowers
Copy link

@bcanseco Thanks for that, that's more reason for stripe to support the full DOM and HTML spec, including shadow DOM. Anyone building a payment app that uses shadow DOM would LOVE to not have to fill out those forms, but unless Stripe decides to support the full DOM spec, we have to use weird workarounds like walking the whole tree.

@threeaccents
Copy link

@matt-stripe Are there any updates on the progress for shadow dom support?

@wayne-o
Copy link

wayne-o commented Apr 30, 2019

Would love to know if this has been fixed - also why is it closed if it's not?

@bennypowers
Copy link

It's worth mentioning that the workaround we use in <stripe-elements> breaks the tab order, as well.

@wayne-o
Copy link

wayne-o commented Apr 30, 2019

@bennypowers - does it also break the PCI compliance side of things or is that ok?

Also does it do the new PaymentIntent stuff?

[UPDATE]
https://stripe.com/docs/payments/payment-intents/quickstart
Ok so I can see that it doesn't... I will see if I can add this to your component but I am concerned that the PCI element is going to be a problem - could you clarify that for me please.

I wish Stripe would sort it out and do this properly within stripe!

@bennypowers
Copy link

bennypowers commented Apr 30, 2019

Won't break PCI, since even with my component, your users are still entering their numbers into an iframe to stripe.com.

but without proper tab order, it is inaccessible. You'll probably need some vile tabindex hacking, either that or very careful dom placement to get it to work the way you'd expect.

But i'd be happily proven wrong. all of ☝️ is based on my experience using <stripe-elements> inside an animated multi-step form.

<shameless-plug> I'd love to review a PR to add payment request and other stripe.js features to the element. Please see bennypowers/stripe-elements#13 </shameless-plug>

@atty-stripe
Copy link
Contributor

Hi folks, we don't have any updates regarding shadow DOM support yet. It should still be possible to use Elements without the shadow DOM, so hopefully you're not blocked from integrating Stripe altogether (or forced to fall back to higher levels of PCI).

We'll post here if/when we have updates on shadow DOM support.

@bathos
Copy link

bathos commented Mar 5, 2020

There are 8 (pretty minor, fairly repetitive) things in stripe.js which I know of that prevent it from working. There may be more which just happen not to occur in the features we’re using, but still, it’s not a super big diff. I have a patched version which does work, but it will be awkward trying to maintain keeping it in sync with the stripe-hosted script. Any news here?

@hofman-stripe
Copy link
Contributor

Shadow DOM causes some issues with the iframes that Stripe.js uses internally for PCI compliance. It may be possible to change Stripe.js to be compatible with Shadow DOM while preserving PCI compliance, but it's definitely more involved than 8 minor patches.

In the meantime, the only workaround is to keep all elements in the main document, doing something like @bennypowers's clever trick in stripe-elements.

@bathos
Copy link

bathos commented Mar 5, 2020

There’s nothing which could be considered PCI-related that I’ve encountered. The iframes are still secure (nothing can change that, really, they are controlled by stripe’s origin exclusively, and none of the Shadow DOM issues concern message passing). It’s only a handful of incorrect assumptions about three things:

  1. how to determine if an element is connected (use isConnected, not document.body.contains);
  2. how to determine if an element is focused (getRootNode().activeElement, not document.activeElement)
  3. how to choose the next/prev sequentially focusable element in the tab order on blur (the most complex thing, but this is a UX level issue)

Shadow DOM does not change how iframes behave.

@hofman-stripe
Copy link
Contributor

hofman-stripe commented Mar 5, 2020

Shadow DOM does not change how iframes behave.

It does. This is the main blocker: whatwg/html@7b56772
See also WICG/webcomponents#145

That means unless all iframes are in the root document, they cannot discover each other to communicate directly.

@bathos
Copy link

bathos commented Mar 5, 2020

Apologies, I framed that wrong. I should have said it does not change their security characteristics.

I’m a bit confused now though because the script creates all of these elements and can hold references to them, and is not obviously failing anywhere.

@hofman-stripe
Copy link
Contributor

While I'm not sure what your patches exactly do, you might end up creating the element iframes but they wouldn't be functioning properly. In particular I don't believe you'd be able to create a Token or PaymentMethod from a card element.

Also, a reminder that self hosting Stripe.js (especially a modified versions) is in itself not PCI compliant.

@bathos
Copy link

bathos commented Mar 5, 2020

Why is that exactly? I should hope that no script I run in my origin could compromise Stripe’s security — is that not the case?

(To be clear — I believe you of course, just looking to understand what’s going on better. This has all been a pretty frustrating experience so far.)

@danperkins
Copy link

@bathos I ran into this same problem as well. I put together a working solution where I have an <iframe> within the shadow DOM that handles loading the Stripe script and rendering react-stripe-element components. Not sure what your exact requirements are, but it seems to work reasonable well for us to get style encapsulation from the Shadow DOM and keep Stripe happy by having all of the content inside the iframe document (which does not have Shadow DOM nodes inside it)

@silenceisgolden
Copy link

silenceisgolden commented Oct 1, 2020

@hofman-stripe @matt-stripe Would it be possible (at least in the createToken(Element) use case) to patch the library and the loader to maintain that state? So when a Stripe Elements iframe is loaded in the doc it could be added to a collection of iframes that is bootstrapped by the Stripe.js loader?

My thought is that this collection would replace window.frames, assuming that is what is being used by the Element iframes. This new collection could also, by necessity, be added to the window as well.

@hofman-stripe
Copy link
Contributor

For security we cannot rely on the Stripe.js script loaded in your page to establish a communication channel between our iframes. They have to discover each other directly. Currently this is done through naming and window.frames, a special property of WindowProxy which cannot be stubbed in any way.

There are other ways to get references to other frames's WindowProxy objects, but these would require a fundamental refactor, not a simple patch.

Interesting read on the topic: https://medium.com/@bluepnume/every-known-way-to-get-references-to-windows-in-javascript-223778bede2d

@jonahbron
Copy link

👍 for supporting shadow dom. I got this working by using a slot to pull in a container from outside the shadow dom, but that was easy to do because my component sits directly inside the root DOM.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests