Skip to content

Commit

Permalink
feat(amplify-category-auth): add auth verification mechanisms to fron…
Browse files Browse the repository at this point in the history
…tend config (#8037) (#8093)
  • Loading branch information
lazpavel authored Sep 10, 2021
1 parent 98e6c56 commit b8949b2
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 19 deletions.
20 changes: 7 additions & 13 deletions packages/amplify-category-auth/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ const { getAddAuthRequestAdaptor, getUpdateAuthRequestAdaptor } = require('./pro
const { getAddAuthHandler, getUpdateAuthHandler } = require('./provider-utils/awscloudformation/handlers/resource-handlers');
const { projectHasAuth } = require('./provider-utils/awscloudformation/utils/project-has-auth');
const { attachPrevParamsToContext } = require('./provider-utils/awscloudformation/utils/attach-prev-params-to-context');
const { getFrontendConfig } = require('./provider-utils/awscloudformation/utils/amplify-meta-updaters');
const { stateManager } = require('amplify-cli-core');
const { headlessImport } = require('./provider-utils/awscloudformation/import');
const { AuthParameters } = require('./provider-utils/awscloudformation/import/types');

const {
doesConfigurationIncludeSMS,
Expand Down Expand Up @@ -250,13 +252,8 @@ async function checkRequirements(requirements, context, category, targetResource

async function initEnv(context) {
const { amplify } = context;
const {
resourcesToBeCreated,
resourcesToBeUpdated,
resourcesToBeSynced,
resourcesToBeDeleted,
allResources,
} = await amplify.getResourceStatus('auth');
const { resourcesToBeCreated, resourcesToBeUpdated, resourcesToBeSynced, resourcesToBeDeleted, allResources } =
await amplify.getResourceStatus('auth');
const isPulling = context.input.command === 'pull' || (context.input.command === 'env' && context.input.subCommands[0] === 'pull');
let toBeCreated = [];
let toBeUpdated = [];
Expand Down Expand Up @@ -410,12 +407,7 @@ const executeAmplifyHeadlessCommand = async (context, headlessPayload) => {
const cognito = await providerPlugin.createCognitoUserPoolService(context);
const identity = await providerPlugin.createIdentityPoolService(context);
const { JSONUtilities } = require('amplify-cli-core/lib/jsonUtilities');
const {
userPoolId,
identityPoolId,
nativeClientId,
webClientId,
} = JSONUtilities.parse(headlessPayload);
const { userPoolId, identityPoolId, nativeClientId, webClientId } = JSONUtilities.parse(headlessPayload);
const projectConfig = context.amplify.getProjectConfig();
const resourceName = projectConfig.projectName.toLowerCase().replace(/[^A-Za-z0-9_]+/g, '_');
const resourceParams = {
Expand Down Expand Up @@ -487,4 +479,6 @@ module.exports = {
category,
importAuth,
isSMSWorkflowEnabled,
AuthParameters,
getFrontendConfig,
};
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type AuthParameters = {
triggers?: string;
identityPoolName?: string;
aliasAttributes?: string[];
usernameAttributes?: string[];
authProviders?: string[];
requiredAttributes?: string[];
passwordPolicyMinLength?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,30 @@ export const getPostUpdateAuthMetaUpdater = (context: any) => async (resourceNam
return resourceName;
};

function getFrontendConfig(authParameters: AuthParameters) {
const loginMechanisms = (authParameters?.aliasAttributes || []).map((att: string) => att.toUpperCase());
export function getFrontendConfig(authParameters: AuthParameters) {
const verificationMechanisms = (authParameters?.autoVerifiedAttributes || []).map((att: string) => att.toUpperCase());
const loginMechanisms: Set<string> = new Set<string>();
(authParameters?.aliasAttributes ?? []).forEach(it => loginMechanisms.add(it.toUpperCase()));

// backwards compatibility
if (authParameters?.usernameAttributes && authParameters?.usernameAttributes.length > 0) {
authParameters.usernameAttributes[0].split(',').forEach(it => loginMechanisms.add(it.trim().toUpperCase()));
}

if (authParameters.authProviders) {
authParameters.authProviders.forEach((provider: string) => {
let name = authProviderList.find(it => it.value === provider)?.name;

if (name) {
loginMechanisms.push(name.toUpperCase());
loginMechanisms.add(name.toUpperCase());
}
});
}

if (loginMechanisms.size == 0) {
loginMechanisms.add('PREFERRED_USERNAME');
}

const signupAttributes = (authParameters?.requiredAttributes || []).map((att: string) => att.toUpperCase());

const passwordProtectionSettings = {
Expand All @@ -139,7 +149,7 @@ function getFrontendConfig(authParameters: AuthParameters) {
}

return {
loginMechanisms: loginMechanisms,
loginMechanisms: Array.from(loginMechanisms),
signupAttributes: signupAttributes,
passwordProtectionSettings: passwordProtectionSettings,
mfaConfiguration: authParameters?.mfaConfiguration,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { JSONUtilities, stateManager } from 'amplify-cli-core';
import { ensureAmplifyMetaFrontendConfig } from '../../../extensions/amplify-helpers/on-category-outputs-change';

jest.mock('amplify-cli-core');

const stateManager_mock = stateManager as jest.Mocked<typeof stateManager>;
stateManager_mock.getMeta.mockReturnValue({ auth: { authResource: { service: 'Cognito' } } });
stateManager_mock.getResourceParametersJson.mockReturnValue({
aliasAttributes: ['EMAIL'],
requiredAttributes: ['EMAIL'],
passwordPolicyMinLength: '10',
mfaConfiguration: 'ON',
mfaTypes: ['SMS Text Message'],
});

const jsonUtilities_mock = JSONUtilities as jest.Mocked<typeof JSONUtilities>;
jsonUtilities_mock.writeJson.mockImplementation(jest.fn());

describe('ensureAmplifyMetaFrontendConfig', () => {
const mockContext = {
amplify: {
pathManager: {
getAmplifyMetaFilePath: jest.fn(() => 'amplifyDirPath'),
},
},
};

it('should add front end config to amplify meta', () => {
ensureAmplifyMetaFrontendConfig(mockContext);
expect(jsonUtilities_mock.writeJson).lastCalledWith(expect.anything(), {
auth: {
authResource: {
frontendAuthConfig: {
loginMechanisms: ['EMAIL'],
mfaConfiguration: 'ON',
mfaTypes: ['SMS'],
passwordProtectionSettings: { passwordPolicyCharacters: [], passwordPolicyMinLength: '10' },
signupAttributes: ['EMAIL'],
verificationMechanisms: [],
},
service: 'Cognito',
},
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import * as fs from 'fs-extra';
import * as path from 'path';
import { getResourceOutputs } from './get-resource-outputs';
import sequential from 'promise-sequential';
import { stateManager } from 'amplify-cli-core';
import { JSONUtilities, stateManager } from 'amplify-cli-core';
import { AuthParameters } from 'amplify-category-auth';
import { getFrontendConfig } from 'amplify-category-auth';

export async function onCategoryOutputsChange(context, cloudAmplifyMeta?, localMeta?) {
if (!cloudAmplifyMeta) {
Expand All @@ -13,8 +15,8 @@ export async function onCategoryOutputsChange(context, cloudAmplifyMeta?, localM
}

const projectConfig = stateManager.getProjectConfig();

if (projectConfig.frontend) {
ensureAmplifyMetaFrontendConfig(context, localMeta);
const frontendPlugins = context.amplify.getFrontendPlugins(context);
const frontendHandlerModule = require(frontendPlugins[projectConfig.frontend]);
await frontendHandlerModule.createFrontendConfigs(context, getResourceOutputs(localMeta), getResourceOutputs(cloudAmplifyMeta));
Expand Down Expand Up @@ -63,3 +65,33 @@ function attachContextExtensions(context, packageLocation) {
}
}
}

// projects created before 5.2.0 didn't populate frontend config in amplify-meta.json
// this method ensures frontend config settings are added to amplify meta on pull as they exist in parameters.json
// https://app.asana.com/0/1200585422384147/1200740448709567/f
export function ensureAmplifyMetaFrontendConfig(context, amplifyMeta?) {
if (!amplifyMeta) {
amplifyMeta = stateManager.getMeta();
}

if (!amplifyMeta.auth) return;

const authResourceName = Object.keys(amplifyMeta.auth).find((key: any) => {
return amplifyMeta.auth[key].service === 'Cognito';
});

if (!authResourceName) return;

const authParameters: AuthParameters = stateManager.getResourceParametersJson(undefined, 'auth', authResourceName);
const frontendAuthConfig = getFrontendConfig(authParameters);

amplifyMeta.auth[authResourceName].frontendAuthConfig = amplifyMeta.auth[authResourceName].frontendAuthConfig ?? {};
const metaFrontendAuthConfig = amplifyMeta.auth[authResourceName].frontendAuthConfig;
Object.keys(frontendAuthConfig).forEach(key => {
if (!metaFrontendAuthConfig.hasOwnProperty(key)) {
metaFrontendAuthConfig[key] = frontendAuthConfig[key];
}
});

JSONUtilities.writeJson(context.amplify.pathManager.getAmplifyMetaFilePath(), amplifyMeta);
}

0 comments on commit b8949b2

Please sign in to comment.