diff --git a/apps/remix/server/security-headers.ts b/apps/remix/server/security-headers.ts index 871af694f..e87d7cb7a 100644 --- a/apps/remix/server/security-headers.ts +++ b/apps/remix/server/security-headers.ts @@ -19,23 +19,32 @@ const NON_PAGE_PATH_REGEX = /^(\/api\/|\/ingest\/|\/__manifest|\/assets\/|\/appl const EMBED_PATH_REGEX = /^\/embed(\/|\.data|$)/; /** - * Auth pages reachable from inside an embed iframe during the + * Non-`/embed` page routes that customers iframe directly, plus the auth + * pages reachable from inside an embed iframe during the * reauth-as-different-account flow. * + * Signing routes (`/sign/:token`, `/d/:token`): + * Some customer integrations embed these URLs directly (without going + * through `EmbedSignDocument`). Without `frame-ancestors *` here, those + * integrations break with a "refused to connect" iframe error. + * + * Auth routes (`/signin`, `/forgot-password`, `/check-email`, + * `/unverified-account`): * `apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx` * does `window.location.href = '/signin?...'` inside the iframe when the * user needs to sign out and sign back in as a different account, and * `` links/navigates to `/forgot-password`, `/check-email`, and - * `/unverified-account` from there. - * - * Without `frame-ancestors *` on these routes, the customer's iframe gets - * blocked the moment the user clicks "Login" in the reauth dialog. + * `/unverified-account` from there. Without `frame-ancestors *` on these + * routes, the customer's iframe gets blocked the moment the user clicks + * "Login" in the reauth dialog. * * These routes still get the strict nonced `script-src`/`style-src-elem` - * policy — only `frame-ancestors` is relaxed. + * policy — only `frame-ancestors` is relaxed. The `(\/|\.data|$)` tail + * keeps `/sign` from matching `/signin`/`/signup` and `/d` from matching + * `/dashboard`. */ -const AUTH_FRAMEABLE_PATH_REGEX = - /^\/(signin|forgot-password|check-email|unverified-account)(\/|\.data|$)/; +const FRAMEABLE_PATH_REGEX = + /^\/(signin|forgot-password|check-email|unverified-account|sign|d)(\/|\.data|$)/; /** * Hono context variable name where the per-request CSP nonce is stashed. @@ -59,7 +68,7 @@ const generateNonce = () => { return btoa(binary); }; -type CspPathKind = 'embed' | 'auth' | 'default'; +type CspPathKind = 'embed' | 'frameable' | 'default'; const buildCspHeader = ({ nonce, kind }: { nonce: string; kind: CspPathKind }) => { // `'self'` is included alongside `'strict-dynamic'` as a fallback for @@ -87,18 +96,18 @@ const buildCspHeader = ({ nonce, kind }: { nonce: string; kind: CspPathKind }) = // Embeds inject customer-supplied CSS via runtime-created `