Skip to content

Commit

Permalink
chore(core): enforcing https on user and identity pools endpoinds
Browse files Browse the repository at this point in the history
  • Loading branch information
HuiSF committed Jan 14, 2025
1 parent 50c0dd6 commit f358adf
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 50 deletions.
82 changes: 45 additions & 37 deletions packages/core/__tests__/singleton/Auth/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,59 @@
import { ConsoleLogger } from '../../../src/Logger';
import { AuthClass } from '../../../src/singleton/Auth';
import { isSecureServiceEndpoint } from '../../../src/utils';

jest.mock('../../../src/Logger', () => {
const warn = jest.fn();
jest.mock('../../../src/utils');

return {
ConsoleLogger: jest.fn(() => ({
warn,
})),
};
});
const mockIsSecureServiceEndpoint = jest.mocked(isSecureServiceEndpoint);

describe('Auth', () => {
const auth = new AuthClass();
const logger = new ConsoleLogger('Auth');
const mockedWarn = logger.warn as jest.Mock;

const mockConfig = {
userPoolClientId: 'userPoolClientId',
userPoolId: 'userPoolId',
identityPoolId: 'identityPoolId',
};

const expectedErrorMatcher = /must use HTTPS protocol\.$/;

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

describe('configure', () => {
const mockConfig = {
userPoolClientId: 'userPoolClientId',
userPoolId: 'userPoolId',
};

it('prints warning when use custom endpoint for Cognito User Pool', () => {
auth.configure({
Cognito: {
...mockConfig,
userPoolEndpoint: 'https://custom-endpoint.com',
},
});

expect(mockedWarn).toHaveBeenCalledWith(
expect.stringContaining('Amazon Cognito User Pool'),
it('throws when custom user pool endpoint is not secure', () => {
const nonSecureUserPoolEndpoint = 'http://example.com';
mockIsSecureServiceEndpoint.mockReturnValueOnce(false);

expect(() => {
auth.configure({
Cognito: {
...mockConfig,
userPoolEndpoint: nonSecureUserPoolEndpoint,
},
});
}).toThrow(expectedErrorMatcher);
expect(mockIsSecureServiceEndpoint).toHaveBeenCalledWith(
nonSecureUserPoolEndpoint,
);
});

it('prints warning when use custom endpoint for Cognito Identity Pool', () => {
auth.configure({
Cognito: {
...mockConfig,
identityPoolId: 'identityPoolId',
identityPoolEndpoint: 'https://custom-endpoint.com',
},
});

expect(mockedWarn).toHaveBeenCalledWith(
expect.stringContaining('Amazon Cognito Identity Pool'),
it('throws when custom identity pool endpoint is not secure', () => {
const nonSecureIdentityPoolEndpoint = 'http://example.com';
mockIsSecureServiceEndpoint.mockReturnValueOnce(true); // good user pool endpoint
mockIsSecureServiceEndpoint.mockReturnValueOnce(false); // bad identity pool endpoint

expect(() => {
auth.configure({
Cognito: {
...mockConfig,
userPoolClientId: 'https://example.com',
identityPoolEndpoint: nonSecureIdentityPoolEndpoint,
},
});
}).toThrow(expectedErrorMatcher);
expect(mockIsSecureServiceEndpoint).toHaveBeenCalledWith(
nonSecureIdentityPoolEndpoint,
);
});
});
Expand Down
9 changes: 8 additions & 1 deletion packages/core/__tests__/singleton/Singleton.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,14 @@ describe('Session tests', () => {
secretAccessKey: 'secretAccessKeyValue',
};
Amplify.configure(
{},
{
Auth: {
Cognito: {
userPoolId: 'userPoolId',
userPoolClientId: 'userPoolClientId',
},
},
},
{
Auth: {
credentialsProvider: {
Expand Down
15 changes: 15 additions & 0 deletions packages/core/__tests__/utils/isSecureServiceEndpoint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { isSecureServiceEndpoint } from '../../src/utils/isSecureServiceEndpoint';

describe('isSecureServiceEndpoint', () => {
const a = isSecureServiceEndpoint;

test.each([
['not-a-url', false],
['http://example.com', false],
['https://example.com', true],
['http://localhost:3000', true],
['http://127.0.0.1:3000', true],
])('isSecureServiceEndpoint(%s) returns %s', (input, expected) => {
expect(a(input)).toBe(expected);
});
});
27 changes: 15 additions & 12 deletions packages/core/src/singleton/Auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { ConsoleLogger } from '../../Logger';
import { isSecureServiceEndpoint } from '../../utils';

import {
AuthConfig,
Expand All @@ -11,7 +11,6 @@ import {
LibraryAuthOptions,
} from './types';

const logger = new ConsoleLogger('Auth');
export class AuthClass {
private authConfig?: AuthConfig;
private authOptions?: LibraryAuthOptions;
Expand All @@ -30,21 +29,25 @@ export class AuthClass {
authResourcesConfig: AuthConfig,
authOptions?: LibraryAuthOptions,
): void {
this.authConfig = authResourcesConfig;
this.authOptions = authOptions;
const userPoolEndpoint = authResourcesConfig.Cognito?.userPoolEndpoint;
const identityPoolEndpoint =
authResourcesConfig.Cognito?.identityPoolEndpoint;

if (authResourcesConfig && authResourcesConfig.Cognito?.userPoolEndpoint) {
logger.warn(getCustomEndpointWarningMessage('Amazon Cognito User Pool'));
if (userPoolEndpoint && !isSecureServiceEndpoint(userPoolEndpoint)) {
throw new Error(getNonHttpsEndpointErrorMessage('`userPoolEndpoint`'));
}

if (
authResourcesConfig &&
authResourcesConfig.Cognito?.identityPoolEndpoint
identityPoolEndpoint &&
!isSecureServiceEndpoint(identityPoolEndpoint)
) {
logger.warn(
getCustomEndpointWarningMessage('Amazon Cognito Identity Pool'),
throw new Error(
getNonHttpsEndpointErrorMessage('`identityPoolEndpoint`'),
);
}

this.authConfig = authResourcesConfig;
this.authOptions = authOptions;
}

/**
Expand Down Expand Up @@ -111,5 +114,5 @@ export class AuthClass {
}
}

const getCustomEndpointWarningMessage = (target: string): string =>
`You are using a custom Amazon ${target} endpoint, ensure the endpoint is correct.`;
const getNonHttpsEndpointErrorMessage = (target: string): string =>
`${target} must use HTTPS protocol.`;
1 change: 1 addition & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export { urlSafeEncode } from './urlSafeEncode';
export { deepFreeze } from './deepFreeze';
export { deDupeAsyncFunction } from './deDupeAsyncFunction';
export { isTokenExpired } from './isTokenExpired';
export { isSecureServiceEndpoint } from './isSecureServiceEndpoint';
21 changes: 21 additions & 0 deletions packages/core/src/utils/isSecureServiceEndpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { AmplifyUrl } from './amplifyUrl';

export const isSecureServiceEndpoint = (input: string): boolean => {
try {
const url = new AmplifyUrl(input);

if (url.protocol === 'http:' && !isLocalhost(url.hostname)) {
return false;
}

return true;
} catch {
return false;
}
};

const isLocalhost = (hostname: string): boolean =>
hostname === 'localhost' || hostname === '127.0.0.1';

0 comments on commit f358adf

Please sign in to comment.