SAML setup UI/UX improvements. (#1457)

This commit is contained in:
Kiran K 2023-09-07 17:56:34 +05:30 committed by GitHub
parent 221c9e90f3
commit 42cabf0b35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 13409 additions and 267 deletions

View File

@ -1,6 +1,6 @@
export const PoweredBy = () => {
return (
<p className='text-center text-xs text-gray-500'>
<p className='text-center text-xs text-gray-500 py-5'>
<a href='https://boxyhq.com/' target='_blank' rel='noopener noreferrer'>
Powered by BoxyHQ
</a>

View File

@ -14,9 +14,8 @@ import { mutate } from 'swr';
import { ApiResponse } from 'types';
import { errorToast } from '@components/Toaster';
import { useTranslation } from 'next-i18next';
import { LinkBack } from '@components/LinkBack';
import { ButtonPrimary } from '@components/ButtonPrimary';
import { InputWithCopyButton } from '@components/ClipboardButton';
import { LinkBack } from '@components/LinkBack';
function getInitialState(connectionType, fieldCatalog: FieldCatalogItem[]) {
const _state = {};
@ -40,7 +39,6 @@ function getInitialState(connectionType, fieldCatalog: FieldCatalogItem[]) {
const CreateConnection = ({
setupLinkToken,
idpEntityID,
isSettingsView = false,
adminPortalSSODefaults,
}: {
@ -65,7 +63,7 @@ const CreateConnection = ({
const connectionIsOIDC = newConnectionType === 'oidc';
const backUrl = setupLinkToken
? `/setup/${setupLinkToken}`
? null
: isSettingsView
? '/admin/settings/sso-connection'
: '/admin/sso-connection';
@ -134,14 +132,7 @@ const CreateConnection = ({
return (
<>
<LinkBack href={backUrl} />
{idpEntityID && setupLinkToken && (
<div className='mb-5 mt-5 items-center justify-between'>
<div className='form-control'>
<InputWithCopyButton text={idpEntityID} label={t('idp_entity_id')} />
</div>
</div>
)}
{backUrl && <LinkBack href={backUrl} />}
<div>
<h2 className='mb-5 mt-5 font-bold text-gray-700 dark:text-white md:text-xl'>
{t('create_sso_connection')}

View File

@ -12,12 +12,12 @@ import { hexToHsl, darkenHslColor } from '@lib/color';
import { PoweredBy } from '@components/PoweredBy';
export const SetupLinkLayout = ({ children }: { children: React.ReactNode }) => {
const { t } = useTranslation('common');
const router = useRouter();
const { branding } = usePortalBranding();
const { t } = useTranslation('common');
const { token } = router.query as { token: string };
const { branding } = usePortalBranding();
const { setupLink, error, isLoading } = useSetupLink(token);
if (isLoading) {
@ -43,27 +43,25 @@ export const SetupLinkLayout = ({ children }: { children: React.ReactNode }) =>
<style>{`:root { --p: ${primaryColor}; --pf: ${darkenHslColor(primaryColor, 30)}; }`}</style>
)}
<div className='flex flex-1 flex-col'>
<div className='sticky top-0 z-10 flex h-16 flex-shrink-0 border-b bg-white'>
<div className='flex flex-shrink-0 items-center px-4'>
<Link href={`/setup/${token}`}>
<div className='flex items-center'>
<div className='mx-auto max-w-3xl'>
<div className='flex flex-1 flex-col'>
<div className='top-0 flex h-16 flex-shrink-0 border-b'>
<div className='flex flex-shrink-0 items-center gap-4'>
<Link href={`/setup/${token}`}>
{branding?.logoUrl && (
<Image src={branding.logoUrl} alt={branding.companyName} width={42} height={42} />
<Image src={branding.logoUrl} alt={branding.companyName} width={40} height={40} />
)}
<span className='ml-4 text-xl font-bold text-gray-900'>{title}</span>
</div>
</Link>
</Link>
<span className='text-xl font-bold tracking-wide text-gray-900'>{title}</span>
</div>
</div>
</div>
<main>
<div className='py-6'>
<div className='mx-auto max-w-7xl px-4 sm:px-6 md:px-8'>
<main>
<div className='py-6'>
{error && <InvalidSetupLinkAlert message={error.message} />}
{setupLink && children}
</div>
</div>
</main>
</main>
</div>
</div>
<PoweredBy />
</>

View File

@ -0,0 +1,52 @@
import { useRouter } from 'next/router';
import { CreateSAMLConnection as CreateSAML, CreateOIDCConnection as CreateOIDC } from '@boxyhq/react-ui/sso';
import { errorToast } from '@components/Toaster';
interface CreateSSOConnectionProps {
setupLinkToken: string;
idpType: 'saml' | 'oidc';
}
const CreateSSOConnection = ({ setupLinkToken, idpType }: CreateSSOConnectionProps) => {
const router = useRouter();
const onSuccess = () => {
router.push({
pathname: '/setup/[token]/sso-connection',
query: { token: setupLinkToken },
});
};
const onError = (message: string) => {
errorToast(message);
};
const urls = {
save: `/api/setup/${setupLinkToken}/sso-connection`,
};
const classNames = {
button: 'btn btn-primary mt-4',
};
return idpType === 'saml' ? (
<CreateSAML
variant='basic'
classNames={classNames}
urls={urls}
successCallback={onSuccess}
errorCallback={onError}
/>
) : (
<CreateOIDC
variant='basic'
classNames={classNames}
urls={urls}
successCallback={onSuccess}
errorCallback={onError}
/>
);
};
export default CreateSSOConnection;

View File

@ -0,0 +1,17 @@
import NextButton from '@components/setup-link-instructions/NextButton';
import PreviousButton from '@components/setup-link-instructions/PreviousButton';
interface FooterProps {
hasNext?: boolean;
}
const Footer = ({ hasNext = true }: FooterProps) => {
return (
<div className='flex justify-between items-center py-4'>
<PreviousButton />
{hasNext && <NextButton />}
</div>
);
};
export default Footer;

View File

@ -0,0 +1,28 @@
import { useRouter } from 'next/router';
import { ButtonPrimary } from '@components/ButtonPrimary';
const NextButton = () => {
const router = useRouter();
const onClick = () => {
const { idp, step, token } = router.query as { idp: string; step: string; token: string };
router.push({
pathname: router.pathname,
query: {
idp,
step: parseInt(step) + 1,
token,
},
});
};
return (
<div>
<ButtonPrimary onClick={onClick}>Next Step</ButtonPrimary>
</div>
);
};
export default NextButton;

View File

@ -0,0 +1,28 @@
import { useRouter } from 'next/router';
import { ButtonOutline } from '@components/ButtonOutline';
const PreviousButton = () => {
const router = useRouter();
const onClick = () => {
const { idp, step, token } = router.query as { idp: string; step: string; token: string };
router.push({
pathname: router.pathname,
query: {
idp,
step: parseInt(step) - 1,
token,
},
});
};
return (
<div>
<ButtonOutline onClick={onClick}>Previous Step</ButtonOutline>
</div>
);
};
export default PreviousButton;

View File

@ -0,0 +1,31 @@
import { useRouter } from 'next/router';
import { identityProviders } from '@lib/constants';
const SelectIdentityProviders = () => {
const router = useRouter();
const onClick = (id: string) => {
const params = new URLSearchParams({
idp: id,
step: '1',
});
router.push(router.asPath + '?' + params.toString());
};
return (
<div className='grid gap-4 grid-cols-2 w-full'>
{identityProviders.map((provider) => (
<button
key={provider.id}
className='text-left border p-4 rounded hover:border-primary font-bold'
onClick={() => onClick(provider.id)}>
{provider.name}
</button>
))}
</div>
);
};
export default SelectIdentityProviders;

View File

@ -0,0 +1,15 @@
## Step 1: Create Application
From your Auth0 dashboard, click **Applications** from the left navigation menu.
If your application is already created, choose it from the list and move to the next step.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/auth0/1.png)
If you haven't created a SAML application, click the **Create Application** button to create a new application.
Give your application a **Name** and click **Create**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/auth0/5.png)
<Footer />

View File

@ -0,0 +1,19 @@
## Step 2: Configure Application
Click the tab **Addons** and enable **SAML2 WEB APP** addon.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/auth0/2.png)
Enter your **Application Callback URL** on the next screen and click **Enable**.
<InputWithCopyButton text={spConfig.acsUrl} label='Application Callback URL' />
<AdvancedSPConfigLink />
![img alt](https://cdn.boxyhq.com/docs/sso/providers/auth0/3.png)
Click the tab **Usage** and download the **Identity Provider Metadata**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/auth0/4.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 3: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,19 @@
## Step 1: Create Application
From your Azure Admin console, click **Enterprise applications** from the left navigation menu.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/1.png)
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click **New application** from the top to create a new application.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/2.png)
From the next screen, click **Create your own application**.
Give your application a **Name** and click **Create**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/3.png)
<Footer />

View File

@ -0,0 +1,26 @@
## Step 2: Configure Application
Select **Single Sign On** from the **Manage** section of your app and then **SAML**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/4.png)
Click **Edit** on the **Basic SAML Configuration** section.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/5.png)
Enter the following values in the **Basic SAML Configuration** section on the next screen:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='Reply URL (Assertion Consumer Service URL)' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Identifier (Entity ID)' />
</div>
</div>
Click **Save** to save your changes.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/6.png)
<Footer />

View File

@ -0,0 +1,22 @@
## Step 3: Attribute Mapping
Click **Edit** on the **Attributes & Claims** section.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/7.png)
You have to configure the following attributes under the **Attributes & Claims** section:
| Name | Value |
| -------------------------------------------------------------------- | ---------------------- |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress` | user.mail |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname` | user.givenname |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` | user.userprincipalname |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname` | user.surname |
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/8.png)
Go to the section **SAML Signing Certificate** section and download the **Federation Metadata XML**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/azure/9.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,19 @@
## Step 1: Create Application
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={oidcCallbackUrl} label='Callback URL' />
</div>
</div>
**Client/Application ID**
The OIDC Identity Provider normally will generate a unique identifier for the registered App. Make a note of this to be used later when creating the SSO connection with Jackson.
**Client Secret**
Along with the Client ID, the IdP also generates a client secret which is used to authenticate the client while issuing tokens. Make a note of this to be used later when creating the SSO connection with Jackson.
_Since Jackson acts as a proxy between the app and the OIDC Identity Provider, here the application/client is Jackson._
<Footer />

View File

@ -0,0 +1,5 @@
## Step 2: Create OIDC Connection
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='oidc' />
<Footer hasNext={false} />

View File

@ -0,0 +1,28 @@
## Step 1: Configuration SAML Application
Your Identity Provider (IdP) will ask for the following information while configuring the SAML application.
Please do not add a trailing slash at the end of the URLs.
Create them exactly as shown below:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='Reply URL (Assertion Consumer Service URL)' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Identifier (Entity ID)' />
</div>
</div>
- Response: `Signed`
- Assertion Signature: `Signed`
- Signature Algorithm: `RSA-SHA256`
- Assertion Encryption: `Unencrypted`
The deployed Jackson service has a Service Provider (SP) endpoint that exposes the above metadata and the same can be accessed at [SAML Configuration](/.well-known/saml-configuration).
<Footer />

View File

@ -0,0 +1,19 @@
## Step 2: SAML Profile/Claims/Attributes Mapping
We try and support 4 attributes in the SAML claims:
- **id**
- **email**
- **firstName**
- **lastName**
This is how the common SAML attributes map over for most providers, but some providers have custom mappings. Please refer to the documentation on Identity Provider to understand the exact mapping.
| SAML Attribute | Jackson Mapping |
| ---------------------------------------------------------------------- | --------------- |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier` | id |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress` | email |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname` | firstName |
| `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname` | lastName |
<Footer />

View File

@ -0,0 +1,7 @@
## Step 3: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,15 @@
## Step 1: Create Application
From your Google Admin console, click **Apps** from the sidebar then click **Web and mobile apps** from the list.
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click **Add custom SAML app** from the menu.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/1.png)
Give your application an **App name** and click **Continue**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/2.png)
<Footer />

View File

@ -0,0 +1,22 @@
## Step 2: Configure Application
From the next screen, click **DOWNLOAD METADATA** to download the metadata XML file, then click **Continue**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/3.png)
Enter the following values in the **Service provider details** section:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS URL' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Entity ID' />
</div>
</div>
<AdvancedSPConfigLink />
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/4.png)
<Footer />

View File

@ -0,0 +1,23 @@
## Step 3: Attribute Mapping
Under the **Attributes** section, you have to configure the following attributes:
| App attributes | Google directory attributes |
| -------------- | --------------------------- |
| email | Primary email |
| firstName | First name |
| lastName | Last name |
After you have configured the attributes, click **Finish** to save the configuration.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/5.png)
From the next screen, click **User access** to configure the application to allow users to log in.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/6.png)
Check the **ON for everyone** checkbox and click **Save**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/google/7.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,15 @@
## Step 1: Create Application
From your JumpCloud Admin console, click **SSO** from the left navigation menu.
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click plus icon and then **Custom SAML App** to create a new application
![img alt](https://cdn.boxyhq.com/docs/sso/providers/jumpcloud/1.png)
Give your application a **Display Label**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/jumpcloud/2.png)
<Footer />

View File

@ -0,0 +1,21 @@
## Step 2: Configure Application
Next click on the **SSO** tab and enter the following values:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={idpEntityId} label='IdP Entity ID' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='SP Entity ID' />
</div>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS URL' />
</div>
</div>
<AdvancedSPConfigLink />
![img alt](https://cdn.boxyhq.com/docs/sso/providers/jumpcloud/3.png)
<Footer />

View File

@ -0,0 +1,21 @@
## Step 3: Attribute Mapping
Under the **Attributes** section, you have to configure the following attributes:
| Service Provider Attribute Name | JumpCloud Attribute Name |
| ------------------------------- | ------------------------ |
| email | email |
| firstName | firstname |
| lastName | lastname |
Make sure you have checked the **Declare Redirect Endpoint** checkbox.
Finally, click **Activate** to save the application configuration.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/jumpcloud/4.png)
Now go back to the SAML app you just created, click the tab **SSO**, and click the button **Export Metadata** to download the metadata XML file.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/jumpcloud/5.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,43 @@
## Step 1: Create a claims aware Relying Party Trust using federation metadata
In AD FS, a relying party (RP) trust needs to be set up. You can supply our SP metadata file and let AD FS automatically pick up the configuration.
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spMetadataUrl} label='Federation metadata URL' />
</div>
</div>
The public certificate is needed for Signature verification. You can download the certificate from the following URL:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={publicCertUrl} label='Public certificate' />
</div>
</div>
<AdvancedSPConfigLink />
In **Server Manager**, click **Tools**, and then select **AD FS Management**. Under **Actions**, click **Add Relying Party Trust**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/adfs/addtrust1.png)
On the Welcome page, choose **Claims aware** and click **Start**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/adfs/addtrust2.png)
On the **Select Data Source** page, click Import data about the relying party published online or on a local network.
In **Federation metadata address (host name or URL)**, type the federation metadata URL or host name for the partner, and then click **Next**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/adfs/addtrust12.png)
On the **Specify Display Name** page type a name in **Display name**, under Notes type a description for this relying party trust, and then click Next.
On the **Choose Issuance Authorization Rules** page, select either Permit all users to access this relying party or Deny all users access to this relying party, and then click Next.
On the **Ready to Add Trust** page, review the settings, and then click Next to save your relying party trust information.
On the Finish page, click Close. This action automatically displays the Edit Claim Rules dialog box.
<Footer />

View File

@ -0,0 +1,12 @@
## Step 2: Attribute Mapping
On the **Configure Claim Rule** screen, enter a **Claim Rule Name** of your choice, select **Active Directory** as the **Attribute Store**, then add the following mapping:
| LDAP Attribute | Outgoing Claim Type |
| ------------------- | ------------------- |
| E-Mail-Addresses | E-Mail Address |
| Given-Name | Given Name |
| Surname | Surname |
| User-Principal-Name | Name ID |
<Footer />

View File

@ -0,0 +1,19 @@
## Step 3: Transform Rule
Create a transform rule mapping the incoming **Email-Address** to outgoing **NameID** (of type **Email**), ADFS by default sends **NameID** as **Unspecified** which results in an **InvalidNameIDPolicy** error if this step is missed.
![Transform rule](https://cdn.boxyhq.com/docs/sso/providers/adfs/nameid-email.png)
If you'd rather use Claim rule language then the following rule can be applied:
```sh
c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] => issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
```
Finally open Windows PowerShell as an administrator, then run the following command:
```sh
Set-ADFSRelyingPartyTrust -TargetName <display-name> -SamlResponseSignature "MessageAndAssertion"
```
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,19 @@
## Step 1: Create Application
From your Okta account, click **Applications** from the left navigation menu.
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click the **Create App Integration** button to create a new application.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/1.png)
Choose **SAML 2.0** from the next screen and click **Next**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/2.png)
Give your application an **App Name** and click **Next**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/3.png)
<Footer />

View File

@ -0,0 +1,20 @@
## Step 2: Configure Application
Enter the following values in the **SAML Settings** section on the next screen:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='Single sign on URL' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Audience URI (SP Entity ID)' />
</div>
</div>
<AdvancedSPConfigLink />
Select **EmailAddress** from the **Name ID format** dropdown.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/4.png)
<Footer />

View File

@ -0,0 +1,26 @@
## Step 3: Attribute Mapping
Under the **Attribute Statements** section, you have to configure the following attributes:
| Name | Value |
| --------- | -------------- |
| id | user.id |
| email | user.email |
| firstName | user.firstName |
| lastName | user.lastName |
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/5.png)
On the next screen select **I'm an Okta customer adding an internal app** and click **Finish**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/6.png)
From your application, click **Sign On** tab and go to the section **SAML Signing Certificates**
Click the **Actions** dropdown for the correct certificate and click **View IdP metadata**.
A separate window will open with the metadata XML file, you can copy it to your clipboard.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/okta/7.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,19 @@
## Step 1: Create Application
From your OneLogin account, click **Applications** from the top navigation menu.
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click the **Add App** button to create a new application.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/1.png)
Search for **SAML Test Connector** in the **Find Applications** section. Select **SAML Custom Connector (Advanced)** from the search results.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/2.png)
Give your application a **Display Name** and click **Save**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/3.png)
<Footer />

View File

@ -0,0 +1,33 @@
## Step 2: Configure Application
From your application, click the **Configuration** tab on the left to configure the application.
You have to enter the following values in the **Application details** section:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Audience (Entity ID)' />
</div>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS (Consumer) URL Validator' />
</div>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS (Consumer) URL' />
</div>
</div>
<AdvancedSPConfigLink />
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/4.png)
Select the **Service Provider** from the **SAML initiator** dropdown.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/7.png)
Click **Save** to save the configuration.
Click the dropdown menu **More Actions** from the top right corner and click **SAML Metadata** to download the metadata XML file.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/5.png)
<Footer />

View File

@ -0,0 +1,28 @@
## Step 3: Attribute Mapping
From your application, click the **Parameters** tab on the left.
You have to configure the following attributes:
| SAML Custom Connector Field | Value |
| --------------------------- | ---------- |
| id | UUID |
| email | Email |
| firstName | First Name |
| lastName | Last Name |
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/6.png)
See the below screenshot to see how to map the **id** attribute to **UUID**.
Enter **id** in the **Field name** input and check the **Include in SAML assertion** checkbox. Click **Save** to continue.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/8.png)
On the next screen, select **UUID** from the **Value** dropdown and click **Save**.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/onelogin/9.png)
Do the same for the other attributes (email, firstName, lastName).
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,13 @@
## Step 1: Create Application
From your PingOne account, click **Connections** > **Applications** from left navigation menu.
If your application is already created, choose it from the list and move to the next step.
If you haven't created a SAML application, click plus button to create a new application.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/1.png)
Give your application a **Application Name**, choose **SAML Application** from the **Application Type** and click **Configure**.
<Footer />

View File

@ -0,0 +1,26 @@
## Step 2: Configure Application
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/2.png)
From the next screen, you have to enter the following values in the **SAML Configuration** section:
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS URLs' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Entity ID' />
</div>
</div>
<AdvancedSPConfigLink />
Click **Save** to save the configuration.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/3.png)
Click the **Configuration** tab from the top and click **Download Metadata** to download the metadata XML file.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/4.png)
<Footer />

View File

@ -0,0 +1,18 @@
## Step 3: Attribute Mapping
Click the **Attribute Mappings** tab from the top and you have to configure the following attributes:
| SAML App | PingOne |
| --------- | ------------- |
| id | User ID |
| email | Email Address |
| firstName | Given Name |
| lastName | Family Name |
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/5.png)
Make sure you have enabled your app so that it can be used by the users. You can do this by clicking the **Toggle** button next to your app.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/pingone/6.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 4: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -0,0 +1,18 @@
## Step 1: Create Application
You'll need to create a new **Custom App** from your Rippling account if the app is not already in the Rippling app directory.
Go to **IT Management** > **Custom App** from the left navigation menu.
Click **Create New App** button to create a new application.
From the next screen, fill in the following fields:
- **App Name**
- **Select Categories**
- **Upload Logo**
- **What type of app would you like to create?** - Make sure you select **Single Sign-On (SAML)** from the list.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/rippling/2.png)
<Footer />

View File

@ -0,0 +1,28 @@
## Step 2: Configure Application
Copy the **Idp Metadata URL** or **Idp Metadata XML** from the next screen. You'll need this value to configure your SAML connection in SAML Jackson.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/rippling/3.png)
You'll need to enter the **ACS URL** and **Service Provider Entity ID**.
<div className='flex flex-col gap-4'>
<div>
<InputWithCopyButton text={spConfig.acsUrl} label='ACS URL' />
</div>
<div>
<InputWithCopyButton text={spConfig.entityId} label='Service Provider Entity ID' />
</div>
</div>
<AdvancedSPConfigLink />
![img alt](https://cdn.boxyhq.com/docs/sso/providers/rippling/4.png)
From the **Settings** tab of custom app you just created. Go to **Advanced SAML Settings** section and check the box for **Disable InResponseTo field in assertions for IdP initiated SSO**.
This is because Rippling sends dummy values for the **InResponseTo** field and SAML Jackson will try to validate the **InResponseTo** field if it is present.
![img alt](https://cdn.boxyhq.com/docs/sso/providers/rippling/5.png)
<Footer />

View File

@ -0,0 +1,7 @@
## Step 3: Create SAML Connection
Enter the Identity Provider Metadata below. You can either enter the metadata URL or paste the XML file content directly.
<CreateSSOConnection setupLinkToken={setupLinkToken} idpType='saml' />
<Footer hasNext={false} />

View File

@ -1 +1,59 @@
export const sessionName = 'next-auth.saml-jackson';
export const identityProviders = [
{
name: 'Auth0 SAML SSO',
id: 'auth0',
stepCount: 3,
},
{
name: 'Azure SAML SSO',
id: 'azure',
stepCount: 4,
},
{
name: 'Google SAML SSO',
id: 'google',
stepCount: 4,
},
{
name: 'JumpCloud SAML SSO',
id: 'jumpcloud',
stepCount: 4,
},
{
name: 'Microsoft AD FS SAML SSO',
id: 'microsoft-adfs',
stepCount: 4,
},
{
name: 'Okta SAML SSO',
id: 'okta',
stepCount: 4,
},
{
name: 'OneLogin SAML SSO',
id: 'onelogin',
stepCount: 4,
},
{
name: 'PingOne SAML SSO',
id: 'pingone',
stepCount: 4,
},
{
name: 'Rippling SAML SSO',
id: 'rippling',
stepCount: 3,
},
{
name: 'Generic SAML 2.0',
id: 'generic-saml',
stepCount: 3,
},
{
name: 'OIDC Provider',
id: 'generic-oidc',
stepCount: 2,
},
];

View File

@ -1,11 +1,18 @@
import useSWR from 'swr';
import type { ApiError, ApiSuccess } from 'types';
import { useRouter } from 'next/router';
import { fetcher } from '@lib/ui/utils';
import type { ApiError, ApiSuccess } from 'types';
const useIdpEntityID = (setupLinkToken: string) => {
const url = setupLinkToken ? `/api/setup/${setupLinkToken}/sso-connection/idp-entityid` : null;
const useIdpEntityID = (setupLinkToken?: string) => {
const { query, isReady } = useRouter();
const { data, error, isLoading } = useSWR<ApiSuccess<{ idpEntityID: string }>, ApiError>(url, fetcher);
const token = setupLinkToken || (isReady ? query.token : null);
const { data, error, isLoading } = useSWR<ApiSuccess<{ idpEntityID: string }>, ApiError>(
token ? `/api/setup/${token}/sso-connection/idp-entityid` : null,
fetcher
);
return {
idpEntityID: data?.data.idpEntityID,

12524
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -64,10 +64,12 @@
"cors": "2.8.5",
"daisyui": "3.7.3",
"i18next": "22.5.1",
"medium-zoom": "1.0.8",
"micromatch": "4.0.5",
"next": "13.4.19",
"next-auth": "4.23.1",
"next-i18next": "13.3.0",
"next-mdx-remote": "4.4.1",
"nodemailer": "6.9.5",
"raw-body": "2.5.2",
"react": "18.2.0",
@ -75,6 +77,7 @@
"react-dom": "18.2.0",
"react-i18next": "12.3.1",
"react-syntax-highlighter": "15.5.0",
"remark-gfm": "3.0.1",
"sharp": "0.32.5",
"swr": "2.2.2"
},

View File

@ -6,11 +6,10 @@ import useIdpEntityID from '@lib/ui/hooks/useIdpEntityID';
const ConnectionsIndexPage: NextPage = () => {
const router = useRouter();
const { idpEntityID } = useIdpEntityID();
const { token } = router.query as { token: string };
const { idpEntityID } = useIdpEntityID(token);
return <ConnectionList setupLinkToken={token} idpEntityID={idpEntityID} />;
};

View File

@ -1,25 +1,186 @@
import type { NextPage } from 'next';
import CreateConnection from '@components/connection/CreateConnection';
import { useRouter } from 'next/router';
import fs from 'fs';
import path from 'path';
import Link from 'next/link';
import { useEffect } from 'react';
import remarkGfm from 'remark-gfm';
import mediumZoom from 'medium-zoom';
import { MDXRemote } from 'next-mdx-remote';
import { serialize } from 'next-mdx-remote/serialize';
import type { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
import { ArrowsRightLeftIcon } from '@heroicons/react/24/outline';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import useIdpEntityID from '@lib/ui/hooks/useIdpEntityID';
const ConnectionCreatePage: NextPage = () => {
const router = useRouter();
import jackson from '@lib/jackson';
import { jacksonOptions } from '@lib/env';
import { identityProviders } from '@lib/constants';
import Footer from '@components/setup-link-instructions/Footer';
import NextButton from '@components/setup-link-instructions/NextButton';
import { InputWithCopyButton } from '@components/ClipboardButton';
import PreviousButton from '@components/setup-link-instructions/PreviousButton';
import CreateSSOConnection from '@components/setup-link-instructions/CreateSSOConnection';
import SelectIdentityProviders from '@components/setup-link-instructions/SelectIdentityProviders';
const { token } = router.query as { token: string };
type NewConnectionProps = InferGetServerSidePropsType<typeof getServerSideProps>;
const { idpEntityID } = useIdpEntityID(token);
return <CreateConnection setupLinkToken={token} idpEntityID={idpEntityID} />;
const AdvancedSPConfigLink = () => {
return (
<div className='py-2'>
<Link href='/.well-known/saml-configuration' target='_blank' className='underline-offset-4'>
<span className='text-xs'>Advanced Service Provider (SP) SAML Configuration</span>
</Link>
</div>
);
};
export async function getServerSideProps({ locale }) {
const components = {
Footer,
NextButton,
PreviousButton,
InputWithCopyButton,
CreateSSOConnection,
AdvancedSPConfigLink,
};
const proseClassNames = [
'prose',
'prose-sm',
'prose-h2:text-lg',
'prose-h2:text-slate-700',
'prose-h2:font-semibold',
'prose-p:text-base',
'prose-img:rounded',
'prose-img:h-96',
'prose-img:w-full',
'prose-table:table',
'prose-table:border',
'prose-a:text-sm',
];
const findIdp = (idp: string) => {
return identityProviders.find((provider) => provider.id === idp);
};
const NewConnection = ({
step,
idp,
setupLinkToken,
idpEntityId,
source,
spConfig,
spMetadataUrl,
publicCertUrl,
oidcCallbackUrl,
}: NewConnectionProps) => {
const linkSelectIdp = { pathname: '/setup/[token]/sso-connection/new', query: { token: setupLinkToken } };
const scope = {
idpEntityId,
spConfig,
setupLinkToken,
spMetadataUrl,
publicCertUrl,
oidcCallbackUrl,
};
let heading = '';
let progress = 0;
// Add zoom to images
useEffect(() => {
if (typeof window === 'undefined') return;
const zoom = mediumZoom('img', {
scrollOffset: 0,
margin: 50,
});
return () => {
zoom.detach();
};
}, [step]);
if (source && idp && step) {
const selectedIdP = findIdp(idp);
if (!selectedIdP) {
throw new Error('Identity Provider not found');
}
progress = (100 / selectedIdP.stepCount) * parseInt(step);
heading = `Configure ${selectedIdP?.name}`;
} else {
heading = 'Select Identity Provider';
}
return (
<>
<div className='flex space-y-4 flex-col pb-6'>
<div className='flex justify-between items-center'>
<h1 className='text-xl font-noraml'>{heading}</h1>
{source && (
<Link className='btn btn-xs h-0' href={linkSelectIdp}>
<ArrowsRightLeftIcon className='w-5 h-5' />
Change Identity Provider
</Link>
)}
</div>
{source && <progress className='progress progress-primary w-full' value={progress} max={100} />}
</div>
<article className={`${proseClassNames.join(' ')} max-w-4xl`}>
{source ? (
<MDXRemote {...source} components={components} scope={scope} />
) : (
<SelectIdentityProviders />
)}
</article>
</>
);
};
export async function getServerSideProps({ locale, query }: GetServerSidePropsContext) {
const { spConfig, setupLinkController, connectionAPIController } = await jackson();
const { idp, step, token } = query as { idp: string; step: string; token: string };
const { tenant, product } = await setupLinkController.getByToken(token);
const idpEntityId = connectionAPIController.getIDPEntityID({
tenant,
product,
});
let mdxSource: MDXRemoteSerializeResult<Record<string, unknown>> | null = null;
// Read the MDX file based on the idp and step
if (idp && step) {
try {
const mdxDirectory = path.join(process.cwd(), `components/setup-link-instructions/${idp}`);
const source = fs.readFileSync(`${mdxDirectory}/${step}.mdx`, 'utf8');
mdxSource = await serialize(source, { mdxOptions: { remarkPlugins: [remarkGfm] } });
} catch (error: any) {
return {
redirect: {
destination: `/setup/${token}/sso-connection/new`,
},
};
}
}
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
idp: idp || null,
step: step || null,
setupLinkToken: token,
idpEntityId,
source: mdxSource,
spConfig: await spConfig.get(),
spMetadataUrl: `${jacksonOptions.externalUrl}/.well-known/sp-metadata`,
publicCertUrl: `${jacksonOptions.externalUrl}/.well-known/saml.cer`,
oidcCallbackUrl: `${jacksonOptions.externalUrl}${jacksonOptions.oidcPath}`,
...(locale ? await serverSideTranslations(locale, ['common']) : {}),
},
};
}
export default ConnectionCreatePage;
export default NewConnection;