-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG]: 3D Secure Authentication behind top layer elements #599
Comments
Can you share a basic reproduction of this to illustrate? What would you expect to happen vs what you observe? |
Here's a codesandbox: https://codesandbox.io/p/devbox/goofy-mountain-wwzgsc?file=%2Fapp%2F_lib%2Fmain.tsx%3A37%2C16 The card number in the dialog will trigger 3d secure. I'd expect the 3d secure modal to appear over the form modal, but it's behind, because top layer is above any z-index. |
Thanks for sharing that repro -- we're investigating this. |
Also a big issue for us! Our checkout flow is in a native |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Some further activity |
Hitting this issue as well. I have a checkout in a dialog: <dialog><StripePaymentElement/><dialog> When using .showModal() on the dialog, the browser add its to the "Top Layer", which cannot be overwritten with z-index. Because of this, the 3d secure popup is under my dialog. Paypal has the same issue with their embedded login. The fix is for stripe to move their modals to a modally shown element. Right now for paypal I am closing my I've put my modal at 20% opacity here to show the issue: |
@tinleym @benkingcode - did you find any workaround to this? I'm trying to find a good way to detect if the 3DS modal is open so I can close mine. Doesn't look like Stripe offers an even for this, and detecting the iframe being inejcted seems tricky as there are no classes or src that I can grab onto.. |
@wesbos I'm setting the dialog display to block, and then on submit I call dialog.close (it stays visible because of display: block, but is removed from the top layer), I then await my stripe calls, and then I call dialog.showModal to get the dialog back to its presubmission state. In my case, I want the dialog visible and inert during a submission, whether 3DS is called or not, so that's as far as I got. |
ohhhh interesting trick - display block on the closed dialog. thank you - might help me out |
@brendanm-stripe any update? |
This is going to become more and more of an issue as more and more people will start using dialog elements. |
We have no updates to share about this yet. An interim alternative Stripe payment flow would be generating confirmation tokens to confirm on your server, specifying |
I faced the same issue. My current work around is to remove Stripe frame from its original wrapper Really this behavior should be default in Stripe itself already, since {
/**
* [wrap, dialog]
*/
const map = new WeakMap<HTMLElement, HTMLDialogElement>();
const mo = new MutationObserver(mutations => {
for (let { addedNodes, removedNodes } of mutations) {
for (let addedNode of addedNodes) {
// @note node should be element
if (!(addedNode instanceof HTMLElement)) continue;
// @note element is not a dialog (fired when we insert dialog with stripe frame)
if (addedNode.tagName === "DIALOG") continue;
let stripeFrame = addedNode.querySelector("[name^=__privateStripeFrame]");
if (stripeFrame) {
const stripeDialog = document.createElement("dialog");
stripeDialog.style.width = "100%";
stripeDialog.style.height = "100%";
// @note keep its original div wrapper for dialog clean up reasons
map.set(stripeFrame.parentElement!, stripeDialog);
// @note append stripe frame itself to our new dialog and then we
stripeDialog.append(stripeFrame);
document.body.append(stripeDialog);
stripeDialog.showModal();
}
}
// @note stripe.js::this.unmount removes wrapper element from <body> so we can track that too
for (let removedNode of removedNodes) {
const dialog = map.get(removedNode as HTMLElement);
if (!dialog) continue;
dialog.remove();
}
}
});
mo.observe(document.body, { childList: true });
} |
The fix via stripe support was to move the generation of tokens to the server, but I was too far in to change my solutions, so I made this brutal hook to check for the modal. Hopefully this is fixed soon /**
* Check if the Stripe modal is open.
* @link https://github.com/stripe/stripe-js/issues/599
* Stripe has no way to tell if their modal is open, and when using a dialog .showmodal(), it opens overtop of the Stripe modal.
* This hook checks if the Stripe modal is open
*/
export function useStripeModal() {
const [open, setOpen] = useState(false);
const [checkoutNeedsToBeReopened, setCheckoutNeedsToBeReopened] = useState(false);
const dialog = document.querySelector<HTMLDialogElement>('dialog.checkout');
// Continually check if there is a div with the class of .StripeElement
useInterval(() => {
const stripeModal = document.querySelector(
'div[tabindex="-1"] > iframe[name*="__privateStripeFrame"]'
);
setOpen(!!stripeModal);
}, 250);
useEffect(() => {
console.log('Stripe modal status changed', open);
// If the dialog is open and the stripe modal is open, close the dialog
if(dialog?.open && open) {
dialog.close();
setCheckoutNeedsToBeReopened(true);
} else if (checkoutNeedsToBeReopened && !open) {
// If the dialog was closed and the stripe modal is closed, reopen the dialog
dialog?.showModal();
}
// If the stripe modal is closed
}, [open])
return open;
} |
Somewhat tangential - but possibly of 👀 for @wesbos . Caleb Porzio was recently talking about the weirdness with the top level 'dialog' layer vs. z-index vs. manual vs. ..... stuff for his new component library. https://notesonwork.transistor.fm/episodes/modals-and-popover-woes (Not an affiliate link, blah) |
We also use the I implemented something similar as shared by @Lutymane. It works well but I'm a bit worried about it relying on some implementation details from this library. For instance if the If we can't solve the z-index issue right away, it would be great if stripe would let us know when such frames like 3D secures are being displayed so we don't have to rely on the lib internals. Note: this fix also works for stripe captcha challenges. It uses the same mechanism. |
Also an issue for us |
the same for us. it would be great if stripe would at least emit events for when this happens |
FOUND A SOLUTIONAs this thread explains how dialogs work basically all i had to do was to change how i open the dialog instead of using showModal() i tried using only show() and the 3D secure popped up above the dialog fromThis: the only thing that this removes is the backdrop that you can simply give back just by adding to the dialog element this class : |
|
What happened?
3D Secure Authentication modals rely on a high z-index, which regardless of number is trumped by top layer elements (e.g. a dialog opened with .showModal()). This can make the modal inaccessible (if, for example, it's called from a dialog element opened with showModal).
https://developer.mozilla.org/en-US/docs/Glossary/Top_layer
Environment
No response
Reproduction
No response
The text was updated successfully, but these errors were encountered: