Skip to content

Commit 35c3b4e

Browse files
committed
Implement attribute mapping step
1 parent 770bd07 commit 35c3b4e

4 files changed

Lines changed: 307 additions & 65 deletions

File tree

packages/localizations/src/en-US.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,39 @@ export const enUS: LocalizationResource = {
703703
},
704704
},
705705
},
706+
attributeMappingStep: {
707+
headerSubtitle: 'Map user attributes from Microsoft Entra to your application',
708+
title: 'We expect your SAML responses to have the following specific attributes:',
709+
paragraph:
710+
"These are the defaults and probably won't need you to change them. However, many SAML configuration errors are due to incorrect attribute mappings, so it's worth double-checking. Here's how:",
711+
step1: 'On the <bold>SAML-based Sign-on</bold> page, find the <bold>Attributes & Claims</bold> section.',
712+
step2: 'Select <bold>Edit</bold>',
713+
step3: 'Verify that the above three attributes and values are present.',
714+
attributeMappingTable: {
715+
columns: {
716+
attribute: 'Attribute',
717+
claimName: 'Claim name',
718+
value: 'Value',
719+
},
720+
rows: {
721+
email: {
722+
attribute: 'Email address',
723+
claimName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
724+
value: 'user.mail',
725+
},
726+
firstName: {
727+
attribute: 'First name',
728+
claimName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
729+
value: 'user.givenname',
730+
},
731+
lastName: {
732+
attribute: 'Last name',
733+
claimName: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
734+
value: 'user.surname',
735+
},
736+
},
737+
},
738+
},
706739
},
707740
},
708741
},

packages/shared/src/types/localization.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,26 @@ export type __internal_LocalizationResource = {
17441744
};
17451745
};
17461746
};
1747+
attributeMappingStep: {
1748+
headerSubtitle: LocalizationValue;
1749+
title: LocalizationValue;
1750+
paragraph: LocalizationValue;
1751+
step1: LocalizationValue;
1752+
step2: LocalizationValue;
1753+
step3: LocalizationValue;
1754+
attributeMappingTable: {
1755+
columns: {
1756+
attribute: LocalizationValue;
1757+
claimName: LocalizationValue;
1758+
value: LocalizationValue;
1759+
};
1760+
rows: {
1761+
email: { attribute: LocalizationValue; claimName: LocalizationValue; value: LocalizationValue };
1762+
firstName: { attribute: LocalizationValue; claimName: LocalizationValue; value: LocalizationValue };
1763+
lastName: { attribute: LocalizationValue; claimName: LocalizationValue; value: LocalizationValue };
1764+
};
1765+
};
1766+
};
17471767
};
17481768
};
17491769
confirmation: {

packages/ui/src/components/ConfigureSSO/steps/ConfigureStep/saml/SamlMicrosoftConfigureSteps.tsx

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { useConfigureSSO } from '../../../ConfigureSSOContext';
1111
import { Step } from '../../../elements/Step';
1212
import { useWizard, Wizard } from '../../../elements/Wizard';
1313
import { InnerStepCounter } from '../../../elements/Wizard/InnerStepCounter';
14+
import { AttributeMappingTable, type AttributeMappingTableConfig } from './shared/AttributeMappingTable';
1415
import {
1516
applySamlSubmitError,
1617
buildSamlConfigurationPayload,
@@ -45,6 +46,16 @@ export const SamlMicrosoftConfigureSteps = (): JSX.Element => {
4546
<SamlMicrosoftServiceProviderStep />
4647
</Wizard.Step>
4748

49+
<Wizard.Step id='attribute-mapping'>
50+
<Step.Header
51+
title={localizationKeys('configureSSO.configureStep.samlMicrosoft.mainHeaderTitle')}
52+
description={localizationKeys('configureSSO.configureStep.samlMicrosoft.attributeMappingStep.headerSubtitle')}
53+
>
54+
<InnerStepCounter />
55+
</Step.Header>
56+
<SamlMicrosoftAttributeMappingStep />
57+
</Wizard.Step>
58+
4859
<Wizard.Step id='identity-provider-metadata'>
4960
<Step.Header
5061
title={localizationKeys('configureSSO.configureStep.samlMicrosoft.mainHeaderTitle')}
@@ -394,6 +405,134 @@ const SamlMicrosoftServiceProviderStep = (): JSX.Element => {
394405
);
395406
};
396407

408+
const MICROSOFT_ATTRIBUTE_MAPPING: AttributeMappingTableConfig = {
409+
columns: {
410+
first: localizationKeys(
411+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.columns.attribute',
412+
),
413+
second: localizationKeys(
414+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.columns.claimName',
415+
),
416+
third: localizationKeys(
417+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.columns.value',
418+
),
419+
},
420+
// Microsoft's default claim names are long URLs that don't fit the column;
421+
// truncate them with an ellipsis and surface the full value in a tooltip.
422+
truncateSecond: true,
423+
rows: [
424+
{
425+
id: 'email',
426+
isRequired: true,
427+
first: localizationKeys(
428+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.email.attribute',
429+
),
430+
second: localizationKeys(
431+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.email.claimName',
432+
),
433+
third: localizationKeys(
434+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.email.value',
435+
),
436+
},
437+
{
438+
id: 'firstName',
439+
isRequired: false,
440+
first: localizationKeys(
441+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.firstName.attribute',
442+
),
443+
second: localizationKeys(
444+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.firstName.claimName',
445+
),
446+
third: localizationKeys(
447+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.firstName.value',
448+
),
449+
},
450+
{
451+
id: 'lastName',
452+
isRequired: false,
453+
first: localizationKeys(
454+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.lastName.attribute',
455+
),
456+
second: localizationKeys(
457+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.lastName.claimName',
458+
),
459+
third: localizationKeys(
460+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.attributeMappingTable.rows.lastName.value',
461+
),
462+
},
463+
],
464+
};
465+
466+
const SamlMicrosoftAttributeMappingStep = (): JSX.Element => {
467+
const { goNext, goPrev, isFirstStep, isLastStep } = useWizard();
468+
469+
return (
470+
<>
471+
<Step.Body>
472+
<Step.Section sx={theme => ({ gap: theme.space.$3 })}>
473+
<Heading
474+
elementDescriptor={descriptors.configureSSOInstructionsHeading}
475+
as='h3'
476+
textVariant='subtitle'
477+
localizationKey={localizationKeys('configureSSO.configureStep.samlMicrosoft.attributeMappingStep.title')}
478+
/>
479+
480+
<AttributeMappingTable config={MICROSOFT_ATTRIBUTE_MAPPING} />
481+
482+
<Text
483+
as='p'
484+
colorScheme='secondary'
485+
localizationKey={localizationKeys(
486+
'configureSSO.configureStep.samlMicrosoft.attributeMappingStep.paragraph',
487+
)}
488+
/>
489+
490+
<Col
491+
elementDescriptor={descriptors.configureSSOInstructionsList}
492+
as='ul'
493+
sx={theme => ({
494+
gap: theme.space.$1x5,
495+
margin: 0,
496+
paddingInlineStart: theme.space.$5,
497+
listStyleType: 'disc',
498+
})}
499+
>
500+
<Text
501+
elementDescriptor={descriptors.configureSSOInstructionsListItem}
502+
as='li'
503+
colorScheme='secondary'
504+
localizationKey={localizationKeys('configureSSO.configureStep.samlMicrosoft.attributeMappingStep.step1')}
505+
/>
506+
<Text
507+
elementDescriptor={descriptors.configureSSOInstructionsListItem}
508+
as='li'
509+
colorScheme='secondary'
510+
localizationKey={localizationKeys('configureSSO.configureStep.samlMicrosoft.attributeMappingStep.step2')}
511+
/>
512+
<Text
513+
elementDescriptor={descriptors.configureSSOInstructionsListItem}
514+
as='li'
515+
colorScheme='secondary'
516+
localizationKey={localizationKeys('configureSSO.configureStep.samlMicrosoft.attributeMappingStep.step3')}
517+
/>
518+
</Col>
519+
</Step.Section>
520+
</Step.Body>
521+
522+
<Step.Footer>
523+
<Step.Footer.Previous
524+
onClick={() => goPrev()}
525+
isDisabled={isFirstStep}
526+
/>
527+
<Step.Footer.Continue
528+
onClick={() => goNext()}
529+
isDisabled={isLastStep}
530+
/>
531+
</Step.Footer>
532+
</>
533+
);
534+
};
535+
397536
const MICROSOFT_SAML_IDP_MODES = ['metadataUrl', 'manual'] as const satisfies readonly IdpConfigurationMode[];
398537

399538
const SamlMicrosoftIdentityProviderMetadataStep = (): JSX.Element => {

0 commit comments

Comments
 (0)