Skip to main content
Most 3DS authentications resolve frictionlessly with no shopper interaction. When the issuer requires a challenge, threeDS.authenticate() presents it in an iframe or modal by default. Some issuers instead require a top-level redirect to their own page — this guide covers both.

Container vs. modal

By default, a challenge renders in a centered modal overlay (role="dialog", non-dismissable). To render it inline instead, pass challengeContainer:
challengeContainer accepts an element or a CSS selector. Use it when you want the challenge to appear inside your own checkout layout instead of a floating overlay.

Choosing how the challenge presents

challengePresentation controls whether a challenge uses an iframe or a full-page redirect: Use redirect (or rely on auto with returnUrl set) inside in-app webviews, where a same-origin iframe challenge may not render reliably.
returnUrl only switches which local path the browser’s top-level redirect comes back to — it isn’t sent to or validated by the gateway. The gateway always resolves the challenge against the return URL registered on your account (see the enablement note below), so pointing returnUrl at a different path doesn’t change where the issuer is told to redirect.

How it works: the redirect path

  1. The browser leaves your page in a top-level form submission to the issuer’s authentication page.
  2. The shopper completes the challenge there.
  3. The issuer redirects back to your returnUrl, with #action_ref= in the URL fragment (some flows use ?action_ref=; your return page should handle both).
  4. Your return page resumes the pending 3DS flow and reads the final status.
  5. Your server charges using the resulting reference, same as the non-redirect path.

The return page

Serve the redirect return page with Referrer-Policy: no-referrer, and don’t load third-party scripts on it. The action_ref in the URL is a capability — anything that can read the URL or a Referer header sent from this page can look up the 3DS status.
Registering returnUrl with your account requires enablement — contact support to allow-list it.
Redirect and return routes need a slightly loosened Content Security Policy (form-action and frame-src allowing https:) scoped to just those routes — see Content Security Policy.
3DS calls run on your own checkout page, not a RadiumOne-hosted one — your CSP’s connect-src must include your API origin. Without it, the browser silently blocks the status calls and a challenge times out after 5 minutes instead of failing fast. See Content Security Policy: troubleshooting.

Decoupled authentication

Some issuers authenticate out-of-band — for example, the shopper approves the payment in their banking app instead of typing a code. Pass onDecoupled to show your own waiting UI:
The SDK polls the authentication status on your behalf until it reaches a final state or expires — you don’t need to poll yourself.

Cancellation

Pass an AbortSignal to let the shopper cancel a challenge in progress (for example with a “Cancel” button):
A cancelled authentication rejects with urn:radiumone:three-ds:cancelled. Return the shopper to the payment form — don’t retry automatically. See Handle 3D Secure failures in Elements for this and other rejected-flow cases.

In-app webviews

In-app browsers (for example inside a mobile app’s webview) don’t always support cross-origin iframes reliably. Set challengePresentation: "redirect" and pass a returnUrl that opens back inside your webview.
Never use a secret key (r1sk_…) in browser code, mobile apps, or anywhere a shopper can inspect it. Secret keys belong on your server only.

Server policy

Whichever presentation you use, your server must still independently validate the three_ds.ref at charge time — never trust a browser-reported status alone. See Server policy.

Next steps

Authentication results

Status meanings and 3DS error remedies.

Content Security Policy

CSP directives for card fields, 3DS iframes and redirects.
Last modified on September 15, 2026