From 3cc13cdf05722e85925d868733a5a06c4614ebd9 Mon Sep 17 00:00:00 2001 From: akshay Date: Mon, 24 Apr 2023 12:02:57 -0700 Subject: [PATCH 1/6] fix: creates oauthKeys from userpool for pull use cases --- .../awscloudformation/index.test.ts | 112 +++++++++++++++++- .../awscloudformation/import/index.ts | 2 +- .../provider-utils/awscloudformation/index.ts | 9 ++ .../utils/get-oauth-secrets-from-cognito.ts | 50 ++++++++ .../src/__tests__/auth_2a.test.ts | 14 ++- 5 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts diff --git a/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts b/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts index 7da1118f152..2b560eb658c 100644 --- a/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts +++ b/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts @@ -1,7 +1,9 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; import { updateConfigOnEnvInit } from '../../../provider-utils/awscloudformation/index'; +import { getOAuthObjectFromCognito } from '../../../provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito'; jest.mock('@aws-amplify/amplify-environment-parameters'); +jest.mock('../../../provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito'); jest.mock('@aws-amplify/amplify-cli-core', () => ({ ...(jest.requireActual('@aws-amplify/amplify-cli-core') as {}), JSONUtilities: { @@ -18,12 +20,14 @@ jest.mock('@aws-amplify/amplify-cli-core', () => ({ const pluginInstanceMock = jest.fn(); const loadResourceParametersMock = jest.fn().mockReturnValue({ hostedUIProviderMeta: - '[{"ProviderName":"Facebook","authorize_scopes":"email,public_profile","AttributeMapping":{"email":"email","username":"id"}},{"ProviderName":"LoginWithAmazon","authorize_scopes":"profile profile:user_id","AttributeMapping":{"email":"email","username":"user_id"}},{"ProviderName":"Google","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}}]', + '[{"ProviderName":"Facebook","authorize_scopes":"email,public_profile","AttributeMapping":{"email":"email","username":"id"}},{"ProviderName":"LoginWithAmazon","authorize_scopes":"profile profile:user_id","AttributeMapping":{"email":"email","username":"user_id"}},{"ProviderName":"Google","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}},{"ProviderName":"SignInWithApple","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}}]', }); const pluginInstance = { loadResourceParameters: loadResourceParametersMock, }; +const getOAuthObjectFromCognitoMock = getOAuthObjectFromCognito as jest.MockedFunction; + // mock context let mockContext = { amplify: { @@ -61,8 +65,106 @@ let mockContext = { }, } as unknown as $TSContext; -test('throws amplify error when auth headless params are missing during pull', async () => { - expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( - `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, - ); +describe('import checks', () => { + test('throws amplify error when auth headless params are missing during pull', async () => { + expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( + `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, + ); + }); +}); + +describe('update config when amplify pull headless command', () => { + test('throws amplify error when auth headless params are missing during pull', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( + `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, + ); + }); + + test('works when secrets are fetched from userpool', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue([ + { + client_id: 'mockClientFacebook', + client_secret: 'mockSecretFacebook', + ProviderName: 'Facebook', + }, + { + client_id: 'mockClientGoogle', + client_secret: 'mockSecretGoogle', + ProviderName: 'Google', + }, + { + client_id: 'mockClientLoginWithAmazon', + client_secret: 'mockSecretLoginWithAmazon', + ProviderName: 'LoginWithAmazon', + }, + { + client_id: 'mockClientSignInWithApple', + team_id: 'mockTeamIdSignInWithApple', + key_id: 'mockKeyIdSignInWithApple', + private_key: 'mockPrivayKeySignInWithApple', + ProviderName: 'SignInWithApple', + }, + ]); + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\"},{\\"ProviderName\\":\\"Google\\"},{\\"ProviderName\\":\\"SignInWithApple\\"}]", + } + `); + }); + + test('works when secrets are present in deployment params', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + mockContext.amplify.loadEnvResourceParameters = jest.fn().mockReturnValue({ + hostedUIProviderCreds: + '[{"ProviderName":"Facebook","client_id":"sdcsdc","client_secret":"bfdsvsr"},{"ProviderName":"Google","client_id":"avearver","client_secret":"vcvereger"},{"ProviderName":"LoginWithAmazon","client_id":"vercvdsavcer","client_secret":"revfdsavrtv"},{"ProviderName":"SignInWithApple","client_id":"vfdvergver","team_id":"ervervre","key_id":"vfdavervfer","private_key":"vaveb"}]', + }); + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\"},{\\"ProviderName\\":\\"Google\\"},{\\"ProviderName\\":\\"SignInWithApple\\"}]", + } + `); + }); + + test('test works when secrets are present in context input params', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + mockContext.amplify.loadEnvResourceParameters = jest.fn().mockReturnValue('[]'); + mockContext.exeInfo = { + inputParams: { + yes: true, + categories: { + auth: { + facebookAppIdUserPool: 'mockfacebookAppIdUserPool', + facebookAppSecretUserPool: 'facebookAppSecretUserPool', + googleAppIdUserPool: 'googleAppIdUserPool', + googleAppSecretUserPool: 'googleAppSecretUserPool', + loginwithamazonAppIdUserPool: 'loginwithamazonAppIdUserPool', + loginwithamazonAppSecretUserPool: 'loginwithamazonAppSecretUserPool', + signinwithappleClientIdUserPool: 'signinwithappleClientIdUserPool', + signinwithappleTeamIdUserPool: 'signinwithappleTeamIdUserPool', + signinwithappleKeyIdUserPool: 'signinwithappleKeyIdUserPool', + signinwithapplePrivateKeyUserPool: 'signinwithapplePrivateKeyUserPool', + }, + }, + }, + localEnvInfo: { + projectPath: 'mockProjectPath', + defaultEditor: 'vscode', + envName: 'dev', + noUpdateBackend: false, + }, + }; + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\",\\"client_id\\":\\"mockfacebookAppIdUserPool\\",\\"client_secret\\":\\"facebookAppSecretUserPool\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\",\\"client_id\\":\\"loginwithamazonAppIdUserPool\\",\\"client_secret\\":\\"loginwithamazonAppSecretUserPool\\"},{\\"ProviderName\\":\\"Google\\",\\"client_id\\":\\"googleAppIdUserPool\\",\\"client_secret\\":\\"googleAppSecretUserPool\\"},{\\"ProviderName\\":\\"SignInWithApple\\",\\"client_id\\":\\"signinwithappleClientIdUserPool\\",\\"team_id\\":\\"signinwithappleTeamIdUserPool\\",\\"key_id\\":\\"signinwithappleKeyIdUserPool\\",\\"private_key\\":\\"signinwithapplePrivateKeyUserPool\\"}]", + } + `); + }); }); 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 ac12cbfe1e3..25d272f7e57 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 @@ -897,7 +897,7 @@ const createEnvSpecificResourceParameters = ( return envSpecificResourceParameters; }; -const createOAuthCredentials = (identityProviders: IdentityProviderType[]): string => { +export const createOAuthCredentials = (identityProviders: IdentityProviderType[]): string => { const credentials = identityProviders.map((idp) => { if (idp.ProviderName === 'SignInWithApple') { return { diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts index a4b84befea8..b5a9e1e59fd 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts @@ -10,6 +10,7 @@ import { getAddAuthHandler, getUpdateAuthHandler } from './handlers/resource-han import { getSupportedServices } from '../supported-services'; import { importResource, importedAuthEnvInit } from './import'; import { AuthContext } from '../../context'; +import { getOAuthObjectFromCognito } from './utils/get-oauth-secrets-from-cognito'; export { importResource } from './import'; @@ -133,6 +134,14 @@ export const updateConfigOnEnvInit = async (context: $TSContext, category: any, if (hostedUIProviderMeta) { currentEnvSpecificValues = getOAuthProviderKeys(currentEnvSpecificValues, resourceParams); + const authParamsFromCognito = await getOAuthObjectFromCognito(context, resourceParams.userPoolName); + // fill in the OAuthProvider Keys from userpool if missing from currentEnvValues + if (authParamsFromCognito) { + currentEnvSpecificValues = { + ...getOAuthProviderKeys({ hostedUIProviderCreds: JSON.stringify(authParamsFromCognito) }, resourceParams), + ...currentEnvSpecificValues, + }; + } } // legacy headless mode (only supports init) diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts new file mode 100644 index 00000000000..b587d6f1596 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts @@ -0,0 +1,50 @@ +import { ICognitoUserPoolService } from '@aws-amplify/amplify-util-import'; +import { $TSContext, stateManager, JSONUtilities } from '@aws-amplify/amplify-cli-core'; +import { IdentityProviderType } from 'aws-sdk/clients/cognitoidentityserviceprovider'; +import { createOAuthCredentials } from '../import'; + +/** + * get oAuth secrets from cognito only for amplify generated userPools + */ +export const getOAuthObjectFromCognito = async ( + context: $TSContext, + userPoolName: string, +): Promise | undefined> => { + const { envName } = stateManager.getLocalEnvInfo(); + const envUserPoolName = `${userPoolName}-${envName}`; + const cognito = (await context.amplify.invokePluginMethod(context, 'awscloudformation', undefined, 'createCognitoUserPoolService', [ + context, + ])) as ICognitoUserPoolService; + const userPool = (await cognito.listUserPools()).filter((userPoolCognito) => userPoolCognito.Name === envUserPoolName)[0]; + const userPoolId = userPool?.Id; + if (userPoolId) { + const identityProviders: IdentityProviderType[] = await cognito.listUserPoolIdentityProviders(userPoolId); + if (identityProviders.length > 0) { + const providerObj = JSONUtilities.parse>(createOAuthCredentials(identityProviders)); + return providerObj; + } + } + return undefined; +}; + +/** + * type for "Facebook"|"Google"|"LoginWithAmazon" + */ +export type GenericProviderDetails = { + ProviderName: string; + client_id: string; + client_secret: string; +}; + +/** + * type for SignInWithApple + */ +export type AppleProviderDetails = { + ProviderName: string; + client_id: string; + team_id: string; + key_id: string; + private_key: string; +}; + +export type OAuthProviderDetails = GenericProviderDetails | AppleProviderDetails; diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts index 9b8b6858e45..656030192c6 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts @@ -1,10 +1,14 @@ /* eslint-disable spellcheck/spell-checker */ import { addAuthWithDefaultSocial, + addFunction, + amplifyPull, amplifyPushAuth, createNewProjectDir, deleteProject, deleteProjectDir, + generateRandomShortId, + getAppId, getProjectMeta, getUserPool, getUserPoolClients, @@ -29,10 +33,11 @@ describe('amplify add auth...', () => { }); it('...should init a project and add auth with defaultSocial', async () => { - await initJSProjectWithProfile(projRoot, defaultsSettings); + await initJSProjectWithProfile(projRoot, { ...defaultsSettings, disableAmplifyAppCreation: false }); await addAuthWithDefaultSocial(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeTruthy(); await amplifyPushAuth(projRoot); + const appId = getAppId(projRoot); const meta = getProjectMeta(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeFalsy(); const authMeta = Object.keys(meta.auth).map((key) => meta.auth[key])[0]; @@ -47,5 +52,12 @@ describe('amplify add auth...', () => { expect(clients[0].UserPoolClient.CallbackURLs[0]).toEqual('https://www.google.com/'); expect(clients[0].UserPoolClient.LogoutURLs[0]).toEqual('https://www.nytimes.com/'); expect(clients[0].UserPoolClient.SupportedIdentityProviders).toHaveLength(5); + + // amplify pull should work + const functionName = `testcorsfunction${generateRandomShortId()}`; + const projRoot2 = await createNewProjectDir('auth2'); + await addFunction(projRoot, { functionTemplate: 'Hello World', name: functionName }, 'nodejs'); + await amplifyPull(projRoot2, { emptyDir: true, appId, envName: 'integtest', yesFlag: true }); + deleteProjectDir(projRoot2); }); }); From 0c736540b238f4bfb3d0d9a8629ce0d201a06aac Mon Sep 17 00:00:00 2001 From: akshay Date: Mon, 24 Apr 2023 12:02:57 -0700 Subject: [PATCH 2/6] fix: creates oauthKeys from userpool for pull use cases --- .../awscloudformation/index.test.ts | 112 +++++++++++++++++- .../awscloudformation/import/index.ts | 2 +- .../provider-utils/awscloudformation/index.ts | 9 ++ .../utils/get-oauth-secrets-from-cognito.ts | 50 ++++++++ .../src/__tests__/auth_2a.test.ts | 14 ++- 5 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts diff --git a/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts b/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts index 7da1118f152..2b560eb658c 100644 --- a/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts +++ b/packages/amplify-category-auth/src/__tests__/provider-utils/awscloudformation/index.test.ts @@ -1,7 +1,9 @@ import { $TSContext } from '@aws-amplify/amplify-cli-core'; import { updateConfigOnEnvInit } from '../../../provider-utils/awscloudformation/index'; +import { getOAuthObjectFromCognito } from '../../../provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito'; jest.mock('@aws-amplify/amplify-environment-parameters'); +jest.mock('../../../provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito'); jest.mock('@aws-amplify/amplify-cli-core', () => ({ ...(jest.requireActual('@aws-amplify/amplify-cli-core') as {}), JSONUtilities: { @@ -18,12 +20,14 @@ jest.mock('@aws-amplify/amplify-cli-core', () => ({ const pluginInstanceMock = jest.fn(); const loadResourceParametersMock = jest.fn().mockReturnValue({ hostedUIProviderMeta: - '[{"ProviderName":"Facebook","authorize_scopes":"email,public_profile","AttributeMapping":{"email":"email","username":"id"}},{"ProviderName":"LoginWithAmazon","authorize_scopes":"profile profile:user_id","AttributeMapping":{"email":"email","username":"user_id"}},{"ProviderName":"Google","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}}]', + '[{"ProviderName":"Facebook","authorize_scopes":"email,public_profile","AttributeMapping":{"email":"email","username":"id"}},{"ProviderName":"LoginWithAmazon","authorize_scopes":"profile profile:user_id","AttributeMapping":{"email":"email","username":"user_id"}},{"ProviderName":"Google","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}},{"ProviderName":"SignInWithApple","authorize_scopes":"openid email profile","AttributeMapping":{"email":"email","username":"sub"}}]', }); const pluginInstance = { loadResourceParameters: loadResourceParametersMock, }; +const getOAuthObjectFromCognitoMock = getOAuthObjectFromCognito as jest.MockedFunction; + // mock context let mockContext = { amplify: { @@ -61,8 +65,106 @@ let mockContext = { }, } as unknown as $TSContext; -test('throws amplify error when auth headless params are missing during pull', async () => { - expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( - `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, - ); +describe('import checks', () => { + test('throws amplify error when auth headless params are missing during pull', async () => { + expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( + `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, + ); + }); +}); + +describe('update config when amplify pull headless command', () => { + test('throws amplify error when auth headless params are missing during pull', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + expect(() => updateConfigOnEnvInit(mockContext, 'auth', 'Cognito')).rejects.toThrowErrorMatchingInlineSnapshot( + `"auth headless is missing the following inputParameters facebookAppIdUserPool, facebookAppSecretUserPool, loginwithamazonAppIdUserPool, loginwithamazonAppSecretUserPool, googleAppIdUserPool, googleAppSecretUserPool"`, + ); + }); + + test('works when secrets are fetched from userpool', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue([ + { + client_id: 'mockClientFacebook', + client_secret: 'mockSecretFacebook', + ProviderName: 'Facebook', + }, + { + client_id: 'mockClientGoogle', + client_secret: 'mockSecretGoogle', + ProviderName: 'Google', + }, + { + client_id: 'mockClientLoginWithAmazon', + client_secret: 'mockSecretLoginWithAmazon', + ProviderName: 'LoginWithAmazon', + }, + { + client_id: 'mockClientSignInWithApple', + team_id: 'mockTeamIdSignInWithApple', + key_id: 'mockKeyIdSignInWithApple', + private_key: 'mockPrivayKeySignInWithApple', + ProviderName: 'SignInWithApple', + }, + ]); + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\"},{\\"ProviderName\\":\\"Google\\"},{\\"ProviderName\\":\\"SignInWithApple\\"}]", + } + `); + }); + + test('works when secrets are present in deployment params', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + mockContext.amplify.loadEnvResourceParameters = jest.fn().mockReturnValue({ + hostedUIProviderCreds: + '[{"ProviderName":"Facebook","client_id":"sdcsdc","client_secret":"bfdsvsr"},{"ProviderName":"Google","client_id":"avearver","client_secret":"vcvereger"},{"ProviderName":"LoginWithAmazon","client_id":"vercvdsavcer","client_secret":"revfdsavrtv"},{"ProviderName":"SignInWithApple","client_id":"vfdvergver","team_id":"ervervre","key_id":"vfdavervfer","private_key":"vaveb"}]', + }); + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\"},{\\"ProviderName\\":\\"Google\\"},{\\"ProviderName\\":\\"SignInWithApple\\"}]", + } + `); + }); + + test('test works when secrets are present in context input params', async () => { + mockContext.input.command = 'pull'; + getOAuthObjectFromCognitoMock.mockResolvedValue(undefined); + mockContext.amplify.loadEnvResourceParameters = jest.fn().mockReturnValue('[]'); + mockContext.exeInfo = { + inputParams: { + yes: true, + categories: { + auth: { + facebookAppIdUserPool: 'mockfacebookAppIdUserPool', + facebookAppSecretUserPool: 'facebookAppSecretUserPool', + googleAppIdUserPool: 'googleAppIdUserPool', + googleAppSecretUserPool: 'googleAppSecretUserPool', + loginwithamazonAppIdUserPool: 'loginwithamazonAppIdUserPool', + loginwithamazonAppSecretUserPool: 'loginwithamazonAppSecretUserPool', + signinwithappleClientIdUserPool: 'signinwithappleClientIdUserPool', + signinwithappleTeamIdUserPool: 'signinwithappleTeamIdUserPool', + signinwithappleKeyIdUserPool: 'signinwithappleKeyIdUserPool', + signinwithapplePrivateKeyUserPool: 'signinwithapplePrivateKeyUserPool', + }, + }, + }, + localEnvInfo: { + projectPath: 'mockProjectPath', + defaultEditor: 'vscode', + envName: 'dev', + noUpdateBackend: false, + }, + }; + const params = await updateConfigOnEnvInit(mockContext, 'auth', 'Cognito'); + expect(params).toMatchInlineSnapshot(` + Object { + "hostedUIProviderCreds": "[{\\"ProviderName\\":\\"Facebook\\",\\"client_id\\":\\"mockfacebookAppIdUserPool\\",\\"client_secret\\":\\"facebookAppSecretUserPool\\"},{\\"ProviderName\\":\\"LoginWithAmazon\\",\\"client_id\\":\\"loginwithamazonAppIdUserPool\\",\\"client_secret\\":\\"loginwithamazonAppSecretUserPool\\"},{\\"ProviderName\\":\\"Google\\",\\"client_id\\":\\"googleAppIdUserPool\\",\\"client_secret\\":\\"googleAppSecretUserPool\\"},{\\"ProviderName\\":\\"SignInWithApple\\",\\"client_id\\":\\"signinwithappleClientIdUserPool\\",\\"team_id\\":\\"signinwithappleTeamIdUserPool\\",\\"key_id\\":\\"signinwithappleKeyIdUserPool\\",\\"private_key\\":\\"signinwithapplePrivateKeyUserPool\\"}]", + } + `); + }); }); 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 ac12cbfe1e3..25d272f7e57 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 @@ -897,7 +897,7 @@ const createEnvSpecificResourceParameters = ( return envSpecificResourceParameters; }; -const createOAuthCredentials = (identityProviders: IdentityProviderType[]): string => { +export const createOAuthCredentials = (identityProviders: IdentityProviderType[]): string => { const credentials = identityProviders.map((idp) => { if (idp.ProviderName === 'SignInWithApple') { return { diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts index a4b84befea8..b5a9e1e59fd 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.ts @@ -10,6 +10,7 @@ import { getAddAuthHandler, getUpdateAuthHandler } from './handlers/resource-han import { getSupportedServices } from '../supported-services'; import { importResource, importedAuthEnvInit } from './import'; import { AuthContext } from '../../context'; +import { getOAuthObjectFromCognito } from './utils/get-oauth-secrets-from-cognito'; export { importResource } from './import'; @@ -133,6 +134,14 @@ export const updateConfigOnEnvInit = async (context: $TSContext, category: any, if (hostedUIProviderMeta) { currentEnvSpecificValues = getOAuthProviderKeys(currentEnvSpecificValues, resourceParams); + const authParamsFromCognito = await getOAuthObjectFromCognito(context, resourceParams.userPoolName); + // fill in the OAuthProvider Keys from userpool if missing from currentEnvValues + if (authParamsFromCognito) { + currentEnvSpecificValues = { + ...getOAuthProviderKeys({ hostedUIProviderCreds: JSON.stringify(authParamsFromCognito) }, resourceParams), + ...currentEnvSpecificValues, + }; + } } // legacy headless mode (only supports init) diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts new file mode 100644 index 00000000000..b587d6f1596 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts @@ -0,0 +1,50 @@ +import { ICognitoUserPoolService } from '@aws-amplify/amplify-util-import'; +import { $TSContext, stateManager, JSONUtilities } from '@aws-amplify/amplify-cli-core'; +import { IdentityProviderType } from 'aws-sdk/clients/cognitoidentityserviceprovider'; +import { createOAuthCredentials } from '../import'; + +/** + * get oAuth secrets from cognito only for amplify generated userPools + */ +export const getOAuthObjectFromCognito = async ( + context: $TSContext, + userPoolName: string, +): Promise | undefined> => { + const { envName } = stateManager.getLocalEnvInfo(); + const envUserPoolName = `${userPoolName}-${envName}`; + const cognito = (await context.amplify.invokePluginMethod(context, 'awscloudformation', undefined, 'createCognitoUserPoolService', [ + context, + ])) as ICognitoUserPoolService; + const userPool = (await cognito.listUserPools()).filter((userPoolCognito) => userPoolCognito.Name === envUserPoolName)[0]; + const userPoolId = userPool?.Id; + if (userPoolId) { + const identityProviders: IdentityProviderType[] = await cognito.listUserPoolIdentityProviders(userPoolId); + if (identityProviders.length > 0) { + const providerObj = JSONUtilities.parse>(createOAuthCredentials(identityProviders)); + return providerObj; + } + } + return undefined; +}; + +/** + * type for "Facebook"|"Google"|"LoginWithAmazon" + */ +export type GenericProviderDetails = { + ProviderName: string; + client_id: string; + client_secret: string; +}; + +/** + * type for SignInWithApple + */ +export type AppleProviderDetails = { + ProviderName: string; + client_id: string; + team_id: string; + key_id: string; + private_key: string; +}; + +export type OAuthProviderDetails = GenericProviderDetails | AppleProviderDetails; diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts index 9b8b6858e45..656030192c6 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts @@ -1,10 +1,14 @@ /* eslint-disable spellcheck/spell-checker */ import { addAuthWithDefaultSocial, + addFunction, + amplifyPull, amplifyPushAuth, createNewProjectDir, deleteProject, deleteProjectDir, + generateRandomShortId, + getAppId, getProjectMeta, getUserPool, getUserPoolClients, @@ -29,10 +33,11 @@ describe('amplify add auth...', () => { }); it('...should init a project and add auth with defaultSocial', async () => { - await initJSProjectWithProfile(projRoot, defaultsSettings); + await initJSProjectWithProfile(projRoot, { ...defaultsSettings, disableAmplifyAppCreation: false }); await addAuthWithDefaultSocial(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeTruthy(); await amplifyPushAuth(projRoot); + const appId = getAppId(projRoot); const meta = getProjectMeta(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeFalsy(); const authMeta = Object.keys(meta.auth).map((key) => meta.auth[key])[0]; @@ -47,5 +52,12 @@ describe('amplify add auth...', () => { expect(clients[0].UserPoolClient.CallbackURLs[0]).toEqual('https://www.google.com/'); expect(clients[0].UserPoolClient.LogoutURLs[0]).toEqual('https://www.nytimes.com/'); expect(clients[0].UserPoolClient.SupportedIdentityProviders).toHaveLength(5); + + // amplify pull should work + const functionName = `testcorsfunction${generateRandomShortId()}`; + const projRoot2 = await createNewProjectDir('auth2'); + await addFunction(projRoot, { functionTemplate: 'Hello World', name: functionName }, 'nodejs'); + await amplifyPull(projRoot2, { emptyDir: true, appId, envName: 'integtest', yesFlag: true }); + deleteProjectDir(projRoot2); }); }); From 7dd48236443a7ff4d08d3b882b72190da63b6c52 Mon Sep 17 00:00:00 2001 From: akshay Date: Fri, 28 Apr 2023 14:23:39 -0700 Subject: [PATCH 3/6] fix: exclude pull test from windows --- .../utils/get-oauth-secrets-from-cognito.ts | 2 +- .../src/__tests__/auth_2g.test.ts | 51 +++++++++++++++++++ scripts/split-e2e-tests-v2.ts | 1 + 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts index b587d6f1596..e3779d959e7 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/get-oauth-secrets-from-cognito.ts @@ -28,7 +28,7 @@ export const getOAuthObjectFromCognito = async ( }; /** - * type for "Facebook"|"Google"|"LoginWithAmazon" + * type for "Facebook" | "Google" | "LoginWithAmazon" */ export type GenericProviderDetails = { ProviderName: string; diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts new file mode 100644 index 00000000000..a0c1297ccc2 --- /dev/null +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts @@ -0,0 +1,51 @@ +/* eslint-disable spellcheck/spell-checker */ +import { + addAuthWithDefaultSocial, + addFunction, + amplifyPull, + amplifyPushAuth, + createNewProjectDir, + deleteProject, + deleteProjectDir, + generateRandomShortId, + getAppId, + getProjectMeta, + getUserPool, + getUserPoolClients, + initJSProjectWithProfile, + isDeploymentSecretForEnvExists, + validateNodeModulesDirRemoval, +} from '@aws-amplify/amplify-e2e-core'; + +const defaultsSettings = { + name: 'authTest', +}; + +describe('amplify add auth...', () => { + let projRoot: string; + let projRoot2 + beforeEach(async () => { + projRoot = await createNewProjectDir('auth'); + projRoot2 = await createNewProjectDir('auth2'); + + }); + + afterEach(async () => { + await deleteProject(projRoot); + deleteProjectDir(projRoot); + deleteProjectDir(projRoot2); + }); + + it('...should init a project and add auth with defaultSocial and pull should succeed', async () => { + await initJSProjectWithProfile(projRoot, { ...defaultsSettings, disableAmplifyAppCreation: false }); + await addAuthWithDefaultSocial(projRoot); + await amplifyPushAuth(projRoot); + const appId = getAppId(projRoot); + const meta = getProjectMeta(projRoot); + // amplify pull should work + const functionName = `testcorsfunction${generateRandomShortId()}`; + await addFunction(projRoot, { functionTemplate: 'Hello World', name: functionName }, 'nodejs'); + await amplifyPull(projRoot2, { emptyDir: true, appId, envName: 'integtest', yesFlag: true }); + await amplifyPushAuth(projRoot2); + }); +}); diff --git a/scripts/split-e2e-tests-v2.ts b/scripts/split-e2e-tests-v2.ts index f1ea3111c16..293bd14d882 100644 --- a/scripts/split-e2e-tests-v2.ts +++ b/scripts/split-e2e-tests-v2.ts @@ -105,6 +105,7 @@ const TEST_EXCLUSIONS: { l: string[]; w: string[] } = { 'src/__tests__/storage-simulator/S3server.test.ts', 'src/__tests__/amplify-app.test.ts', // failing in parsing JSON strings on powershell + 'src/__tests__/auth_2g.test.ts', 'src/__tests__/auth_12.test.ts', 'src/__tests__/datastore-modelgen.test.ts', 'src/__tests__/diagnose.test.ts', From 52fd7a6b9dde1ee17de85154e5236aefde836518 Mon Sep 17 00:00:00 2001 From: akshay Date: Fri, 28 Apr 2023 14:40:39 -0700 Subject: [PATCH 4/6] chore: revert test file changes --- .../src/__tests__/auth_2a.test.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts index 656030192c6..9b8b6858e45 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2a.test.ts @@ -1,14 +1,10 @@ /* eslint-disable spellcheck/spell-checker */ import { addAuthWithDefaultSocial, - addFunction, - amplifyPull, amplifyPushAuth, createNewProjectDir, deleteProject, deleteProjectDir, - generateRandomShortId, - getAppId, getProjectMeta, getUserPool, getUserPoolClients, @@ -33,11 +29,10 @@ describe('amplify add auth...', () => { }); it('...should init a project and add auth with defaultSocial', async () => { - await initJSProjectWithProfile(projRoot, { ...defaultsSettings, disableAmplifyAppCreation: false }); + await initJSProjectWithProfile(projRoot, defaultsSettings); await addAuthWithDefaultSocial(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeTruthy(); await amplifyPushAuth(projRoot); - const appId = getAppId(projRoot); const meta = getProjectMeta(projRoot); expect(isDeploymentSecretForEnvExists(projRoot, 'integtest')).toBeFalsy(); const authMeta = Object.keys(meta.auth).map((key) => meta.auth[key])[0]; @@ -52,12 +47,5 @@ describe('amplify add auth...', () => { expect(clients[0].UserPoolClient.CallbackURLs[0]).toEqual('https://www.google.com/'); expect(clients[0].UserPoolClient.LogoutURLs[0]).toEqual('https://www.nytimes.com/'); expect(clients[0].UserPoolClient.SupportedIdentityProviders).toHaveLength(5); - - // amplify pull should work - const functionName = `testcorsfunction${generateRandomShortId()}`; - const projRoot2 = await createNewProjectDir('auth2'); - await addFunction(projRoot, { functionTemplate: 'Hello World', name: functionName }, 'nodejs'); - await amplifyPull(projRoot2, { emptyDir: true, appId, envName: 'integtest', yesFlag: true }); - deleteProjectDir(projRoot2); }); }); From 1f990e43261b1cf3ca9362783a0f3b6ab1bddeef Mon Sep 17 00:00:00 2001 From: akshay Date: Fri, 28 Apr 2023 15:00:14 -0700 Subject: [PATCH 5/6] chore: fixes codeql warnings --- packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts index 2f52e16d362..5e33813a4ba 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts @@ -9,12 +9,7 @@ import { deleteProjectDir, generateRandomShortId, getAppId, - getProjectMeta, - getUserPool, - getUserPoolClients, - initJSProjectWithProfile, - isDeploymentSecretForEnvExists, - validateNodeModulesDirRemoval, + initJSProjectWithProfile } from '@aws-amplify/amplify-e2e-core'; const defaultsSettings = { @@ -40,11 +35,9 @@ describe('amplify add auth...', () => { await addAuthWithDefaultSocial(projRoot); await amplifyPushAuth(projRoot); const appId = getAppId(projRoot); - const meta = getProjectMeta(projRoot); // amplify pull should work const functionName = `testcorsfunction${generateRandomShortId()}`; await addFunction(projRoot, { functionTemplate: 'Hello World', name: functionName }, 'nodejs'); await amplifyPull(projRoot2, { emptyDir: true, appId, envName: 'integtest', yesFlag: true }); - await amplifyPushAuth(projRoot2); }); }); From 23d38ad6eff32085f887f1ddc723e0579935a084 Mon Sep 17 00:00:00 2001 From: akshay Date: Mon, 1 May 2023 10:14:20 -0700 Subject: [PATCH 6/6] chore: fixes lint --- packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts index 5e33813a4ba..9837f5f88ae 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2g.test.ts @@ -9,7 +9,7 @@ import { deleteProjectDir, generateRandomShortId, getAppId, - initJSProjectWithProfile + initJSProjectWithProfile, } from '@aws-amplify/amplify-e2e-core'; const defaultsSettings = {