diff --git a/Dockerfile b/Dockerfile index 967545e24..f5a671fa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG NODEJS_IMAGE=node:18.15.0-alpine3.17 +ARG NODEJS_IMAGE=node:18.16.1-alpine3.18 FROM --platform=$BUILDPLATFORM $NODEJS_IMAGE AS base # Install dependencies only when needed diff --git a/README.md b/README.md index 7669aeef1..6577daf84 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # SAML Jackson: Enterprise SSO made simple

+ npm Docker pull Github stargazers @@ -28,7 +29,13 @@ Jackson implements the SAML login flow as an OAuth 2.0 or OpenID Connect flow, a Try our hosted demo showcasing the SAML SP login flow [here](https://saml-demo.boxyhq.com), no SAML configuration required thanks to our [Mock SAML](https://mocksaml.com) service. -You can also try our hosted demo showcasing the SAML IdP login flow [here](https://mocksaml.com/saml/login). +## Videos +- SSO/OIDC Tutorial [SAML Jackson Enterprise SSO](https://www.youtube.com/watch?v=nvsD4-GQw4A) +- SAML single sign-on login [demo](https://www.youtube.com/watch?v=VBUznQwoEWU) + +## Demo +- SAML IdP login flow showcasing self hosted [Mock SAML](https://mocksaml.com/saml/login) +- SAML [demo flow](https://saml-demo.boxyhq.com/) ## Documentation diff --git a/components/connection/EditConnection.tsx b/components/connection/EditConnection.tsx index c498e9e1d..4a7cec16e 100644 --- a/components/connection/EditConnection.tsx +++ b/components/connection/EditConnection.tsx @@ -103,14 +103,16 @@ const EditConnection = ({ connection, setupLinkToken, isSettingsView = false }: const [delModalVisible, setDelModalVisible] = useState(false); const toggleDelConfirm = () => setDelModalVisible(!delModalVisible); const deleteConnection = async () => { + const queryParams = new URLSearchParams({ + clientID: connection.clientID, + clientSecret: connection.clientSecret, + }); const res = await fetch( - setupLinkToken ? `/api/setup/${setupLinkToken}/sso-connection` : '/api/admin/connections', + setupLinkToken + ? `/api/setup/${setupLinkToken}/sso-connection?${queryParams}` + : `/api/admin/connections?${queryParams}`, { method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ clientID: connection?.clientID, clientSecret: connection?.clientSecret }), } ); diff --git a/components/dsync/CreateDirectory.tsx b/components/dsync/CreateDirectory.tsx index c084e77c3..f9444a017 100644 --- a/components/dsync/CreateDirectory.tsx +++ b/components/dsync/CreateDirectory.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'next-i18next'; import { useRouter } from 'next/router'; -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { ApiResponse } from 'types'; import { errorToast, successToast } from '@components/Toaster'; import type { Directory } from '@boxyhq/saml-jackson'; @@ -13,28 +13,32 @@ interface CreateDirectoryProps { defaultWebhookEndpoint: string | undefined; } +type UnSavedDirectory = Omit & { + webhook_url: string; + webhook_secret: string; +}; + +const defaultDirectory: UnSavedDirectory = { + name: '', + tenant: '', + product: '', + webhook_url: '', + webhook_secret: '', + type: 'azure-scim-v2', + google_domain: '', +}; + const CreateDirectory = ({ setupLinkToken, defaultWebhookEndpoint }: CreateDirectoryProps) => { const { t } = useTranslation('common'); const router = useRouter(); const { providers } = useDirectoryProviders(setupLinkToken); const [loading, setLoading] = useState(false); - const [directory, setDirectory] = useState({ - name: '', - tenant: '', - product: '', - webhook_url: defaultWebhookEndpoint, - webhook_secret: '', - type: '', + const [showDomain, setShowDomain] = useState(false); + const [directory, setDirectory] = useState({ + ...defaultDirectory, + webhook_url: defaultWebhookEndpoint || '', }); - useEffect(() => { - if (providers && Object.keys(providers).length > 0) { - setDirectory((directory) => { - return { ...directory, type: Object.keys(providers)[0] }; - }); - } - }, [providers]); - const onSubmit = async (event: React.FormEvent) => { event.preventDefault(); @@ -78,6 +82,11 @@ const CreateDirectory = ({ setupLinkToken, defaultWebhookEndpoint }: CreateDirec ...directory, [target.id]: target.value, }); + + // Ask for domain if google is selected + if (target.id === 'type') { + target.value === 'google' ? setShowDomain(true) : setShowDomain(false); + } }; const backUrl = setupLinkToken ? `/setup/${setupLinkToken}/directory-sync` : '/admin/directory-sync'; @@ -116,6 +125,22 @@ const CreateDirectory = ({ setupLinkToken, defaultWebhookEndpoint }: CreateDirec })} + {showDomain && ( +

+ + +
+ )} {!setupLinkToken && ( <>
diff --git a/components/dsync/DirectoryInfo.tsx b/components/dsync/DirectoryInfo.tsx index 7a9fc5d3b..8d3815e4f 100644 --- a/components/dsync/DirectoryInfo.tsx +++ b/components/dsync/DirectoryInfo.tsx @@ -6,6 +6,7 @@ import React from 'react'; import useDirectory from '@lib/ui/hooks/useDirectory'; import Loading from '@components/Loading'; import { errorToast } from '@components/Toaster'; +import { dsyncGoogleAuthURL } from '@lib/env'; const DirectoryInfo = ({ directoryId, setupLinkToken }: { directoryId: string; setupLinkToken?: string }) => { const { t } = useTranslation('common'); @@ -71,14 +72,24 @@ const DirectoryInfo = ({ directoryId, setupLinkToken }: { directoryId: string; s )}
-
-
- + {directory.scim.endpoint && directory.scim.secret && ( +
+
+ +
+
+ +
-
- + )} + {directory.type === 'google' && ( +
+
-
+ )}
); diff --git a/components/dsync/EditDirectory.tsx b/components/dsync/EditDirectory.tsx index 111484069..5f2f231e2 100644 --- a/components/dsync/EditDirectory.tsx +++ b/components/dsync/EditDirectory.tsx @@ -11,7 +11,7 @@ import useDirectory from '@lib/ui/hooks/useDirectory'; import { ToggleConnectionStatus } from './ToggleConnectionStatus'; import { DeleteDirectory } from './DeleteDirectory'; -type FormState = Pick; +type FormState = Pick; const defaultFormState: FormState = { name: '', @@ -20,6 +20,7 @@ const defaultFormState: FormState = { endpoint: '', secret: '', }, + google_domain: '', }; const EditDirectory = ({ directoryId, setupLinkToken }: { directoryId: string; setupLinkToken?: string }) => { @@ -39,6 +40,7 @@ const EditDirectory = ({ directoryId, setupLinkToken }: { directoryId: string; s endpoint: directory.webhook?.endpoint, secret: directory.webhook?.secret, }, + google_domain: directory.google_domain, }); } }, [directory]); @@ -131,6 +133,20 @@ const EditDirectory = ({ directoryId, setupLinkToken }: { directoryId: string; s value={directoryUpdated.name} />
+ {directory.type === 'google' && ( +
+ + +
+ )}