Revise SAML flow, update Okta SAML docs

This commit is contained in:
Tuan Dang
2023-07-29 01:57:07 +07:00
parent ed2cf68935
commit cc4b749ce8
22 changed files with 131 additions and 174 deletions
+2 -1
View File
@@ -4,7 +4,8 @@ import SecurityClient from "@app/components/utilities/SecurityClient";
import {
getAuthToken,
getMfaTempToken,
getSignupTempToken} from "@app/reactQuery";
getSignupTempToken
} from "@app/reactQuery";
export const apiRequest = axios.create({
baseURL: "/",
+4 -10
View File
@@ -29,8 +29,7 @@ export const useCreateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert,
audience
cert
}: {
organizationId: string;
authProvider: string;
@@ -38,7 +37,6 @@ export const useCreateSSOConfig = () => {
entryPoint: string;
issuer: string;
cert: string;
audience: string;
}) => {
const { data } = await apiRequest.post(
"/api/v1/sso/config",
@@ -48,8 +46,7 @@ export const useCreateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert,
audience
cert
}
);
@@ -70,8 +67,7 @@ export const useUpdateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert,
audience
cert
}: {
organizationId: string;
authProvider?: string;
@@ -79,7 +75,6 @@ export const useUpdateSSOConfig = () => {
entryPoint?: string;
issuer?: string;
cert?: string;
audience?: string;
}) => {
const { data } = await apiRequest.patch(
"/api/v1/sso/config",
@@ -89,8 +84,7 @@ export const useUpdateSSOConfig = () => {
...(isActive !== undefined ? { isActive } : {}),
...(entryPoint !== undefined ? { entryPoint } : {}),
...(issuer !== undefined ? { issuer } : {}),
...(cert !== undefined ? { cert } : {}),
...(audience !== undefined ? { audience } : {})
...(cert !== undefined ? { cert } : {})
}
);
@@ -6,6 +6,7 @@ import { Button, Switch, UpgradePlanModal } from "@app/components/v2";
import { useOrganization, useSubscription } from "@app/context";
import {
useGetSSOConfig,
useCreateSSOConfig,
useUpdateSSOConfig
} from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
@@ -27,6 +28,8 @@ export const OrgSSOSection = (): JSX.Element => {
"addSSO"
] as const);
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateSSOConfig();
const handleSamlSSOToggle = async (value: boolean) => {
try {
if (!currentOrg?._id) return;
@@ -49,6 +52,33 @@ export const OrgSSOSection = (): JSX.Element => {
}
}
const addSSOBtnClick = async () => {
try {
if (subscription?.samlSSO && currentOrg) {
if (!data) {
// case: SAML SSO is not configured
// -> initialize empty SAML SSO configuration
await createMutateAsync({
organizationId: currentOrg._id,
authProvider: "okta-saml",
isActive: false,
entryPoint: "",
issuer: "",
cert: ""
});
}
handlePopUpOpen("addSSO");
} else {
handlePopUpOpen("upgradePlan");
}
} catch (err) {
console.error(err);
}
}
console.log("getSSOConfig: ", data);
return (
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
<div className="flex items-center mb-8">
@@ -57,13 +87,7 @@ export const OrgSSOSection = (): JSX.Element => {
</h2>
{!isLoading && (
<Button
onClick={() => {
if (subscription?.samlSSO) {
handlePopUpOpen("addSSO");
} else {
handlePopUpOpen("upgradePlan");
}
}}
onClick={addSSOBtnClick}
colorSchema="secondary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
@@ -71,39 +95,33 @@ export const OrgSSOSection = (): JSX.Element => {
</Button>
)}
</div>
{!isLoading && data && (
<>
<div className="mb-4">
<Switch
id="enable-saml-sso"
onCheckedChange={(value) => handleSamlSSOToggle(value)}
isChecked={data.isActive}
>
Enable SAML SSO
</Switch>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">SSO identifier</h3>
<p className="text-gray-400 text-md">{data._id}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Type</h3>
<p className="text-gray-400 text-md">{ssoAuthProviderMap[data.authProvider]}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Audience</h3>
<p className="text-gray-400 text-md">{data.audience}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Entrypoint</h3>
<p className="text-gray-400 text-md">{data.entryPoint}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Issuer</h3>
<p className="text-gray-400 text-md">{data.issuer}</p>
</div>
</>
{data && (
<div className="mb-4">
<Switch
id="enable-saml-sso"
onCheckedChange={(value) => handleSamlSSOToggle(value)}
isChecked={data ? data.isActive : false}
>
Enable SAML SSO
</Switch>
</div>
)}
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">SSO identifier</h3>
<p className="text-gray-400 text-md">{(data && data._id !== "") ? data._id : "-"}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Type</h3>
<p className="text-gray-400 text-md">{(data && data.authProvider !== "") ? ssoAuthProviderMap[data.authProvider] : "-"}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Entrypoint</h3>
<p className="text-gray-400 text-md">{(data && data.entryPoint !== "") ? data.entryPoint : "-"}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Issuer</h3>
<p className="text-gray-400 text-md">{(data && data.issuer !== "") ? data.issuer : "-"}</p>
</div>
<SSOModal
popUp={popUp}
handlePopUpClose={handlePopUpClose}
@@ -2,7 +2,6 @@ import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import {
Button,
@@ -29,8 +28,7 @@ const schema = yup.object({
authProvider: yup.string().required("SSO Type is required"),
entryPoint: yup.string().required("IDP entrypoint is required"),
issuer: yup.string().required("Issuer string is required"),
cert: yup.string().required("IDP's public signing certificate is required"),
audience: yup.string().required("Expected SAML response audience is required"),
cert: yup.string().required("IDP's public signing certificate is required")
}).required();
export type AddSSOFormData = yup.InferType<typeof schema>;
@@ -70,8 +68,7 @@ export const SSOModal = ({
authProvider: data?.authProvider ?? "",
entryPoint: data?.entryPoint ?? "",
issuer: data?.issuer ?? "",
cert: data?.cert ?? "",
audience: data?.audience ?? ""
cert: data?.cert ?? ""
});
}
}, [data]);
@@ -80,8 +77,7 @@ export const SSOModal = ({
authProvider,
entryPoint,
issuer,
cert,
audience
cert
}: AddSSOFormData) => {
try {
if (!currentOrg) return;
@@ -93,8 +89,7 @@ export const SSOModal = ({
isActive: false,
entryPoint,
issuer,
cert,
audience
cert
});
} else {
await updateMutateAsync({
@@ -103,8 +98,7 @@ export const SSOModal = ({
isActive: false,
entryPoint,
issuer,
cert,
audience
cert
});
}
@@ -160,24 +154,16 @@ export const SSOModal = ({
</FormControl>
)}
/>
{authProvider && authProvider === "okta-saml" && (
{authProvider && authProvider === "okta-saml" && data && (
<>
<Controller
control={control}
name="audience"
render={({ field, fieldState: { error } }) => (
<FormControl
label="Audience"
errorText={error?.message}
isError={Boolean(error)}
>
<Input
{...field}
placeholder="https://your-domain.com"
/>
</FormControl>
)}
/>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">ACS URL</h3>
<p className="text-gray-400 text-md break-all">{`${window.origin}/api/v1/sso/saml2/${data._id}`}</p>
</div>
<div className="mb-4">
<h3 className="text-mineshaft-400 text-sm">Entity ID</h3>
<p className="text-gray-400 text-md">{window.origin}</p>
</div>
<Controller
control={control}
name="entryPoint"