Skip to content

Commit

Permalink
fix(auth): custom userPoolEndpoint cannot be applied on the server-…
Browse files Browse the repository at this point in the history
…side (#13739)

* chore(auth): add the service client factories into foundation

* refactor(auth): use service clients in foundation

* chore(auth): refactor existing unit test suites

* chore(auth): add unit tests for newly added modules

* chore(auth): add missing license header

* chore(repo): update sdk type extractio destination for the auth package

* chore: resolve most of the comments from cshfang

* chore: resolve most of the comments from cshfang cont.

* chore: resolve most of the comments from cshfang cont. 2

* chore: flatten out the /core from foundation

* chore: update outdate test file name to match the src changes
  • Loading branch information
HuiSF committed Sep 10, 2024
1 parent 1fea052 commit a76b594
Show file tree
Hide file tree
Showing 117 changed files with 2,775 additions and 987 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { AmplifyUrl } from '@aws-amplify/core/internals/utils';

import { cognitoUserPoolEndpointResolver } from '../../src/foundation/cognitoUserPoolEndpointResolver';
import { COGNITO_IDP_SERVICE_NAME } from '../../src/foundation/constants';

describe('cognitoUserPoolEndpointResolver', () => {
it('should return the Cognito User Pool endpoint', () => {
const region = 'us-west-2';
const { url } = cognitoUserPoolEndpointResolver({ region });

expect(url instanceof AmplifyUrl).toBe(true);
expect(url.toString()).toEqual(
`https://${COGNITO_IDP_SERVICE_NAME}.us-west-2.amazonaws.com/`,
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers';

import * as serviceClients from '../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider';
import { DEFAULT_SERVICE_CLIENT_API_CONFIG } from '../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/constants';

import { mockServiceClientAPIConfig } from './testUtils/data';

jest.mock('@aws-amplify/core/internals/aws-client-utils/composers', () => ({
...jest.requireActual(
'@aws-amplify/core/internals/aws-client-utils/composers',
),
composeServiceApi: jest.fn(),
}));

export const mockComposeServiceApi = jest.mocked(composeServiceApi);

describe('service clients', () => {
const serviceClientFactories = Object.keys(serviceClients);

afterEach(() => {
mockComposeServiceApi.mockClear();
});

test.each(serviceClientFactories)(
'factory `%s` should invoke composeServiceApi with expected parameters',
serviceClientFactory => {
// eslint-disable-next-line import/namespace
serviceClients[serviceClientFactory](mockServiceClientAPIConfig);

expect(mockComposeServiceApi).toHaveBeenCalledWith(
expect.any(Function),
expect.any(Function),
expect.any(Function),
expect.objectContaining({
...DEFAULT_SERVICE_CLIENT_API_CONFIG,
...mockServiceClientAPIConfig,
}),
);
},
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { unauthenticatedHandler } from '@aws-amplify/core/internals/aws-client-utils';
import { composeTransferHandler } from '@aws-amplify/core/internals/aws-client-utils/composers';

import { cognitoUserPoolTransferHandler } from '../../../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/shared/handler';

jest.mock('@aws-amplify/core/internals/aws-client-utils/composers');
jest.mock('@aws-amplify/core/internals/aws-client-utils');

const mockComposeTransferHandler = jest.mocked(composeTransferHandler);
const mockUnauthenticatedHandler = jest.mocked(unauthenticatedHandler);

describe('cognitoUserPoolTransferHandler', () => {
beforeAll(() => {
// need to make sure cognitoUserPoolTransferHandler is imported and used in
// the scope of the test
const _ = cognitoUserPoolTransferHandler;
});

it('adds the disableCacheMiddlewareFactory at module loading', () => {
expect(mockComposeTransferHandler).toHaveBeenCalledTimes(1);

const [core, middleware] = mockComposeTransferHandler.mock.calls[0];

expect(core).toStrictEqual(mockUnauthenticatedHandler);
expect(middleware).toHaveLength(1);

const disableCacheMiddlewareFactory = middleware[0] as any;
const disableCacheMiddlewarePendingNext = disableCacheMiddlewareFactory();

const mockNext = jest.fn();
const disableCacheMiddleware = disableCacheMiddlewarePendingNext(mockNext);
const mockRequest = {
headers: {},
};

disableCacheMiddleware(mockRequest);

expect(mockNext).toHaveBeenCalledWith(mockRequest);
expect(mockRequest.headers).toEqual({
'cache-control': 'no-store',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
HttpResponse,
parseJsonError,
} from '@aws-amplify/core/internals/aws-client-utils';

import { createEmptyResponseDeserializer } from '../../../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/shared/serde/createEmptyResponseDeserializer';
import { AuthError } from '../../../../../../../src/errors/AuthError';

jest.mock('@aws-amplify/core/internals/aws-client-utils');

const mockParseJsonError = jest.mocked(parseJsonError);

describe('createEmptyResponseDeserializer created response deserializer', () => {
const deserializer = createEmptyResponseDeserializer();

it('returns undefined for 2xx status code', async () => {
const response: HttpResponse = {
statusCode: 200,
body: {
json: () => Promise.resolve({}),
blob: () => Promise.resolve(new Blob()),
text: () => Promise.resolve(''),
},
headers: {},
};
const output = await deserializer(response);

expect(output).toBeUndefined();
});

it('throws AuthError for 4xx status code', async () => {
const expectedErrorName = 'TestError';
const expectedErrorMessage = 'TestErrorMessage';
const expectedError = new Error(expectedErrorMessage);
expectedError.name = expectedErrorName;

mockParseJsonError.mockReturnValueOnce(expectedError as any);
const response: HttpResponse = {
statusCode: 400,
body: {
json: () => Promise.resolve({}),
blob: () => Promise.resolve(new Blob()),
text: () => Promise.resolve(''),
},
headers: {},
};

expect(deserializer(response as any)).rejects.toThrow(
new AuthError({
name: expectedErrorName,
message: expectedErrorMessage,
}),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
HttpResponse,
parseJsonBody,
parseJsonError,
} from '@aws-amplify/core/internals/aws-client-utils';

import { createUserPoolDeserializer } from '../../../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/shared/serde/createUserPoolDeserializer';
import { AuthError } from '../../../../../../../src/errors/AuthError';

jest.mock('@aws-amplify/core/internals/aws-client-utils');

const mockParseJsonBody = jest.mocked(parseJsonBody);
const mockParseJsonError = jest.mocked(parseJsonError);

describe('buildUserPoolDeserializer created response deserializer', () => {
const deserializer = createUserPoolDeserializer();

it('returns body for 2xx status code', async () => {
const expectedBody = { test: 'test' };
mockParseJsonBody.mockResolvedValueOnce(expectedBody);
const response: HttpResponse = {
statusCode: 200,
body: {
json: () => Promise.resolve({}),
blob: () => Promise.resolve(new Blob()),
text: () => Promise.resolve(''),
},
headers: {},
};
const output = await deserializer(response);

expect(output).toStrictEqual(expectedBody);
});

it('throws AuthError for 4xx status code', async () => {
const expectedErrorName = 'TestError';
const expectedErrorMessage = 'TestErrorMessage';
const expectedError = new Error(expectedErrorMessage);
expectedError.name = expectedErrorName;

mockParseJsonError.mockReturnValueOnce(expectedError as any);
const response: HttpResponse = {
statusCode: 400,
body: {
json: () => Promise.resolve({}),
blob: () => Promise.resolve(new Blob()),
text: () => Promise.resolve(''),
},
headers: {},
};

expect(deserializer(response as any)).rejects.toThrow(
new AuthError({
name: expectedErrorName,
message: expectedErrorMessage,
}),
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AmplifyUrl } from '@aws-amplify/core/internals/utils';

import { createUserPoolSerializer } from '../../../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/shared/serde/createUserPoolSerializer';

describe('createUserPoolSerializer created request serializer', () => {
test.each(['SignUp', 'InitiateAuth', 'RevokeToken'] as const)(
`it serializes requests from operation %s`,
operation => {
const testInput = { testBody: 'testBody' };
const testEndpoint = {
url: new AmplifyUrl('http://test.com'),
};
const serializer = createUserPoolSerializer(operation);
const result = serializer(testInput, testEndpoint);

expect(result).toEqual({
method: 'POST',
url: testEndpoint.url,
headers: {
'content-type': 'application/x-amz-json-1.1',
'x-amz-target': `AWSCognitoIdentityProviderService.${operation}`,
},
body: JSON.stringify(testInput),
});
},
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ServiceClientFactoryInput } from '../../../../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/types';

export const mockServiceClientAPIConfig: ServiceClientFactoryInput = {
endpointResolver: jest.fn() as jest.MockedFunction<
ServiceClientFactoryInput['endpointResolver']
>,
};
47 changes: 47 additions & 0 deletions packages/auth/__tests__/foundation/parsers/regionParsers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AuthError } from '../../../src/errors/AuthError';
import {
getRegionFromIdentityPoolId,
getRegionFromUserPoolId,
} from '../../../src/foundation/parsers/regionParsers';

describe('getRegionFromIdentityPoolId()', () => {
it('returns the region from the identity pool id', () => {
const identityPoolId = 'us-west-2:12345678-1234-1234-1234-123456789012';
const region = getRegionFromIdentityPoolId(identityPoolId);
expect(region).toEqual('us-west-2');
});

test.each([undefined, 'invalid-id-123'])(
`throws an error when the identity pool id is invalid as %p`,
identityPoolId => {
expect(() => getRegionFromIdentityPoolId(identityPoolId)).toThrow(
new AuthError({
name: 'InvalidIdentityPoolIdException',
message: 'Invalid identity pool id provided.',
recoverySuggestion:
'Make sure a valid identityPoolId is given in the config.',
}),
);
},
);
});

describe('getRegionFromUserPoolId()', () => {
it('should return the region from the user pool id', () => {
const userPoolId = 'us-west-2_12345678';
const region = getRegionFromUserPoolId(userPoolId);
expect(region).toEqual('us-west-2');
});

test.each([undefined, 'invalid-id-123'])(
`throws an error when the user pool id is invalid as %p`,
userPoolId => {
expect(() => getRegionFromUserPoolId(userPoolId)).toThrow(
new AuthError({
name: 'InvalidUserPoolId',
message: 'Invalid user pool id provided.',
}),
);
},
);
});
37 changes: 20 additions & 17 deletions packages/auth/__tests__/providers/cognito/autoSignIn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ import {
signUp,
} from '../../../src/providers/cognito';
import { autoSignIn } from '../../../src/providers/cognito/apis/autoSignIn';
import * as signUpClient from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider';
import {
RespondToAuthChallengeCommandOutput,
SignUpCommandOutput,
} from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider/types';
import * as initiateAuthHelpers from '../../../src/providers/cognito/utils/signInHelpers';
import { AuthError } from '../../../src/errors/AuthError';
import { createSignUpClient } from '../../../src/foundation/factories/serviceClients/cognitoIdentityProvider';
import { RespondToAuthChallengeCommandOutput } from '../../../src/foundation/factories/serviceClients/cognitoIdentityProvider/types';

import { authAPITestParams } from './testUtils/authApiTestParams';

Expand All @@ -23,6 +20,9 @@ jest.mock('@aws-amplify/core/internals/utils', () => ({
...jest.requireActual('@aws-amplify/core/internals/utils'),
isBrowser: jest.fn(() => false),
}));
jest.mock(
'../../../src/foundation/factories/serviceClients/cognitoIdentityProvider',
);

const authConfig = {
Cognito: {
Expand All @@ -35,27 +35,30 @@ Amplify.configure({
Auth: authConfig,
});
describe('Auto sign-in API Happy Path Cases:', () => {
let signUpSpy;
let handleUserSRPAuthflowSpy;
let handleUserSRPAuthFlowSpy: jest.SpyInstance;

const mockSignUp = jest.fn();
const mockCreateSignUpClient = jest.mocked(createSignUpClient);

const { user1 } = authAPITestParams;
beforeEach(async () => {
signUpSpy = jest
.spyOn(signUpClient, 'signUp')
.mockImplementationOnce(
async () => ({ UserConfirmed: true }) as SignUpCommandOutput,
);
mockSignUp.mockResolvedValueOnce({ UserConfirmed: true });
mockCreateSignUpClient.mockReturnValueOnce(mockSignUp);

handleUserSRPAuthflowSpy = jest
handleUserSRPAuthFlowSpy = jest
.spyOn(initiateAuthHelpers, 'handleUserSRPAuthFlow')
.mockImplementationOnce(
async (): Promise<RespondToAuthChallengeCommandOutput> =>
authAPITestParams.RespondToAuthChallengeCommandOutput,
);
});

afterEach(() => {
signUpSpy.mockClear();
handleUserSRPAuthflowSpy.mockClear();
mockSignUp.mockClear();
mockCreateSignUpClient.mockClear();
handleUserSRPAuthFlowSpy.mockClear();
});

test('signUp should enable autoSignIn and return COMPLETE_AUTO_SIGN_IN step', async () => {
const resp = await signUp({
username: user1.username,
Expand All @@ -71,13 +74,13 @@ describe('Auto sign-in API Happy Path Cases:', () => {
signUpStep: 'COMPLETE_AUTO_SIGN_IN',
},
});
expect(signUpSpy).toHaveBeenCalledTimes(1);
expect(mockSignUp).toHaveBeenCalledTimes(1);
});

test('Auto sign-in should resolve to a signIn output', async () => {
const signInOutput = await autoSignIn();
expect(signInOutput).toEqual(authAPITestParams.signInResult());
expect(handleUserSRPAuthflowSpy).toHaveBeenCalledTimes(1);
expect(handleUserSRPAuthFlowSpy).toHaveBeenCalledTimes(1);
});
});

Expand Down
Loading

0 comments on commit a76b594

Please sign in to comment.