diff --git a/apps/remix/app/routes/_unauthenticated+/signup.tsx b/apps/remix/app/routes/_unauthenticated+/signup.tsx
index 7a254457d2..313fa29de0 100644
--- a/apps/remix/app/routes/_unauthenticated+/signup.tsx
+++ b/apps/remix/app/routes/_unauthenticated+/signup.tsx
@@ -12,7 +12,7 @@ import { isValidReturnTo, normalizeReturnTo } from '@documenso/lib/utils/is-vali
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { Loader2Icon } from 'lucide-react';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { redirect, useSearchParams } from 'react-router';
import { SignUpForm } from '~/components/forms/signup';
@@ -78,6 +78,8 @@ export default function SignUp({ loaderData }: Route.ComponentProps) {
} = loaderData;
const [searchParams] = useSearchParams();
+ const [isRedirectFailed, setIsRedirectFailed] = useState(false);
+ const [isEmbeddedRedirect, setIsEmbeddedRedirect] = useState(false);
// Suppress the automatic redirect when the user asked for the manual form
// via ?direct=1, or when a previous OIDC attempt bounced back with an error
@@ -85,17 +87,33 @@ export default function SignUp({ loaderData }: Route.ComponentProps) {
const isDirectEntry = searchParams.get('direct') === '1';
const hasIdpError = searchParams.get('error') !== null;
- const shouldRedirectToOIDC = shouldAutoRedirectToOIDC && !isDirectEntry && !hasIdpError;
+ useEffect(() => {
+ const params = new URLSearchParams(window.location.hash.slice(1));
+
+ setIsEmbeddedRedirect(params.get('embedded') === 'true');
+ }, []);
+
+ const shouldRedirectToOIDC = shouldAutoRedirectToOIDC && !isDirectEntry && !hasIdpError && !isEmbeddedRedirect;
useEffect(() => {
if (!shouldRedirectToOIDC) {
return;
}
- void authClient.oidc.signIn({ redirectPath: returnTo ?? '/' });
+ // Guard against the initial render racing the embedded detection above:
+ // read the hash synchronously so embedded contexts never bounce to the IdP.
+ if (new URLSearchParams(window.location.hash.slice(1)).get('embedded') === 'true') {
+ return;
+ }
+
+ authClient.oidc.signIn({ redirectPath: returnTo ?? '/' }).catch(() => {
+ // Fall back to the manual form instead of leaving the user on the
+ // spinner forever when the IdP is unreachable.
+ setIsRedirectFailed(true);
+ });
}, [shouldRedirectToOIDC, returnTo]);
- if (shouldRedirectToOIDC) {
+ if (shouldRedirectToOIDC && !isRedirectFailed) {
return (