Skip to content

Commit afb75e6

Browse files
authored
feat(ui): Add Google Workspace SAML to self-serve SSO (#8690)
1 parent e2b6a9f commit afb75e6

18 files changed

Lines changed: 1553 additions & 548 deletions

.changeset/beige-breads-bathe.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': patch
3+
'@clerk/shared': patch
4+
'@clerk/ui': patch
5+
---
6+
7+
Add support for Google Workspace SAML provider to self-serve SSO

packages/localizations/src/en-US.ts

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ export const enUS: LocalizationResource = {
242242
groupLabel: 'SAML',
243243
okta: 'Okta Workforce',
244244
customSaml: 'Custom SAML Provider',
245+
google: 'Google Workspace',
245246
},
246247
warning: 'Once a provider is selected you cannot change again until the configuration is over',
247248
},
@@ -359,11 +360,11 @@ export const enUS: LocalizationResource = {
359360
step5: 'Click <bold>Next</bold> to complete creating the application.',
360361
},
361362
serviceProviderInstructions: {
362-
title: 'Configure service provider',
363+
title: 'Add service provider configuration to Okta',
363364
paragraph1:
364365
'Once you have moved forward from the General Settings instructions, you will be presented with the Configure SAML page.',
365366
paragraph2:
366-
'To configure your service provider (Clerk), you must add these two fields to your Okta application:',
367+
'To configure your service provider, you must add these two fields to your Okta SAML application:',
367368
serviceProviderFields: {
368369
acsUrl: {
369370
label: 'Single sign-on URL',
@@ -387,7 +388,7 @@ export const enUS: LocalizationResource = {
387388
step2: 'Select <bold>Add Expression</bold> for each row below, then enter the matching name and value:',
388389
attributeMappingTable: {
389390
columns: {
390-
name: 'Name',
391+
name: 'Attribute name',
391392
expression: 'Expression',
392393
},
393394
rows: {
@@ -516,6 +517,104 @@ export const enUS: LocalizationResource = {
516517
},
517518
},
518519
},
520+
samlGoogle: {
521+
mainHeaderTitle: 'Configure Google Workspace',
522+
createAppStep: {
523+
headerSubtitle: 'Create a new enterprise application in your Google Workspace',
524+
createAppInstructions: {
525+
title: 'Create a new enterprise application in Google Workspace',
526+
step1: 'Sign in to Google Admin Portal.',
527+
step2: 'In the side navigation, under <bold>Apps</bold>, select <bold>Web and mobile apps.</bold>',
528+
step3: 'Click on the <bold>Add</bold> app button, and select <bold>Add custom SAML app.</bold>',
529+
step4: 'In the <bold>App details</bold> section, fill out the required <bold>App name</bold>.',
530+
step5: 'Select the <bold>Continue</bold> button.',
531+
},
532+
},
533+
identityProviderMetadataStep: {
534+
headerSubtitle: 'Configure identity provider metadata',
535+
modes: {
536+
title: 'Fill in your Google Workspace application details',
537+
ariaLabel: 'Configuration ',
538+
metadataFile: 'Add via metadata',
539+
manual: 'Configure manually',
540+
},
541+
metadataFile: {
542+
label: 'IdP metadata',
543+
description: 'In your Google Workspace application, download the IdP metadata and upload it below.',
544+
uploadFile: 'Upload file',
545+
replaceFile: 'Replace file',
546+
removeFile: 'Remove file',
547+
fileUploaded: 'File uploaded',
548+
},
549+
manual: {
550+
description: 'In your Google Workspace application, retrieve these values.',
551+
signOnUrl: {
552+
label: 'SSO URL',
553+
placeholder: 'Paste URL here...',
554+
},
555+
issuer: {
556+
label: 'Entity ID',
557+
placeholder: 'Paste URL here...',
558+
},
559+
signingCertificate: {
560+
label: 'Signing certificate',
561+
uploadFile: 'Upload file',
562+
replaceFile: 'Replace file',
563+
removeFile: 'Remove file',
564+
fileUploaded: 'File uploaded',
565+
},
566+
},
567+
},
568+
serviceProviderStep: {
569+
headerSubtitle: 'Configure service provider',
570+
title: 'Configure service provider',
571+
paragraph:
572+
'To configure your service provider, you must add these two fields to your Google Workspace SAML application:',
573+
serviceProviderFields: {
574+
acsUrl: {
575+
label: 'ACS URL',
576+
},
577+
spEntityId: {
578+
label: 'Entity ID',
579+
},
580+
},
581+
nameIdInstructions: {
582+
step1:
583+
'Under the <bold>Name ID</bold> section, select the <bold>Name ID</bold> format dropdown and select <bold>Email</bold>.',
584+
step2: 'Select <bold>Continue</bold>',
585+
},
586+
},
587+
attributeMappingStep: {
588+
headerSubtitle: 'Map user attributes from Google Workspace to your application',
589+
paragraph: 'We expect your SAML response to return the user’s email, first name and last name.',
590+
step1: 'In the <bold>Google Admin Console</bold>, find the <bold>Attributes</bold> section.',
591+
step2:
592+
'Select <bold>Add mapping</bold> for each attribute, and enter the following Google and app attribute:',
593+
attributeMappingTable: {
594+
columns: {
595+
googleAttribute: 'Google attribute',
596+
appAttribute: 'App attribute',
597+
},
598+
rows: {
599+
email: { googleAttribute: 'Primary email', appAttribute: 'email' },
600+
firstName: { googleAttribute: 'First name', appAttribute: 'firstName' },
601+
lastName: { googleAttribute: 'Last name', appAttribute: 'lastName' },
602+
},
603+
},
604+
},
605+
configureUserAccess: {
606+
headerSubtitle: 'Enable your Google Workspace SAML application',
607+
assignUsersInstructions: {
608+
paragraph1:
609+
"Once the configuration is complete in Google, you'll be redirected to the app's overview page.",
610+
step1: 'Open the <bold>User access</bold> section.',
611+
step2: 'Select <bold>ON for everyone.</bold>',
612+
step3: 'Select <bold>Save</bold>.',
613+
paragraph2:
614+
'Google may take up to 24 hours to propagate these changes. The connection will remain inactive until they take effect.',
615+
},
616+
},
617+
},
519618
},
520619
},
521620
createOrganization: {

packages/shared/src/react/hooks/useEnterpriseConnectionTestRuns.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ function useEnterpriseConnectionTestRuns(
104104
},
105105
enabled: queryEnabled,
106106
refetchIntervalInBackground: false,
107+
refetchOnWindowFocus: false,
107108
});
108109

109110
const hasRows = (query.data?.data?.length ?? 0) > 0;

packages/shared/src/types/elementIds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export type FieldId =
2828
| 'apiKeySecret'
2929
| 'idpCertificate'
3030
| 'idpEntityId'
31+
| 'idpMetadata'
3132
| 'idpMetadataUrl'
3233
| 'idpSsoUrl'
3334
| 'acsUrl'

packages/shared/src/types/localization.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ export type __internal_LocalizationResource = {
13081308
groupLabel: LocalizationValue;
13091309
okta: LocalizationValue;
13101310
customSaml: LocalizationValue;
1311+
google: LocalizationValue;
13111312
};
13121313
warning: LocalizationValue;
13131314
};
@@ -1570,6 +1571,99 @@ export type __internal_LocalizationResource = {
15701571
};
15711572
};
15721573
};
1574+
samlGoogle: {
1575+
mainHeaderTitle: LocalizationValue;
1576+
createAppStep: {
1577+
headerSubtitle: LocalizationValue;
1578+
createAppInstructions: {
1579+
title: LocalizationValue;
1580+
step1: LocalizationValue;
1581+
step2: LocalizationValue;
1582+
step3: LocalizationValue;
1583+
step4: LocalizationValue;
1584+
step5: LocalizationValue;
1585+
};
1586+
};
1587+
identityProviderMetadataStep: {
1588+
headerSubtitle: LocalizationValue;
1589+
modes: {
1590+
title: LocalizationValue;
1591+
ariaLabel: LocalizationValue;
1592+
metadataFile: LocalizationValue;
1593+
manual: LocalizationValue;
1594+
};
1595+
metadataFile: {
1596+
label: LocalizationValue;
1597+
description: LocalizationValue;
1598+
uploadFile: LocalizationValue;
1599+
replaceFile: LocalizationValue;
1600+
removeFile: LocalizationValue;
1601+
fileUploaded: LocalizationValue;
1602+
};
1603+
manual: {
1604+
description: LocalizationValue;
1605+
signOnUrl: {
1606+
label: LocalizationValue;
1607+
placeholder: LocalizationValue;
1608+
};
1609+
issuer: {
1610+
label: LocalizationValue;
1611+
placeholder: LocalizationValue;
1612+
};
1613+
signingCertificate: {
1614+
label: LocalizationValue;
1615+
uploadFile: LocalizationValue;
1616+
replaceFile: LocalizationValue;
1617+
removeFile: LocalizationValue;
1618+
fileUploaded: LocalizationValue;
1619+
};
1620+
};
1621+
};
1622+
serviceProviderStep: {
1623+
headerSubtitle: LocalizationValue;
1624+
title: LocalizationValue;
1625+
paragraph: LocalizationValue;
1626+
serviceProviderFields: {
1627+
acsUrl: {
1628+
label: LocalizationValue;
1629+
};
1630+
spEntityId: {
1631+
label: LocalizationValue;
1632+
};
1633+
};
1634+
nameIdInstructions: {
1635+
step1: LocalizationValue;
1636+
step2: LocalizationValue;
1637+
};
1638+
};
1639+
attributeMappingStep: {
1640+
headerSubtitle: LocalizationValue;
1641+
paragraph: LocalizationValue;
1642+
step1: LocalizationValue;
1643+
step2: LocalizationValue;
1644+
attributeMappingTable: {
1645+
columns: {
1646+
googleAttribute: LocalizationValue;
1647+
appAttribute: LocalizationValue;
1648+
};
1649+
rows: {
1650+
email: { googleAttribute: LocalizationValue; appAttribute: LocalizationValue };
1651+
firstName: { googleAttribute: LocalizationValue; appAttribute: LocalizationValue };
1652+
lastName: { googleAttribute: LocalizationValue; appAttribute: LocalizationValue };
1653+
};
1654+
};
1655+
};
1656+
configureUserAccess: {
1657+
headerSubtitle: LocalizationValue;
1658+
assignUsersInstructions: {
1659+
paragraph1: LocalizationValue;
1660+
step1: LocalizationValue;
1661+
step2: LocalizationValue;
1662+
step3: LocalizationValue;
1663+
paragraph2: LocalizationValue;
1664+
};
1665+
};
1666+
};
15731667
};
15741668
confirmation: {
15751669
statusSection: {

packages/ui/src/components/ConfigureSSO/elements/Stepper/Stepper.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const Item = ({
9595
fontSize: theme.fontSizes.$xs,
9696
fontWeight: theme.fontWeights.$medium,
9797
color: theme.colors.$colorBackground,
98+
lineHeight: '1rem',
9899
})}
99100
>
100101
{bullet}

packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import { useConfigureSSO } from '../../ConfigureSSOContext';
66
import { Step } from '../../elements/Step';
77
import { Wizard } from '../../elements/Wizard';
88
import type { ProviderType } from '../../types';
9-
import { SamlCustomConfigureSteps } from './saml/SamlCustomConfigureSteps';
10-
import { SamlOktaConfigureSteps } from './saml/SamlOktaConfigureSteps';
9+
import { SamlCustomConfigureSteps, SamlGoogleConfigureSteps, SamlOktaConfigureSteps } from './saml';
1110

1211
const STEPS_BY_PROVIDER: Record<ProviderType, () => JSX.Element> = {
1312
saml_custom: SamlCustomConfigureSteps,
1413
saml_okta: SamlOktaConfigureSteps,
14+
saml_google: SamlGoogleConfigureSteps,
1515
};
1616

1717
export const ConfigureStep = (): JSX.Element | null => {

0 commit comments

Comments
 (0)