Skip to content

Commit 48ca0e7

Browse files
authored
fix: automatically map email attributes for social providers and OIDC (#931)
* fix: automatically map email attribute * chore: add changeset * fix: only map email if ONLY email is used * chore: update tests to check for auto mapped attributes
1 parent fb07baf commit 48ca0e7

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed

.changeset/weak-geese-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@aws-amplify/auth-construct-alpha': patch
3+
---
4+
5+
Automatically map email attributes for social providers.

packages/auth-construct/src/construct.test.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,163 @@ void describe('Auth construct', () => {
16551655
},
16561656
});
16571657
});
1658+
1659+
void it('automatically maps email attributes for external providers excluding SAML', () => {
1660+
const app = new App();
1661+
const stack = new Stack(app);
1662+
new AmplifyAuth(stack, 'test', {
1663+
loginWith: {
1664+
email: true,
1665+
externalProviders: {
1666+
google: {
1667+
clientId: googleClientId,
1668+
clientSecret: SecretValue.unsafePlainText(googleClientSecret),
1669+
},
1670+
facebook: {
1671+
clientId: facebookClientId,
1672+
clientSecret: facebookClientSecret,
1673+
},
1674+
signInWithApple: {
1675+
clientId: appleClientId,
1676+
keyId: appleKeyId,
1677+
privateKey: applePrivateKey,
1678+
teamId: appleTeamId,
1679+
},
1680+
loginWithAmazon: {
1681+
clientId: amazonClientId,
1682+
clientSecret: amazonClientSecret,
1683+
},
1684+
oidc: {
1685+
clientId: oidcClientId,
1686+
clientSecret: oidcClientSecret,
1687+
issuerUrl: oidcIssuerUrl,
1688+
name: oidcProviderName,
1689+
},
1690+
callbackUrls: ['https://redirect.com'],
1691+
logoutUrls: ['https://logout.com'],
1692+
},
1693+
},
1694+
});
1695+
const template = Template.fromStack(stack);
1696+
template.hasResourceProperties('AWS::Cognito::UserPool', {
1697+
UsernameAttributes: ['email'],
1698+
AutoVerifiedAttributes: ['email'],
1699+
});
1700+
const expectedAutoMappedAttributes = {
1701+
AttributeMapping: {
1702+
// 'email' is a standardized claim for oauth and oidc IDPS
1703+
// so we can map it to cognito's 'email' claim
1704+
email: 'email',
1705+
},
1706+
};
1707+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1708+
...ExpectedAmazonIDPProperties,
1709+
...expectedAutoMappedAttributes,
1710+
});
1711+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1712+
...ExpectedAppleIDPProperties,
1713+
...expectedAutoMappedAttributes,
1714+
});
1715+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1716+
...ExpectedFacebookIDPProperties,
1717+
...expectedAutoMappedAttributes,
1718+
});
1719+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1720+
...ExpectedGoogleIDPProperties,
1721+
...expectedAutoMappedAttributes,
1722+
});
1723+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1724+
...ExpectedOidcIDPProperties,
1725+
...expectedAutoMappedAttributes,
1726+
});
1727+
template.hasResourceProperties('AWS::Cognito::IdentityPool', {
1728+
SupportedLoginProviders: {
1729+
'www.amazon.com': amazonClientId,
1730+
'accounts.google.com': googleClientId,
1731+
'appleid.apple.com': appleClientId,
1732+
'graph.facebook.com': facebookClientId,
1733+
},
1734+
});
1735+
});
1736+
});
1737+
1738+
void it('does not automatically map email attributes if phone is also enabled', () => {
1739+
const app = new App();
1740+
const stack = new Stack(app);
1741+
new AmplifyAuth(stack, 'test', {
1742+
loginWith: {
1743+
email: true,
1744+
phone: true, // this makes phone_number a required attribute
1745+
externalProviders: {
1746+
google: {
1747+
clientId: googleClientId,
1748+
clientSecret: SecretValue.unsafePlainText(googleClientSecret),
1749+
},
1750+
facebook: {
1751+
clientId: facebookClientId,
1752+
clientSecret: facebookClientSecret,
1753+
},
1754+
signInWithApple: {
1755+
clientId: appleClientId,
1756+
keyId: appleKeyId,
1757+
privateKey: applePrivateKey,
1758+
teamId: appleTeamId,
1759+
},
1760+
loginWithAmazon: {
1761+
clientId: amazonClientId,
1762+
clientSecret: amazonClientSecret,
1763+
},
1764+
oidc: {
1765+
clientId: oidcClientId,
1766+
clientSecret: oidcClientSecret,
1767+
issuerUrl: oidcIssuerUrl,
1768+
name: oidcProviderName,
1769+
},
1770+
callbackUrls: ['https://redirect.com'],
1771+
logoutUrls: ['https://logout.com'],
1772+
},
1773+
},
1774+
});
1775+
const template = Template.fromStack(stack);
1776+
template.hasResourceProperties('AWS::Cognito::UserPool', {
1777+
UsernameAttributes: ['email', 'phone_number'],
1778+
AutoVerifiedAttributes: ['email', 'phone_number'],
1779+
});
1780+
const mappingThatShouldNotExist = {
1781+
AttributeMapping: {
1782+
email: 'email',
1783+
},
1784+
};
1785+
assert.throws(() => {
1786+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1787+
...ExpectedAmazonIDPProperties,
1788+
...mappingThatShouldNotExist,
1789+
});
1790+
});
1791+
assert.throws(() => {
1792+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1793+
...ExpectedAppleIDPProperties,
1794+
...mappingThatShouldNotExist,
1795+
});
1796+
});
1797+
assert.throws(() => {
1798+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1799+
...ExpectedFacebookIDPProperties,
1800+
...mappingThatShouldNotExist,
1801+
});
1802+
});
1803+
assert.throws(() => {
1804+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1805+
...ExpectedGoogleIDPProperties,
1806+
...mappingThatShouldNotExist,
1807+
});
1808+
});
1809+
assert.throws(() => {
1810+
template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', {
1811+
...ExpectedOidcIDPProperties,
1812+
...mappingThatShouldNotExist,
1813+
});
1814+
});
16581815
});
16591816

16601817
void describe('addTrigger', () => {

packages/auth-construct/src/construct.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@ export class AmplifyAuth
584584
userPool: UserPool,
585585
loginOptions: AuthProps['loginWith']
586586
): IdentityProviderSetupResult => {
587+
/**
588+
* If email is enabled, and is the only required attribute, we are able to
589+
* automatically map the email attribute from external providers, excluding SAML.
590+
*/
591+
const shouldMapEmailAttributes = loginOptions.email && !loginOptions.phone;
587592
const result: IdentityProviderSetupResult = {
588593
oauthMappings: {},
589594
providersList: [],
@@ -602,7 +607,14 @@ export class AmplifyAuth
602607
userPool,
603608
clientId: googleProps.clientId,
604609
clientSecretValue: googleProps.clientSecret,
605-
attributeMapping: googleProps.attributeMapping,
610+
attributeMapping:
611+
googleProps.attributeMapping ?? shouldMapEmailAttributes
612+
? {
613+
email: {
614+
attributeName: 'email',
615+
},
616+
}
617+
: undefined,
606618
scopes: googleProps.scopes,
607619
}
608620
);
@@ -616,6 +628,14 @@ export class AmplifyAuth
616628
{
617629
userPool,
618630
...external.facebook,
631+
attributeMapping:
632+
external.facebook.attributeMapping ?? shouldMapEmailAttributes
633+
? {
634+
email: {
635+
attributeName: 'email',
636+
},
637+
}
638+
: undefined,
619639
}
620640
);
621641
result.oauthMappings[authProvidersList.facebook] =
@@ -629,6 +649,15 @@ export class AmplifyAuth
629649
{
630650
userPool,
631651
...external.loginWithAmazon,
652+
attributeMapping:
653+
external.loginWithAmazon.attributeMapping ??
654+
shouldMapEmailAttributes
655+
? {
656+
email: {
657+
attributeName: 'email',
658+
},
659+
}
660+
: undefined,
632661
}
633662
);
634663
result.oauthMappings[authProvidersList.amazon] =
@@ -642,6 +671,15 @@ export class AmplifyAuth
642671
{
643672
userPool,
644673
...external.signInWithApple,
674+
attributeMapping:
675+
external.signInWithApple.attributeMapping ??
676+
shouldMapEmailAttributes
677+
? {
678+
email: {
679+
attributeName: 'email',
680+
},
681+
}
682+
: undefined,
645683
}
646684
);
647685
result.oauthMappings[authProvidersList.apple] =
@@ -652,6 +690,14 @@ export class AmplifyAuth
652690
result.oidc = new cognito.UserPoolIdentityProviderOidc(this, 'OidcIDP', {
653691
userPool,
654692
...external.oidc,
693+
attributeMapping:
694+
external.oidc.attributeMapping ?? shouldMapEmailAttributes
695+
? {
696+
email: {
697+
attributeName: 'email',
698+
},
699+
}
700+
: undefined,
655701
});
656702
result.providersList.push('OIDC');
657703
}

0 commit comments

Comments
 (0)