"use client" import { useCallback, useEffect, useRef, useState } from 'react' import type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection' import type { LoginFormWidgetContext } from '@open-mercato/core/modules/auth/frontend/login-injection' import { apiCall } from '@open-mercato/ui/backend/utils/apiCall' import { useT } from '@open-mercato/shared/lib/i18n/context' import { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate' const SSO_ERROR_CODES = [ 'sso_failed', 'sso_missing_config', 'sso_email_not_verified', 'sso_state_missing', 'sso_idp_error', 'sso_missing_params', ] as const const HRD_DEBOUNCE_MS = 300 type HrdResponse = { hasSso: boolean configId?: string protocol?: string } export default function SsoLoginWidget({ context }: InjectionWidgetComponentProps) { const t = useT() const translate = useCallback( (key: string, fallback: string) => translateWithFallback(t, key, fallback), [t], ) const [ssoActive, setSsoActive] = useState(false) const lastCheckedEmail = useRef('') const debounceTimer = useRef | null>(null) useEffect(() => { const errorParam = context.searchParams.get('error') if (!errorParam) return const errorMessages: Record = { sso_failed: translate('sso.login.errors.failed', 'SSO login failed. Please try again.'), sso_missing_config: translate('sso.login.errors.missingConfig', 'SSO is not configured for this account.'), sso_email_not_verified: translate('sso.login.errors.emailNotVerified', 'Your email address is not verified by the identity provider. Please verify your email and try again.'), sso_state_missing: translate('sso.login.errors.stateMissing', 'SSO session expired. Please try again.'), sso_idp_error: translate('sso.login.errors.idpError', 'The identity provider returned an error. Please try again or contact your administrator.'), sso_missing_params: translate('sso.login.errors.missingParams', 'SSO callback was incomplete. Please try again.'), } if (SSO_ERROR_CODES.includes(errorParam as typeof SSO_ERROR_CODES[number])) { context.setError(errorMessages[errorParam] ?? errorMessages.sso_failed) } }, [context.searchParams, context.setError, translate]) const checkHrd = useCallback(async (email: string) => { if (!email || !email.includes('@')) { context.setAuthOverridePending?.(false) context.setAuthOverride(null) setSsoActive(false) lastCheckedEmail.current = '' return } if (email === lastCheckedEmail.current) { context.setAuthOverridePending?.(false) return } lastCheckedEmail.current = email try { const body: Record = { email } if (context.tenantId) { body.tenantId = context.tenantId } const res = await apiCall( '/api/sso/hrd', { method: 'POST', body: JSON.stringify(body), headers: { 'Content-Type': 'application/json' } }, ) if (res.result?.hasSso && res.result.configId) { const configId = res.result.configId setSsoActive(true) context.setAuthOverride({ providerId: 'sso', providerLabel: translate('sso.login.continueWithSso', 'Continue with SSO'), onSubmit: () => { const returnUrl = context.searchParams.get('returnUrl') || '/backend' window.location.href = `/api/sso/initiate?configId=${encodeURIComponent(configId)}&returnUrl=${encodeURIComponent(returnUrl)}` }, hidePassword: true, hideRememberMe: true, hideForgotPassword: true, }) } else { setSsoActive(false) context.setAuthOverride(null) } } catch { setSsoActive(false) context.setAuthOverride(null) } finally { context.setAuthOverridePending?.(false) } }, [context, translate]) useEffect(() => { if (debounceTimer.current) { clearTimeout(debounceTimer.current) } if (!context.email) { context.setAuthOverridePending?.(false) setSsoActive(false) context.setAuthOverride(null) lastCheckedEmail.current = '' return } debounceTimer.current = setTimeout(() => { context.setAuthOverridePending?.(true) void checkHrd(context.email) }, HRD_DEBOUNCE_MS) return () => { if (debounceTimer.current) { clearTimeout(debounceTimer.current) } } }, [context.email, checkHrd]) useEffect(() => () => { context.setAuthOverridePending?.(false) }, [context]) if (!ssoActive) return null return (
{translate('sso.login.ssoEnabled', 'SSO is enabled for this account')}
) }