diff --git a/packages/aws-amplify/__tests__/initSingleton.test.ts b/packages/aws-amplify/__tests__/initSingleton.test.ts index ddc523b8e65..39523fe9298 100644 --- a/packages/aws-amplify/__tests__/initSingleton.test.ts +++ b/packages/aws-amplify/__tests__/initSingleton.test.ts @@ -91,41 +91,37 @@ describe('initSingleton (DefaultAmplify)', () => { Amplify.configure(mockLegacyConfig); - expect(mockAmplifySingletonConfigure).toHaveBeenCalledWith( - { - Auth: { - Cognito: { - allowGuestAccess: true, - identityPoolId: 'aws_cognito_identity_pool_id', - loginWith: { - email: false, - phone: false, - username: true, - }, - mfa: { - smsEnabled: true, - status: 'off', - totpEnabled: false, - }, - passwordFormat: { - minLength: 8, - requireLowercase: false, - requireNumbers: false, - requireSpecialCharacters: false, - requireUppercase: false, - }, - userAttributes: [ - { - phone_number: { - required: true, - }, - }, - ], - userPoolClientId: 'aws_user_pools_web_client_id', - userPoolId: 'aws_user_pools_id', + const resourcesConfig: ResourcesConfig = { + Auth: { + Cognito: { + allowGuestAccess: true, + identityPoolId: 'aws_cognito_identity_pool_id', + loginWith: { + email: false, + phone: false, + username: true, + }, + mfa: { + smsEnabled: true, + status: 'off', + totpEnabled: false, }, + passwordFormat: { + minLength: 8, + requireLowercase: false, + requireNumbers: false, + requireSpecialCharacters: false, + requireUppercase: false, + }, + userAttributes: { phone_number: { required: true } }, + userPoolClientId: 'aws_user_pools_web_client_id', + userPoolId: 'aws_user_pools_id', }, }, + }; + + expect(mockAmplifySingletonConfigure).toHaveBeenCalledWith( + resourcesConfig, expect.anything() ); }); diff --git a/packages/core/__tests__/parseAWSExports.test.ts b/packages/core/__tests__/parseAWSExports.test.ts index 4dbe05a4221..29ebf893475 100644 --- a/packages/core/__tests__/parseAWSExports.test.ts +++ b/packages/core/__tests__/parseAWSExports.test.ts @@ -1,7 +1,8 @@ import { parseAWSExports } from '../src/parseAWSExports'; +import { ResourcesConfig } from '../src/singleton/types'; // TODO: Add API category tests -describe('Parser', () => { +describe('parseAWSExports', () => { const appId = 'app-id'; const bucket = 'bucket'; const identityPoolId = 'identity-pool-id'; @@ -75,62 +76,8 @@ describe('Parser', () => { const oAuthResponseType = 'code'; it('should parse valid aws-exports.js', () => { - expect( - parseAWSExports({ - aws_cognito_identity_pool_id: identityPoolId, - aws_cognito_sign_up_verification_method: signUpVerificationMethod, - aws_cognito_username_attributes: ['PHONE_NUMBER'], - aws_cognito_signup_attributes: ['PHONE_NUMBER'], - aws_cognito_mfa_configuration: 'OFF', - aws_cognito_mfa_types: ['SMS', 'TOTP'], - aws_cognito_password_protection_settings: { - passwordPolicyMinLength: 8, - passwordPolicyCharacters: [ - 'REQUIRES_SYMBOLS', - 'REQUIRES_UPPERCASE', - 'REQUIRES_NUMBERS', - ], - }, - oauth: { - domain: oAuthDomain, - scope: oAuthScopes, - redirectSignIn: oAuthSigninUrl, - redirectSignOut: oAuthSignoutUrl, - responseType: oAuthResponseType, - }, - aws_cognito_verification_mechanisms: ['EMAIL'], - aws_cognito_social_providers: ['GOOGLE', 'APPLE', 'FACEBOOK', 'AMAZON'], - aws_mandatory_sign_in: 'enable', - aws_mobile_analytics_app_id: appId, - aws_mobile_analytics_app_region: region, - aws_user_files_s3_bucket: bucket, - aws_user_files_s3_bucket_region: region, - aws_user_pools_id: userPoolId, - aws_user_pools_web_client_id: userPoolClientId, - geo: { - amazon_location_service: amazonLocationService, - }, - aws_cloud_logic_custom: [restEndpoint1, restEndpoint2], - aws_appsync_graphqlEndpoint: appsyncEndpoint, - aws_appsync_apiKey: apiKey, - aws_appsync_region: region, - aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS', - Notifications: { - InAppMessaging: { - AWSPinpoint: { - appId: appId, - region: region, - }, - }, - }, - }) - ).toStrictEqual({ - Analytics: { - Pinpoint: { - appId, - region, - }, - }, + const expected: ResourcesConfig = { + Analytics: { Pinpoint: { appId, region } }, Auth: { Cognito: { identityPoolId, @@ -148,11 +95,7 @@ describe('Parser', () => { phone: true, username: false, }, - mfa: { - smsEnabled: true, - status: 'off', - totpEnabled: true, - }, + mfa: { smsEnabled: true, status: 'off', totpEnabled: true }, passwordFormat: { minLength: 8, requireLowercase: false, @@ -161,18 +104,10 @@ describe('Parser', () => { requireUppercase: true, }, signUpVerificationMethod, - userAttributes: [ - { - email: { - required: true, - }, - }, - { - phone_number: { - required: true, - }, - }, - ], + userAttributes: { + email: { required: true }, + phone_number: { required: true }, + }, userPoolId, userPoolClientId, }, @@ -189,10 +124,7 @@ describe('Parser', () => { }, API: { REST: { - api1: { - endpoint: 'https://api1.com', - region: 'us-east-1', - }, + api1: { endpoint: 'https://api1.com', region: 'us-east-1' }, api2: { endpoint: 'https://api2.com', region: 'us-west-2', @@ -207,14 +139,52 @@ describe('Parser', () => { }, }, Notifications: { - InAppMessaging: { - Pinpoint: { - appId, - region, - }, - }, + InAppMessaging: { Pinpoint: { appId, region } }, }, - }); + }; + expect( + parseAWSExports({ + aws_cognito_identity_pool_id: identityPoolId, + aws_cognito_sign_up_verification_method: signUpVerificationMethod, + aws_cognito_username_attributes: ['PHONE_NUMBER'], + aws_cognito_signup_attributes: ['PHONE_NUMBER'], + aws_cognito_mfa_configuration: 'OFF', + aws_cognito_mfa_types: ['SMS', 'TOTP'], + aws_cognito_password_protection_settings: { + passwordPolicyMinLength: 8, + passwordPolicyCharacters: [ + 'REQUIRES_SYMBOLS', + 'REQUIRES_UPPERCASE', + 'REQUIRES_NUMBERS', + ], + }, + oauth: { + domain: oAuthDomain, + scope: oAuthScopes, + redirectSignIn: oAuthSigninUrl, + redirectSignOut: oAuthSignoutUrl, + responseType: oAuthResponseType, + }, + aws_cognito_verification_mechanisms: ['EMAIL'], + aws_cognito_social_providers: ['GOOGLE', 'APPLE', 'FACEBOOK', 'AMAZON'], + aws_mandatory_sign_in: 'enable', + aws_mobile_analytics_app_id: appId, + aws_mobile_analytics_app_region: region, + aws_user_files_s3_bucket: bucket, + aws_user_files_s3_bucket_region: region, + aws_user_pools_id: userPoolId, + aws_user_pools_web_client_id: userPoolClientId, + geo: { amazon_location_service: amazonLocationService }, + aws_cloud_logic_custom: [restEndpoint1, restEndpoint2], + aws_appsync_graphqlEndpoint: appsyncEndpoint, + aws_appsync_apiKey: apiKey, + aws_appsync_region: region, + aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS', + Notifications: { + InAppMessaging: { AWSPinpoint: { appId, region } }, + }, + }) + ).toStrictEqual(expected); }); it('should fallback to IAM auth mode if Appsync auth type is invalid', () => { @@ -267,7 +237,7 @@ describe('Parser', () => { mfa: undefined, passwordFormat: undefined, signUpVerificationMethod: undefined, - userAttributes: [], + userAttributes: {}, userPoolClientId: undefined, userPoolId: userPoolId, }, diff --git a/packages/core/__tests__/singleton/Singleton.test.ts b/packages/core/__tests__/singleton/Singleton.test.ts index 7dd8c383b0c..b79f338346e 100644 --- a/packages/core/__tests__/singleton/Singleton.test.ts +++ b/packages/core/__tests__/singleton/Singleton.test.ts @@ -3,7 +3,7 @@ import { AuthClass as Auth } from '../../src/singleton/Auth'; import { decodeJWT } from '../../src/singleton/Auth/utils'; import { CredentialsAndIdentityId } from '../../src/singleton/Auth/types'; import { TextEncoder, TextDecoder } from 'util'; -import { fetchAuthSession } from '../../src'; +import { fetchAuthSession, ResourcesConfig } from '../../src'; Object.assign(global, { TextDecoder, TextEncoder }); type ArgumentTypes = F extends (...args: infer A) => any ? A @@ -16,7 +16,12 @@ const MOCK_AUTH_CONFIG = { }, }, }; -const modelIntrospection = { + +type ModelIntrospection = NonNullable< + NonNullable['GraphQL'] +>['modelIntrospection']; + +const modelIntrospection: ModelIntrospection = { version: 1, models: { Todo: { @@ -107,24 +112,15 @@ describe('Amplify.configure() and Amplify.getConfig()', () => { Amplify.configure(mockLegacyConfig); const result = Amplify.getConfig(); - - expect(result).toEqual({ + const expected: ResourcesConfig = { Auth: { Cognito: { allowGuestAccess: true, identityPoolId: 'aws_cognito_identity_pool_id', userPoolClientId: 'aws_user_pools_web_client_id', userPoolId: 'aws_user_pools_id', - loginWith: { - email: false, - phone: false, - username: true, - }, - mfa: { - smsEnabled: true, - status: 'off', - totpEnabled: false, - }, + loginWith: { email: false, phone: false, username: true }, + mfa: { smsEnabled: true, status: 'off', totpEnabled: false }, passwordFormat: { minLength: 8, requireLowercase: false, @@ -132,13 +128,7 @@ describe('Amplify.configure() and Amplify.getConfig()', () => { requireSpecialCharacters: false, requireUppercase: false, }, - userAttributes: [ - { - phone_number: { - required: true, - }, - }, - ], + userAttributes: { phone_number: { required: true } }, }, }, API: { @@ -150,7 +140,9 @@ describe('Amplify.configure() and Amplify.getConfig()', () => { modelIntrospection, }, }, - }); + }; + + expect(result).toEqual(expected); }); it('should take the v6 shaped config object for configuring and return it from getConfig()', () => { diff --git a/packages/core/src/parseAWSExports.ts b/packages/core/src/parseAWSExports.ts index 061b4dce337..1823cb44275 100644 --- a/packages/core/src/parseAWSExports.ts +++ b/packages/core/src/parseAWSExports.ts @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { ConsoleLogger } from './Logger'; import { - OAuthConfig, AuthConfigUserAttributes, + LegacyUserAttributeKey, + OAuthConfig, OAuthProvider, } from './singleton/Auth/types'; import { ResourcesConfig } from './singleton/types'; @@ -161,17 +162,22 @@ export const parseAWSExports = ( ) ?? false, } : undefined; - const mergedUserAttributes = Array.from( + const mergedUserAttributes: LegacyUserAttributeKey[] = Array.from( new Set([ ...(aws_cognito_verification_mechanisms ?? []), ...(aws_cognito_signup_attributes ?? []), ]) ); - const userAttributesConfig = mergedUserAttributes.map((s: string) => ({ - [s.toLowerCase()]: { - required: true, // All user attributes generated by the CLI will be required - }, - })) as unknown as AuthConfigUserAttributes; + + const userAttributes: AuthConfigUserAttributes = mergedUserAttributes.reduce( + (attributes: AuthConfigUserAttributes, key: LegacyUserAttributeKey) => ({ + ...attributes, + // All user attributes generated by the CLI are required + [key.toLowerCase()]: { required: true }, + }), + {} + ); + const loginWithEmailEnabled = aws_cognito_username_attributes?.includes('EMAIL') ?? false; const loginWithPhoneEnabled = @@ -182,7 +188,7 @@ export const parseAWSExports = ( identityPoolId: aws_cognito_identity_pool_id, allowGuestAccess: aws_mandatory_sign_in !== 'enable', signUpVerificationMethod: aws_cognito_sign_up_verification_method, - userAttributes: userAttributesConfig, + userAttributes, userPoolClientId: aws_user_pools_web_client_id, userPoolId: aws_user_pools_id, mfa: mfaConfig, diff --git a/packages/core/src/singleton/Auth/types.ts b/packages/core/src/singleton/Auth/types.ts index 9ef1f8f26b7..44fac6f0a78 100644 --- a/packages/core/src/singleton/Auth/types.ts +++ b/packages/core/src/singleton/Auth/types.ts @@ -93,6 +93,9 @@ export type AuthStandardAttributeKey = | 'zoneinfo' | AuthVerifiableAttributeKey; +// legacy config user attribute keys are uppercase +export type LegacyUserAttributeKey = Uppercase; + export type AuthVerifiableAttributeKey = 'email' | 'phone_number'; export type AuthConfigUserAttributes = Partial<