Skip to content

Commit

Permalink
chore: generate auth secret
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolk committed Jul 10, 2023
1 parent 7aeddf4 commit 27566b7
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { $TSContext, stateManager, pathManager, AmplifyFault } from '@aws-amplify/amplify-cli-core';
import { updateAppClientWithGeneratedSecret } from '../../../../provider-utils/awscloudformation/utils/generate-cognito-app-client-secret';
import { projectHasAuth } from '../../../../provider-utils/awscloudformation/utils/project-has-auth';
import { getAuthResourceName } from '../../../../utils/getAuthResourceName';
import { getAppClientSecret } from '../../../../provider-utils/awscloudformation/utils/get-app-client-secret-sdk';

jest.mock('@aws-amplify/amplify-cli-core');
jest.mock('../../../../provider-utils/awscloudformation/utils/project-has-auth');
jest.mock('../../../../utils/getAuthResourceName');
jest.mock('../../../../provider-utils/awscloudformation/utils/get-app-client-secret-sdk');

const stateManagerMock = stateManager as jest.Mocked<typeof stateManager>;
const pathManagerMock = pathManager as jest.Mocked<typeof pathManager>;
const AmplifyFaultMock = AmplifyFault as jest.MockedClass<typeof AmplifyFault>;
const projectHasAuthMock = projectHasAuth as jest.MockedFunction<typeof projectHasAuth>;
const getAuthResourceNameMock = getAuthResourceName as jest.MockedFunction<typeof getAuthResourceName>;
const getAppClientSecretMock = getAppClientSecret as jest.MockedFunction<typeof getAppClientSecret>;

pathManagerMock.getBackendDirPath.mockReturnValue('mockBackendPath');
projectHasAuthMock.mockReturnValue(true);
getAuthResourceNameMock.mockResolvedValue('mockResource');
const contextStub = {
amplify: {
getImportedAuthProperties: jest.fn().mockResolvedValue({ imported: false }),
updateamplifyMetaAfterResourceUpdate: jest.fn(),
},
};
describe('test auth trigger stack Parameters', () => {
it('test case 1 - appClientSecret doesnt get updated when sdk returns undefined ', async () => {
jest.clearAllMocks();
stateManagerMock.getMeta.mockReturnValue({
auth: {
mockResource: {
output: {
AppClientID: 'mockClientId',
UserPoolId: 'mockUserpoolId',
},
},
},
});
getAppClientSecretMock.mockResolvedValue(undefined);
await updateAppClientWithGeneratedSecret(contextStub as unknown as $TSContext);
expect(contextStub.amplify.updateamplifyMetaAfterResourceUpdate.mock.calls).toMatchInlineSnapshot(`[]`);
});

it('test case 2 - appClientSecret updates successfully ', async () => {
jest.clearAllMocks();
stateManagerMock.getMeta.mockReturnValue({
auth: {
mockResource: {
output: {
AppClientID: 'mockClientId',
UserPoolId: 'mockUserpoolId',
AmazonWebClient: 'mockAmazonWebClient',
FacebookWebClient: 'mockFacebookWebClient',
GoogleWebClient: 'mockGoogleWebClient',
AppleWebClient: 'mockAppleWebClient',
HostedUIDomain: 'mockHostedUIDomain',
OAuthMetadata: 'mockOAuthMetadata',
},
},
},
});
getAppClientSecretMock.mockResolvedValue('mockAppClientSecret');
await updateAppClientWithGeneratedSecret(contextStub as unknown as $TSContext);
expect(contextStub.amplify.updateamplifyMetaAfterResourceUpdate.mock.calls).toMatchInlineSnapshot(`
[
[
"auth",
"mockResource",
"output",
{
"AmazonWebClient": "mockAmazonWebClient",
"AppClientID": "mockClientId",
"AppClientSecret": "mockAppClientSecret",
"AppleWebClient": "mockAppleWebClient",
"FacebookWebClient": "mockFacebookWebClient",
"GoogleWebClient": "mockGoogleWebClient",
"HostedUIDomain": "mockHostedUIDomain",
"OAuthMetadata": "mockOAuthMetadata",
"UserPoolId": "mockUserpoolId",
},
],
]
`);
});

it('test case 3 - throws error when getAppClientSecret call fails ', async () => {
jest.clearAllMocks();
stateManagerMock.getMeta.mockReturnValue({
auth: {
mockResource: {
output: {
AppClientID: 'mockClientId',
UserPoolId: 'mockUserpoolId',
},
},
},
});
getAppClientSecretMock.mockRejectedValue('error fetching app client secret');
try {
await updateAppClientWithGeneratedSecret(contextStub as unknown as $TSContext);
} catch (err) {
console.log(err);
expect(err).toMatchInlineSnapshot(`"error fetching app client secret"`);
expect(contextStub.amplify.updateamplifyMetaAfterResourceUpdate.mock.calls).toMatchInlineSnapshot(`[]`);
}
});
});
2 changes: 2 additions & 0 deletions packages/amplify-category-auth/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const { privateKeys } = require('./provider-utils/awscloudformation/constants');
const { checkAuthResourceMigration } = require('./provider-utils/awscloudformation/utils/check-for-auth-migration');
const { run: authRunPush } = require('./commands/auth/push');
const { getAuthTriggerStackCfnParameters } = require('./provider-utils/awscloudformation/utils/get-auth-trigger-stack-cfn-parameters');
const { updateAppClientWithGeneratedSecret } = require('./provider-utils/awscloudformation/utils/generate-cognito-app-client-secret');
const { prePushHandler } = require('./events/prePushHandler');

// this function is being kept for temporary compatability.
Expand Down Expand Up @@ -575,6 +576,7 @@ module.exports = {
transformCategoryStack,
authPluginAPIPush: authPushYes,
getAuthTriggerStackCfnParameters,
updateAppClientWithGeneratedSecret,
};

// force major version bump for cdk v2
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { $TSContext, AmplifyCategories, stateManager } from '@aws-amplify/amplify-cli-core';
import { getAuthResourceName } from '../../../utils/getAuthResourceName';
import { MetaOutput } from '../import/types';
import { getAppClientSecret } from './get-app-client-secret-sdk';
import { projectHasAuth } from './project-has-auth';

/**
*
* updates app client secret if user pool exists and userpoolClientGenerateSecret is set to true
*/
export const updateAppClientWithGeneratedSecret = async (context: $TSContext): Promise<void> => {
if (projectHasAuth()) {
// check if its imported auth
const { imported } = context.amplify.getImportedAuthProperties(context);
if (!imported) {
const authResourceName = await getAuthResourceName(context);
const authMetaOutput: MetaOutput = stateManager.getMeta()?.auth[authResourceName]?.output;
const clientId = authMetaOutput.AppClientID;
const userpoolId = authMetaOutput.UserPoolId;
// no else case required as userpool client is default created with userPool when created through amplify
if (clientId && userpoolId) {
const appClientSecret = await getAppClientSecret(context, userpoolId, clientId);
if (appClientSecret) {
authMetaOutput.AppClientSecret = appClientSecret;
await context.amplify.updateamplifyMetaAfterResourceUpdate(AmplifyCategories.AUTH, authResourceName, 'output', authMetaOutput);
}
}
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { $TSContext, AmplifyFault } from '@aws-amplify/amplify-cli-core';
import { CognitoIdentityServiceProvider } from 'aws-sdk';

export const getAppClientSecret = async (context: $TSContext, userpoolId: string, clientId: string): Promise<string | undefined> => {
try {
const identity = await getCognitoIdentityProviderClient(context);
const params = {
ClientId: clientId,
UserPoolId: userpoolId,
};
const result = await identity.describeUserPoolClient(params).promise();
return result.UserPoolClient?.ClientSecret;
} catch (error) {
throw new AmplifyFault(
'ServiceCallFault',
{
message: error.message,
},
error,
);
}
};

const getCognitoIdentityProviderClient = async (context: $TSContext): Promise<CognitoIdentityServiceProvider> => {
const { client } = await context.amplify.invokePluginMethod<{ client: CognitoIdentityServiceProvider }>(
context,
'awscloudformation',
undefined,
'getConfiguredCognitoIdentityProviderClient',
[context],
);
return client;
};
99 changes: 99 additions & 0 deletions packages/amplify-e2e-tests/src/__tests__/auth/hosted-ui.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
addAuthWithDefault,
addAuthWithSignInSignOutUrl,
amplifyPushAuth,
createNewProjectDir,
deleteProject,
deleteProjectDir,
getProjectMeta,
getUserPool,
initJSProjectWithProfile,
updateAuthSignInSignOutUrlWithAll,
} from '@aws-amplify/amplify-e2e-core';

const defaultsSettings = {
name: 'authTest',
};

const oauthSettings = {
signinUrl: 'https://danielle.lol/',
signoutUrl: 'https://danielle.lol/',
};

describe('hosted ui tests', () => {
let projRoot: string;

beforeEach(async () => {
projRoot = await createNewProjectDir('auth');
});

afterEach(async () => {
await deleteProject(projRoot);
deleteProjectDir(projRoot);
});

describe('...amplify add auth', () => {
describe('...creating with oauth', () => {
it('...creates a user pool domain', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithSignInSignOutUrl(projRoot, oauthSettings);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { HostedUIDomain, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const userPoolRes = await getUserPool(UserPoolId, region);

expect(HostedUIDomain).toBeDefined();
expect(HostedUIDomain).toEqual(userPoolRes.UserPool.Domain);
});
});
});

describe('amplify update auth', () => {
describe('...updating to add oauth', () => {
it('...creates a user pool domain', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithDefault(projRoot);
await updateAuthSignInSignOutUrlWithAll(projRoot, oauthSettings);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { HostedUIDomain, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const userPoolRes = await getUserPool(UserPoolId, region);

expect(HostedUIDomain).toBeDefined();
expect(HostedUIDomain).toEqual(userPoolRes.UserPool.Domain);
});
});

describe('...updating to add oauth after push', () => {
it('...creates a user pool domain', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithDefault(projRoot);
await amplifyPushAuth(projRoot);

await updateAuthSignInSignOutUrlWithAll(projRoot, oauthSettings);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { HostedUIDomain, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const userPoolRes = await getUserPool(UserPoolId, region);

expect(HostedUIDomain).toBeDefined();
expect(HostedUIDomain).toEqual(userPoolRes.UserPool.Domain);
});
});
});
});
106 changes: 106 additions & 0 deletions packages/amplify-e2e-tests/src/__tests__/auth/user-groups.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
addAuthWithDefault,
addAuthWithGroups,
amplifyPushAuth,
createNewProjectDir,
deleteProject,
deleteProjectDir,
getIdentityPoolRoles,
getProjectMeta,
initJSProjectWithProfile,
updateAuthAddUserGroups,
} from '@aws-amplify/amplify-e2e-core';

const defaultsSettings = {
name: 'authTest',
};

describe('user group tests', () => {
let projRoot: string;

beforeEach(async () => {
projRoot = await createNewProjectDir('auth');
});

afterEach(async () => {
await deleteProject(projRoot);
deleteProjectDir(projRoot);
});

describe('...amplify add auth', () => {
describe('...creating with a user pool group', () => {
it('...assigns authenticated roles for users added to user group', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithGroups(projRoot);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { AppClientID, AppClientIDWeb, IdentityPoolId, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const identityPoolRoles = await getIdentityPoolRoles(IdentityPoolId, region);
const roleMapKeyClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientID}`;
const roleMapKeyWebClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientIDWeb}`;

expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].Type).toEqual('Token');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].Type).toEqual('Token');
});
});
});

describe('...amplify update auth', () => {
describe('...updating to add a user pool group', () => {
it('...assigns authenticated roles for users added to user group', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithDefault(projRoot);
await amplifyPushAuth(projRoot);

await updateAuthAddUserGroups(projRoot, ['mygroup']);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { AppClientID, AppClientIDWeb, IdentityPoolId, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const identityPoolRoles = await getIdentityPoolRoles(IdentityPoolId, region);
const roleMapKeyClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientID}`;
const roleMapKeyWebClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientIDWeb}`;

expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].Type).toEqual('Token');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].Type).toEqual('Token');
});
});

describe('...updating to add a user pool group after create', () => {
it('...assigns authenticated roles for users added to user group', async () => {
await initJSProjectWithProfile(projRoot, defaultsSettings);
await addAuthWithDefault(projRoot);
await updateAuthAddUserGroups(projRoot, ['mygroup']);
await amplifyPushAuth(projRoot);

const meta = getProjectMeta(projRoot);
const region = meta.providers.awscloudformation.Region;
const { AppClientID, AppClientIDWeb, IdentityPoolId, UserPoolId } = Object.keys(meta.auth)
.map((key) => meta.auth[key])
.find((auth) => auth.service === 'Cognito').output;

const identityPoolRoles = await getIdentityPoolRoles(IdentityPoolId, region);
const roleMapKeyClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientID}`;
const roleMapKeyWebClientId = `cognito-idp.${region}.amazonaws.com/${UserPoolId}:${AppClientIDWeb}`;

expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyClientId].Type).toEqual('Token');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].AmbiguousRoleResolution).toEqual('AuthenticatedRole');
expect(identityPoolRoles.RoleMappings[roleMapKeyWebClientId].Type).toEqual('Token');
});
});
});
});
Loading

0 comments on commit 27566b7

Please sign in to comment.