import { useEffect, useContext } from "react"; import React from "react"; import { Typography, Switch, Button, Tooltip, TextField, Grid, Checkbox } from "@mui/material"; import { makeStyles } from "@mui/styles"; import { Link } from "react-router-dom"; import theme from "../theme.jsx"; import { toast } from "react-toastify"; import { Context } from "../context/ContextApi.jsx"; import { getTheme } from "../theme.jsx"; const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important", }, }); const SSOTab = ({selectedOrganization, userdata, isEditOrgTab, globalUrl, handleEditOrg})=>{ const classes = useStyles(); const [show2faSetup, setShow2faSetup] = React.useState(false); const [autoPrivision, setAutoProvision] = React.useState(selectedOrganization?.sso_config?.auto_provision) const [roleRequired, setRoleRequired] = React.useState(selectedOrganization?.sso_config?.role_required || false); const [showOpenIdCred, setShowOpenIdCred] = React.useState(false); const [showSamlCred, setShowSamlCred] = React.useState(false); const [skipSSOForAdmin, setSkipSSOForAdmin] = React.useState( selectedOrganization?.sso_config === undefined ? false : selectedOrganization?.sso_config?.skip_sso_for_admins === undefined ? false : selectedOrganization?.sso_config?.skip_sso_for_admins ); const [ssoEntrypoint, setSsoEntrypoint] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_entrypoint === undefined || selectedOrganization.sso_config.sso_entrypoint.length === 0 ? "" : selectedOrganization.sso_config.sso_entrypoint ); const [SSORequired, setSSORequired] = React.useState(selectedOrganization.sso_config === undefined ? false : selectedOrganization.sso_config.SSORequired === undefined ? false : selectedOrganization.sso_config.SSORequired); const [ssoCertificate, setSsoCertificate] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_certificate && selectedOrganization.sso_config.sso_certificate.length > 0 ? selectedOrganization.sso_config.sso_certificate : selectedOrganization.sso_config.sso_long_certificate || "" ); const [openidClientId, setOpenidClientId] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.client_id === undefined || selectedOrganization.sso_config.client_id.length === 0 ? "" : selectedOrganization.sso_config.client_id ); const [openidClientSecret, setOpenidClientSecret] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.client_secret === undefined || selectedOrganization.sso_config.client_secret.length === 0 ? "" : selectedOrganization.sso_config.client_secret ); const [openidAuthorization, setOpenidAuthorization] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.openid_authorization === undefined || selectedOrganization.sso_config.openid_authorization.length === 0 ? "" : selectedOrganization.sso_config.openid_authorization ); const [openidToken, setOpenidToken] = React.useState( selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.openid_token === undefined || selectedOrganization.sso_config.openid_token.length === 0 ? "" : selectedOrganization.sso_config.openid_token ) const { themeMode, supportEmail, brandColor } = useContext(Context); const theme = getTheme(themeMode, brandColor); useEffect(()=>{ if (openidClientSecret !== selectedOrganization?.sso_config?.client_secret) { setOpenidClientSecret(selectedOrganization?.sso_config?.client_secret) } if (openidClientId !== selectedOrganization?.sso_config?.client_id) { setOpenidClientId(selectedOrganization?.sso_config?.client_id) } if (openidAuthorization !== selectedOrganization?.sso_config?.openid_authorization) { setOpenidAuthorization(selectedOrganization?.sso_config?.openid_authorization) } if (openidToken !== selectedOrganization?.sso_config?.openid_token) { setOpenidToken(selectedOrganization?.sso_config?.openid_token) } if (ssoCertificate !== selectedOrganization?.sso_config?.sso_certificate || ssoCertificate !== selectedOrganization?.sso_config?.sso_long_certificate) { setSsoCertificate(selectedOrganization?.sso_config?.sso_certificate || selectedOrganization?.sso_config?.sso_long_certificate) } if (ssoEntrypoint !== selectedOrganization?.sso_config?.sso_entrypoint) { setSsoEntrypoint(selectedOrganization?.sso_config?.sso_entrypoint) } if (SSORequired !== selectedOrganization?.sso_config?.SSORequired) { setSSORequired(selectedOrganization?.sso_config?.SSORequired) } if (autoPrivision !== selectedOrganization?.sso_config?.auto_provision) { setAutoProvision(selectedOrganization?.sso_config?.auto_provision) } if (roleRequired !== selectedOrganization?.sso_config?.role_required) { setRoleRequired(selectedOrganization?.sso_config?.role_required) } if (skipSSOForAdmin !== selectedOrganization?.sso_config?.skip_sso_for_admins) { setSkipSSOForAdmin(selectedOrganization?.sso_config?.skip_sso_for_admins) } },[selectedOrganization]) const orgSaveButton = ( ); const toggleBetweenRequiredOrOptional = (event) => { if ( ssoEntrypoint === "" && openidAuthorization === "" && openidToken === "" ) { if (!SSORequired) { toast.error( "Please fill in fields for either OpenID connect or SSO before continuing. " ); return; } } else { toast.info("Toggled SSO. Remember to save."); } setSSORequired(event.target.checked); }; const handleChangeAutoProvision = (event) => { if ( ssoEntrypoint === "" && openidAuthorization === "" && openidToken === "" ) { toast.error( "Please fill in fields for either OpenID connect or SSO before continuing. " ); } else { setAutoProvision((prev)=> !prev); toast.info("Toggled Auto Provisioning. Remember to save."); } }; const handleChangeRoleRequired = (event) => { if ( openidAuthorization?.length === 0 && openidToken?.length === 0 ) { toast.error( "Please fill in fields for OpenID connect before continuing. " ); return; } else { setRoleRequired((prev)=> !prev); toast.info("Toggled Role Required. Remember to save."); } } const handlechangeSkipSSOForAdmin = (event) => { setSkipSSOForAdmin((prev) => !prev); toast.info("Toggled Skip SSO for Admins. Remember to save."); }; const HandleTestSSO = () => { const url = `${globalUrl}/api/v1/orgs/${selectedOrganization?.id}/change`; const data = { org_id: selectedOrganization?.id, sso_test: true, }; fetch(url, { mode: "cors", credentials: "include", crossDomain: true, method: "POST", body: JSON.stringify(data), withCredentials: true, headers: { "Content-Type": "application/json; charset=utf-8", }, }) .then((response) => { if (response.status !== 200) { toast.error( `Failed to test SSO. Please try again later or contact ${supportEmail} if issue persists.`, { duration: 3000 } ); return null; } return response.json(); }) .then((responjson) => { if (!responjson) return; if (responjson["reason"] === "SSO_REDIRECT") { toast.info( "Redirecting to SSO login page as SSO is required for this organization.", { duration: 3000, onClose: () => { window.location.href = responjson["url"]; } } ); } else { toast.error( "No SSO found for this org. Please set up SSO for this org.", { duration: 3000 } ); } }) .catch((error) => { console.error("Error for SSO test:", error); toast.error( "An error occurred while testing SSO. Please try again.", { duration: 3000 } ); }); }; return (
SSO Configuration
Make SAML SSO or OpenID Authentication Required or Optional for Your Organization.
{SSORequired ? "Required" : "Optional"}
{/* auto privisiong in sso */}
Auto-provisioning of users in SSO. By default, users are auto-provisioned in SSO when they login. If you enable this, no new user will be added in your organization when they login via SSO.
Restrict user login to SSO if no valid role is assigned by the SSO provider. When enabled, users will not be allowed to log in via SSO if their assigned role doesn't matches one of the following: shuffle-user, shuffle-admin, or shuffle-org-reader. Currently, available for OpenId Connect only. Learn more
{userdata && userdata?.active_org?.creator_org?.length > 0 ? null :
Skip SSO for Admins. When enabled, parent org admins will be able switch to the sub-organization without SSO.
}
You can test your SSO configuration by clicking the button below. Before testing, ensure you have set Open ID Connect or SAML SSO credentials. 0 || ssoCertificate?.length > 0 || openidAuthorization?.length > 0 || openidClientId?.length > 0 ) ? "Please ensure all SSO credentials are set before testing." : "" } >
OpenID connect Configure and Authorize SAML / SSO or OpenID connect. {" "} Learn more IdP URL for Shuffle OpenID: {`${globalUrl}/api/v1/login_openid`}
Show OpenID Credentials setShowOpenIdCred(e.target.checked)} name="showOpenIdCred" color="primary" />
Client ID 0 ? "•".repeat(50) : ""} onChange={(e) => setOpenidClientId(e.target.value)} onFocus={(e) => setShowOpenIdCred(true)} onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color: theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: theme.palette.textFieldStyle.borderRadius, }, }} /> Client Secret 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidClientSecret(e.target.value); }} onFocus={(e) => setShowOpenIdCred(true)} onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color: theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: theme.palette.textFieldStyle.borderRadius, }, }} /> Authorization URL 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidAuthorization(e.target.value) }} onFocus={(e) => setShowOpenIdCred(true)} onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color: theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: 4, }, }} /> Token URL 0 ? "•".repeat(50) : ""} onChange={(e) => { setOpenidToken(e.target.value) }} onFocus={(e) => setShowOpenIdCred(true)} onBlur={(e) => setShowOpenIdCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color:theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: 4, }, }} />
{/**/} {/*isCloud ? null : */} SAML SSO (v1.1) IdP URL for Shuffle SAML/SSO: {`${globalUrl}/api/v1/login_sso`}
Show SAML Credentials setShowSamlCred(e.target.checked)} name="showSamlCred" color="primary" />
SSO Entrypoint (IdP) 0 ? "•".repeat(50) : ""} onChange={(e) => { setSsoEntrypoint(e.target.value); }} onFocus={(e) => setShowSamlCred(true)} onBlur={(e) => setShowSamlCred(false)} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color: theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: 4, }, }} /> SSO Certificate (X509) 0 ? "•".repeat(50) : ""} onFocus={(e) => setShowSamlCred(true)} onBlur={(e) => setShowSamlCred(false)} onChange={(e) => { setSsoCertificate(e.target.value); }} InputProps={{ classes: { notchedOutline: isEditOrgTab ? null : classes.notchedOutline, }, style: { color: theme.palette.textFieldStyle.color, fontFamily: "var(--zds-typography-base,Inter,Helvetica,arial,sans-serif)", fontWeight: 400, fontSize: 16, borderRadius: 4, }, }} />
{orgSaveButton}
) } export default SSOTab