diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/assets/string-maps.js b/packages/amplify-category-auth/src/provider-utils/awscloudformation/assets/string-maps.js index fdd5eb51690..4ac931440f0 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/assets/string-maps.js +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/assets/string-maps.js @@ -116,8 +116,7 @@ const emailRegistration = [ const authSelectionMap = [ { - name: - 'User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)', + name: 'User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)', value: 'identityPoolAndUserPool', }, { @@ -449,18 +448,22 @@ const hostedUIProviders = [ { name: 'Facebook', value: 'Facebook', + key: 'FACEBOOK', }, { name: 'Google', value: 'Google', + key: 'GOOGLE', }, { name: 'Login With Amazon', value: 'LoginWithAmazon', + key: 'AMAZON', }, { name: 'Sign in with Apple', value: 'SignInWithApple', + key: 'APPLE', }, ]; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/index.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/index.ts index 8ba304cbe5f..18b7ff1be6b 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/index.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/index.ts @@ -1,5 +1,6 @@ import { $TSContext, ServiceSelection, stateManager } from 'amplify-cli-core'; import { + AuthParameters, AuthSelections, BackendConfiguration, EnvSpecificResourceParameters, @@ -25,6 +26,7 @@ import Enquirer from 'enquirer'; import _ from 'lodash'; import { importMessages } from './messages'; import uuid from 'uuid'; +import { hostedUIProviders, coreAttributes } from '../assets/string-maps'; // Currently the CLI only supports the output generation of these providers const supportedIdentityProviders = ['COGNITO', 'Facebook', 'Google', 'LoginWithAmazon', 'SignInWithApple']; @@ -701,7 +703,29 @@ const updateStateFiles = async ( region: questionParameters.region!, }; - stateManager.setResourceParametersJson(undefined, 'auth', answers.resourceName!, resourceParameters); + const authResourceParameters: AuthParameters = { + aliasAttributes: answers.userPool?.AliasAttributes, + usernameAttributes: answers.userPool?.UsernameAttributes, + authProvidersUserPool: answers.oauthProviders?.filter(provider => !!hostedUIProviders.find(it => it.value === provider)), + requiredAttributes: (answers.userPool?.SchemaAttributes ?? []) + .filter(att => att.Required && !!coreAttributes.find(it => it.value === att.Name)) + .map(att => att.Name!), + passwordPolicyMinLength: answers.userPool?.Policies?.PasswordPolicy?.MinimumLength ?? 8, + passwordPolicyCharacters: [ + ...(answers.userPool?.Policies?.PasswordPolicy?.RequireLowercase ? ['Requires Lowercase'] : []), + ...(answers.userPool?.Policies?.PasswordPolicy?.RequireUppercase ? ['Requires Uppercase'] : []), + ...(answers.userPool?.Policies?.PasswordPolicy?.RequireNumbers ? ['Requires Numbers'] : []), + ...(answers.userPool?.Policies?.PasswordPolicy?.RequireSymbols ? ['Requires Symbols'] : []), + ], + mfaConfiguration: answers.userPool?.MfaConfiguration, + autoVerifiedAttributes: answers.userPool?.AutoVerifiedAttributes, + mfaTypes: [ + ...(answers.mfaConfiguration?.SmsMfaConfiguration ? ['SMS Text Message'] : []), + ...(answers.mfaConfiguration?.SoftwareTokenMfaConfiguration ? ['TOTP'] : []), + ], + }; + + stateManager.setResourceParametersJson(undefined, 'auth', answers.resourceName!, { ...resourceParameters, ...authResourceParameters }); // Add resource data to amplify-meta file and backend-config, since backend-config requires less information // we have to do a separate update to it without duplicating the methods diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts index 6ca2ff1e4b6..5761c2d2034 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/import/types.ts @@ -57,8 +57,9 @@ export type AuthParameters = { aliasAttributes?: string[]; usernameAttributes?: string[]; authProviders?: string[]; + authProvidersUserPool?: string[]; requiredAttributes?: string[]; - passwordPolicyMinLength?: string; + passwordPolicyMinLength?: number; passwordPolicyCharacters?: string[]; mfaConfiguration?: string; mfaTypes?: string[]; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts index 7c39eb2e072..fd21aa591c6 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import { JSONUtilities, pathManager } from 'amplify-cli-core'; import { transformUserPoolGroupSchema } from './transform-user-pool-group'; -import { authProviders as authProviderList } from '../assets/string-maps'; +import { hostedUIProviders } from '../assets/string-maps'; import { AuthParameters } from '../import/types'; /** @@ -108,27 +108,20 @@ export const getPostUpdateAuthMetaUpdater = (context: any) => async (resourceNam export function getFrontendConfig(authParameters: AuthParameters) { const verificationMechanisms = (authParameters?.autoVerifiedAttributes || []).map((att: string) => att.toUpperCase()); - const loginMechanisms = new Set(); - (authParameters?.aliasAttributes ?? []).forEach(it => loginMechanisms.add(it.toUpperCase())); + const usernameAttributes: string[] = []; - // backwards compatibility if (authParameters?.usernameAttributes && authParameters.usernameAttributes.length > 0) { - authParameters.usernameAttributes[0].split(',').forEach(it => loginMechanisms.add(it.trim().toUpperCase())); + authParameters.usernameAttributes[0].split(',').forEach(it => usernameAttributes.push(it.trim().toUpperCase())); } - if (authParameters.authProviders) { - authParameters.authProviders.forEach((provider: string) => { - let name = authProviderList.find(it => it.value === provider)?.name; + const socialProviders: string[] = []; + (authParameters?.authProvidersUserPool ?? []).forEach((provider: string) => { + const key = hostedUIProviders.find(it => it.value === provider)?.key; - if (name) { - loginMechanisms.add(name.toUpperCase()); - } - }); - } - - if (loginMechanisms.size === 0) { - loginMechanisms.add('PREFERRED_USERNAME'); - } + if (key) { + socialProviders.push(key); + } + }); const signupAttributes = (authParameters?.requiredAttributes || []).map((att: string) => att.toUpperCase()); @@ -149,7 +142,8 @@ export function getFrontendConfig(authParameters: AuthParameters) { } return { - loginMechanisms: Array.from(loginMechanisms), + socialProviders: socialProviders, + usernameAttributes: usernameAttributes, signupAttributes: signupAttributes, passwordProtectionSettings: passwordProtectionSettings, mfaConfiguration: authParameters?.mfaConfiguration, diff --git a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/ensure-amplify-meta-frontend-config.test.ts b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/ensure-amplify-meta-frontend-config.test.ts index bad72ada9bd..7f37e3453b4 100644 --- a/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/ensure-amplify-meta-frontend-config.test.ts +++ b/packages/amplify-cli/src/__tests__/extensions/amplify-helpers/ensure-amplify-meta-frontend-config.test.ts @@ -6,34 +6,28 @@ jest.mock('amplify-cli-core'); const stateManager_mock = stateManager as jest.Mocked; stateManager_mock.getMeta.mockReturnValue({ auth: { authResource: { service: 'Cognito' } } }); stateManager_mock.getResourceParametersJson.mockReturnValue({ - aliasAttributes: ['EMAIL'], + usernameAttributes: ['EMAIL'], requiredAttributes: ['EMAIL'], - passwordPolicyMinLength: '10', + passwordPolicyMinLength: 10, mfaConfiguration: 'ON', mfaTypes: ['SMS Text Message'], + authProvidersUserPool: ['Google', 'Facebook', 'LoginWithAmazon', 'SignInWithApple'], }); stateManager_mock.setMeta.mockImplementation(jest.fn()); describe('ensureAmplifyMetaFrontendConfig', () => { - const mockContext = { - amplify: { - pathManager: { - getAmplifyMetaFilePath: jest.fn(() => 'amplifyDirPath'), - }, - }, - }; - it('should add front end config to amplify meta', () => { ensureAmplifyMetaFrontendConfig(); expect(stateManager_mock.setMeta).lastCalledWith(undefined, { auth: { authResource: { frontendAuthConfig: { - loginMechanisms: ['EMAIL'], + usernameAttributes: ['EMAIL'], + socialProviders: ['GOOGLE', 'FACEBOOK', 'AMAZON', 'APPLE'], mfaConfiguration: 'ON', mfaTypes: ['SMS'], - passwordProtectionSettings: { passwordPolicyCharacters: [], passwordPolicyMinLength: '10' }, + passwordProtectionSettings: { passwordPolicyCharacters: [], passwordPolicyMinLength: 10 }, signupAttributes: ['EMAIL'], verificationMechanisms: [], }, diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/on-category-outputs-change.ts b/packages/amplify-cli/src/extensions/amplify-helpers/on-category-outputs-change.ts index c753252e5a4..fc8fc10e857 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/on-category-outputs-change.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/on-category-outputs-change.ts @@ -87,9 +87,7 @@ export function ensureAmplifyMetaFrontendConfig(amplifyMeta?) { amplifyMeta.auth[authResourceName].frontendAuthConfig ??= {}; const metaFrontendAuthConfig = amplifyMeta.auth[authResourceName].frontendAuthConfig; Object.keys(frontendAuthConfig).forEach(key => { - if (!metaFrontendAuthConfig.hasOwnProperty(key)) { - metaFrontendAuthConfig[key] = frontendAuthConfig[key]; - } + metaFrontendAuthConfig[key] = frontendAuthConfig[key]; }); stateManager.setMeta(undefined, amplifyMeta); diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_6.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_6.test.ts index 55f964e631d..b7a723a1bfa 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_6.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_6.test.ts @@ -34,12 +34,6 @@ describe('zero config auth ', () => { expect(authMeta.frontendAuthConfig).toMatchInlineSnapshot(` Object { - "loginMechanisms": Array [ - "FACEBOOK", - "GOOGLE", - "AMAZON", - "APPLE", - ], "mfaConfiguration": "ON", "mfaTypes": Array [ "SMS", @@ -57,6 +51,13 @@ describe('zero config auth ', () => { "signupAttributes": Array [ "EMAIL", ], + "socialProviders": Array [ + "FACEBOOK", + "GOOGLE", + "AMAZON", + "APPLE", + ], + "usernameAttributes": Array [], "verificationMechanisms": Array [ "EMAIL", ], diff --git a/packages/amplify-e2e-tests/src/__tests__/import_auth_1.test.ts b/packages/amplify-e2e-tests/src/__tests__/import_auth_1.test.ts index e36ec0bb424..52dbc46022d 100644 --- a/packages/amplify-e2e-tests/src/__tests__/import_auth_1.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/import_auth_1.test.ts @@ -1,52 +1,38 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; - -import { $TSObject, JSONUtilities } from 'amplify-cli-core'; +import { $TSObject, JSONUtilities, stateManager } from 'amplify-cli-core'; import { - AddAuthUserPoolOnlyWithOAuthSettings, + addApi, addApiWithCognitoUserPoolAuthTypeWhenAuthExists, addAuthUserPoolOnlyWithOAuth, + AddAuthUserPoolOnlyWithOAuthSettings, addFunction, - amplifyPull, amplifyPush, amplifyPushAuth, amplifyStatus, createNewProjectDir, deleteProject, deleteProjectDir, - getAppId, - getEnvVars, - getTeamProviderInfo, initJSProjectWithProfile, - initProjectWithAccessKey, - addApi, updateApiSchema, } from 'amplify-e2e-core'; +import * as fs from 'fs-extra'; +import * as path from 'path'; import { - AppClientSettings, - AuthProjectDetails, - addAppClientWithSecret, - addAppClientWithoutSecret, addS3WithAuthConfigurationMismatchErrorExit, + AuthProjectDetails, createUserPoolOnlyWithOAuthSettings, - deleteAppClient, expectApiHasCorrectAuthConfig, - expectAuthLocalAndOGMetaFilesOutputMatching, + expectAuthParametersMatch, expectAuthProjectDetailsMatch, expectLocalAndCloudMetaFilesMatching, - expectLocalAndPulledBackendConfigMatching, expectLocalTeamInfoHasNoCategories, expectNoAuthInMeta, getAuthProjectDetails, getOGAuthProjectDetails, getShortId, - importIdentityPoolAndUserPool, importUserPoolOnly, readRootStack, removeImportedAuthWithDefault, } from '../import-helpers'; -import { addEnvironmentWithImportedAuth, checkoutEnvironment, removeEnvironment } from '../environment/env'; - import { getCognitoResourceName } from '../schema-api-directives/authHelper'; import { randomizedFunctionName } from '../schema-api-directives/functionTester'; @@ -244,4 +230,25 @@ describe('auth import userpool only', () => { await amplifyPush(projectRoot); // successful push indicates iam auth works when only importing user pool }); + + it('should update parameters.json with auth configuration', async () => { + await initJSProjectWithProfile(projectRoot, projectSettings); + await importUserPoolOnly(projectRoot, ogSettings.userPoolName, { native: '_app_client ', web: '_app_clientWeb' }); + + const ogProjectAuthParameters = stateManager.getResourceParametersJson(ogProjectRoot, 'auth', ogProjectDetails.authResourceName); + + let projectDetails = getAuthProjectDetails(projectRoot); + let projectAuthParameters = stateManager.getResourceParametersJson(projectRoot, 'auth', projectDetails.authResourceName); + expectAuthParametersMatch(projectAuthParameters, ogProjectAuthParameters); + + await amplifyStatus(projectRoot, 'Import'); + await amplifyPushAuth(projectRoot); + await amplifyStatus(projectRoot, 'No Change'); + + expectLocalAndCloudMetaFilesMatching(projectRoot); + + projectDetails = getAuthProjectDetails(projectRoot); + projectAuthParameters = stateManager.getResourceParametersJson(projectRoot, 'auth', projectDetails.authResourceName); + expectAuthParametersMatch(projectAuthParameters, ogProjectAuthParameters); + }); }); diff --git a/packages/amplify-e2e-tests/src/import-helpers/expects.ts b/packages/amplify-e2e-tests/src/import-helpers/expects.ts index c8c7773a4b4..ba4428b90dd 100644 --- a/packages/amplify-e2e-tests/src/import-helpers/expects.ts +++ b/packages/amplify-e2e-tests/src/import-helpers/expects.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; import { getProjectMeta, getBackendAmplifyMeta, getTeamProviderInfo, getBackendConfig } from 'amplify-e2e-core'; import { AuthProjectDetails, DynamoDBProjectDetails, readRootStack, StorageProjectDetails } from '.'; +import { AuthParameters } from 'amplify-category-auth'; export const expectAuthProjectDetailsMatch = (projectDetails: AuthProjectDetails, ogProjectDetails: AuthProjectDetails) => { expect(projectDetails.parameters.authSelections).toEqual(ogProjectDetails.parameters.authSelections); @@ -168,3 +169,12 @@ export const expectDynamoDBLocalAndOGMetaFilesOutputMatching = (projectRoot: str expect(storageMeta.output.Arn).toEqual(ogStorageMeta.output.Arn); expect(storageMeta.output.StreamArn).toEqual(ogStorageMeta.output.StreamArn); }; + +export const expectAuthParametersMatch = (authParameters: AuthParameters, ogAuthParameters: AuthParameters) => { + expect(authParameters.authProvidersUserPool).toEqual(ogAuthParameters.authProvidersUserPool); + expect(authParameters.requiredAttributes).toEqual(ogAuthParameters.requiredAttributes); + expect(authParameters.passwordPolicyMinLength).toEqual(ogAuthParameters.passwordPolicyMinLength); + expect(authParameters.passwordPolicyCharacters).toEqual(ogAuthParameters.passwordPolicyCharacters); + expect(authParameters.mfaConfiguration).toEqual(ogAuthParameters.mfaConfiguration); + expect(authParameters.autoVerifiedAttributes).toEqual(ogAuthParameters.autoVerifiedAttributes); +}; diff --git a/packages/amplify-frontend-javascript/lib/frontend-config-creator.js b/packages/amplify-frontend-javascript/lib/frontend-config-creator.js index ef8f52425f6..8864638f185 100644 --- a/packages/amplify-frontend-javascript/lib/frontend-config-creator.js +++ b/packages/amplify-frontend-javascript/lib/frontend-config-creator.js @@ -309,7 +309,8 @@ function getCognitoConfig(cognitoResources, projectRegion) { const frontendAuthConfig = {}; if (cognitoResource.frontendAuthConfig) { - frontendAuthConfig.aws_cognito_login_mechanisms = cognitoResource.frontendAuthConfig.loginMechanisms; + frontendAuthConfig.aws_cognito_username_attributes = cognitoResource.frontendAuthConfig.usernameAttributes; + frontendAuthConfig.aws_cognito_social_providers = cognitoResource.frontendAuthConfig.socialProviders; frontendAuthConfig.aws_cognito_signup_attributes = cognitoResource.frontendAuthConfig.signupAttributes; frontendAuthConfig.aws_cognito_mfa_configuration = cognitoResource.frontendAuthConfig.mfaConfiguration; frontendAuthConfig.aws_cognito_mfa_types = cognitoResource.frontendAuthConfig.mfaTypes;