From 1aef1ee0e65d1489e4d86d19a6d017f852f3bafd Mon Sep 17 00:00:00 2001 From: David McAfee Date: Wed, 24 Apr 2024 12:02:15 -0500 Subject: [PATCH 1/5] fix(data): tests for error response fix (#13279) --- .../internals/generateClient.test.ts | 330 +++++++++++++++--- packages/api-graphql/package.json | 2 +- packages/api-graphql/src/types/index.ts | 2 +- packages/aws-amplify/package.json | 2 +- yarn.lock | 14 +- 5 files changed, 296 insertions(+), 54 deletions(-) diff --git a/packages/api-graphql/__tests__/internals/generateClient.test.ts b/packages/api-graphql/__tests__/internals/generateClient.test.ts index 7f547380abb..629e37efed3 100644 --- a/packages/api-graphql/__tests__/internals/generateClient.test.ts +++ b/packages/api-graphql/__tests__/internals/generateClient.test.ts @@ -376,7 +376,7 @@ describe('generateClient', () => { }); const client = generateClient({ amplify: Amplify }); - const { data } = await client.models.Todo.list({ + await client.models.Todo.list({ filter: { name: { contains: 'name' } }, limit: 5, }); @@ -402,7 +402,7 @@ describe('generateClient', () => { const client = generateClient({ amplify: Amplify }); - const { data } = await client.models.ThingWithCustomPk.list({ + await client.models.ThingWithCustomPk.list({ cpk_cluster_key: '1', sortDirection: 'ASC', }); @@ -428,7 +428,7 @@ describe('generateClient', () => { const client = generateClient({ amplify: Amplify }); - const { data } = await client.models.ThingWithCustomPk.list({ + await client.models.ThingWithCustomPk.list({ cpk_cluster_key: '1', sortDirection: 'DESC', }); @@ -645,7 +645,11 @@ describe('generateClient', () => { }, }); - const { data: notes } = await data.notes(); + expect(data).not.toBeNull(); + + expect(data).not.toBeNull(); + + const { data: notes } = await data!.notes(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); @@ -691,7 +695,9 @@ describe('generateClient', () => { }, }); - const { data: notes } = await data.notes({ nextToken: 'some-token' }); + expect(data).not.toBeNull(); + + const { data: notes } = await data!.notes({ nextToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); @@ -737,7 +743,9 @@ describe('generateClient', () => { }, }); - const { data: notes } = await data.notes({ limit: 5 }); + expect(data).not.toBeNull(); + + const { data: notes } = await data!.notes({ limit: 5 }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); @@ -780,7 +788,9 @@ describe('generateClient', () => { }, }); - const { data: todo } = await data.todo(); + expect(data).not.toBeNull(); + + const { data: todo } = await data!.todo(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); @@ -821,7 +831,9 @@ describe('generateClient', () => { }, }); - const { data: todo } = await data.meta(); + expect(data).not.toBeNull(); + + const { data: todo } = await data!.meta(); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); @@ -835,6 +847,186 @@ describe('generateClient', () => { }); }); + describe('error handling', () => { + test('create() returns null with errors property', async () => { + const expectedErrors = [ + { + path: ['createTodo'], + data: null, + errorType: 'Unauthorized', + errorInfo: null, + locations: [ + { + line: 2, + column: 3, + sourceName: null, + }, + ], + message: 'Not Authorized to access createTodo on type Mutation', + }, + ]; + + const spy = mockApiResponse({ + data: { + getTodo: null, + }, + errors: expectedErrors, + }); + + const client = generateClient({ amplify: Amplify }); + const { data, errors } = await client.models.Todo.create({ + id: 'does not matter', + }); + + expect(data).toBeNull(); + expect(errors?.length).toBe(1); + expect(errors).toEqual(expectedErrors); + }); + + test('returns object in which `data` is null and `errors` contains expected error', async () => { + const expectedErrors = [ + { + path: ['getTodo'], + data: null, + errorType: 'Unauthorized', + errorInfo: null, + locations: [ + { + line: 2, + column: 3, + sourceName: null, + }, + ], + message: 'Not Authorized to access getTodo on type Query', + }, + ]; + + const spy = mockApiResponse({ + data: { + getTodo: null, + }, + errors: expectedErrors, + }); + + const client = generateClient({ amplify: Amplify }); + const { data, errors } = await client.models.Todo.get({ + id: 'does not matter', + }); + + expect(data).toBeNull(); + expect(errors?.length).toBe(1); + expect(errors).toEqual(expectedErrors); + }); + + test('update() returns null with errors property', async () => { + const expectedErrors = [ + { + path: ['updateTodo'], + data: null, + errorType: 'Unauthorized', + errorInfo: null, + locations: [ + { + line: 2, + column: 3, + sourceName: null, + }, + ], + message: 'Not Authorized to access updateTodo on type Mutation', + }, + ]; + + mockApiResponse({ + data: { + updateTodo: null, + }, + errors: expectedErrors, + }); + + const client = generateClient({ amplify: Amplify }); + + const { data, errors } = await client.models.Todo.update({ + id: 'some_id', + name: 'does not matter', + }); + + expect(data).toBeNull(); + expect(errors?.length).toBe(1); + expect(errors).toEqual(expectedErrors); + }); + + test('delete() returns null with errors property', async () => { + const expectedErrors = [ + { + path: ['deleteTodo'], + data: null, + errorType: 'Unauthorized', + errorInfo: null, + locations: [ + { + line: 2, + column: 3, + sourceName: null, + }, + ], + message: 'Not Authorized to access deleteTodo on type Mutation', + }, + ]; + + const spy = mockApiResponse({ + data: { + deleteTodo: null, + }, + errors: expectedErrors, + }); + + const client = generateClient({ amplify: Amplify }); + + const { data, errors } = await client.models.Todo.delete({ + id: 'some_id', + }); + + expect(data).toBeNull(); + expect(errors?.length).toBe(1); + expect(errors).toEqual(expectedErrors); + }); + + test('list() returns empty list with errors property', async () => { + const expectedErrors = [ + { + path: ['listTodos'], + data: null, + errorType: 'Unauthorized', + errorInfo: null, + locations: [ + { + line: 2, + column: 3, + sourceName: null, + }, + ], + message: 'Not Authorized to access listTodos on type Query', + }, + ]; + + mockApiResponse({ + data: { + listTodos: null, + }, + errors: expectedErrors, + }); + + const client = generateClient({ amplify: Amplify }); + const { data, errors } = await client.models.Todo.list({ + filter: { name: { contains: 'name' } }, + }); + + expect(data.length).toBe(0); + expect(errors?.length).toBe(1); + expect(errors).toEqual(expectedErrors); + }); + }); + describe('basic model operations - authMode: CuP override at the time of operation', () => { beforeEach(() => { jest.clearAllMocks(); @@ -1119,7 +1311,9 @@ describe('generateClient', () => { }, }); - await data.notes(); + expect(data).not.toBeNull(); + + await data!.notes(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1157,7 +1351,9 @@ describe('generateClient', () => { }, }); - await data.todo(); + expect(data).not.toBeNull(); + + await data!.todo(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1194,7 +1390,9 @@ describe('generateClient', () => { }, }); - await data.meta(); + expect(data).not.toBeNull(); + + await data!.meta(); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -1235,7 +1433,9 @@ describe('generateClient', () => { }, }); - await data.notes({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.notes({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1273,7 +1473,9 @@ describe('generateClient', () => { }, }); - await data.todo({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.todo({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1310,7 +1512,9 @@ describe('generateClient', () => { }, }); - await data.meta({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.meta({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -1609,7 +1813,9 @@ describe('generateClient', () => { }, }); - await data.notes(); + expect(data).not.toBeNull(); + + await data!.notes(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1648,7 +1854,9 @@ describe('generateClient', () => { }, }); - await data.todo(); + expect(data).not.toBeNull(); + + await data!.todo(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1686,7 +1894,9 @@ describe('generateClient', () => { }, }); - await data.meta(); + expect(data).not.toBeNull(); + + await data!.meta(); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -1727,7 +1937,9 @@ describe('generateClient', () => { }, }); - await data.notes({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.notes({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1765,7 +1977,9 @@ describe('generateClient', () => { }, }); - await data.todo({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.todo({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -1802,7 +2016,9 @@ describe('generateClient', () => { }, }); - await data.meta({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.meta({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -2101,7 +2317,9 @@ describe('generateClient', () => { }, }); - await data.notes(); + expect(data).not.toBeNull(); + + await data!.notes(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2137,7 +2355,9 @@ describe('generateClient', () => { }, }); - await data.todo(); + expect(data).not.toBeNull(); + + await data!.todo(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2172,7 +2392,9 @@ describe('generateClient', () => { }, }); - await data.meta(); + expect(data).not.toBeNull(); + + await data!.meta(); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -2213,7 +2435,9 @@ describe('generateClient', () => { }, }); - await data.notes({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.notes({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2249,7 +2473,9 @@ describe('generateClient', () => { }, }); - await data.todo({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.todo({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2284,7 +2510,9 @@ describe('generateClient', () => { }, }); - await data.meta({ authMode: 'apiKey' }); + expect(data).not.toBeNull(); + + await data!.meta({ authMode: 'apiKey' }); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -2590,7 +2818,9 @@ describe('generateClient', () => { }, }); - await data.notes(); + expect(data).not.toBeNull(); + + await data!.notes(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2627,7 +2857,9 @@ describe('generateClient', () => { }, }); - await data.todo(); + expect(data).not.toBeNull(); + + await data!.todo(); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2663,7 +2895,9 @@ describe('generateClient', () => { }, }); - await data.meta(); + expect(data).not.toBeNull(); + + await data!.meta(); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -2704,7 +2938,9 @@ describe('generateClient', () => { }, }); - await data.notes({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.notes({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2740,7 +2976,9 @@ describe('generateClient', () => { }, }); - await data.todo({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.todo({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildNotesSpy)).toMatchSnapshot(); }); @@ -2775,7 +3013,9 @@ describe('generateClient', () => { }, }); - await data.meta({ authMode: 'lambda', authToken: 'some-token' }); + expect(data).not.toBeNull(); + + await data!.meta({ authMode: 'lambda', authToken: 'some-token' }); expect(normalizePostGraphqlCalls(getChildMetaSpy)).toMatchSnapshot(); }); @@ -5331,6 +5571,7 @@ describe('generateClient', () => { const mockReturnData = { sku: 'sku', factoryId: 'factoryId', + warehouseId: 'warehouseId', description: 'description', trackingMeta: { productMeta: { @@ -5357,10 +5598,9 @@ describe('generateClient', () => { }); expect(normalizePostGraphqlCalls(spy)).toMatchSnapshot(); - expect(result?.data).toEqual({ - ...mockReturnData, - warehouse: expect.any(Function), - }); + expect(result?.data).toMatchObject( + expect.objectContaining(mockReturnData), + ); }); test('can query with returnType of string', async () => { @@ -5436,7 +5676,7 @@ describe('generateClient', () => { updatedAt: '2024-02-21T21:30:29.826Z', }; - const likePostSpy = mockApiResponse({ + mockApiResponse({ data: { likePostReturnPost }, }); @@ -5465,6 +5705,8 @@ describe('generateClient', () => { }, }); + expect(result.data).not.toBeNull(); + const { data: comments } = await result.data!.comments(); expect(normalizePostGraphqlCalls(lazyLoadCommentsSpy)).toMatchSnapshot(); @@ -5601,7 +5843,7 @@ describe('generateClient', () => { someHeader: 'some header value', }, }); - const result = await client.queries.echo({ + await client.queries.echo({ argumentContent: 'echo argumentContent value', }); @@ -5620,7 +5862,7 @@ describe('generateClient', () => { const client = generateClient({ amplify: Amplify, }); - const result = await client.queries.echo( + await client.queries.echo( { argumentContent: 'echo argumentContent value', }, @@ -5648,7 +5890,7 @@ describe('generateClient', () => { authMode: 'lambda', authToken: 'my-auth-token', }); - const result = await client.queries.echo({ + await client.queries.echo({ argumentContent: 'echo argumentContent value', }); @@ -5667,7 +5909,7 @@ describe('generateClient', () => { const client = generateClient({ amplify: Amplify, }); - const result = await client.queries.echo( + await client.queries.echo( { argumentContent: 'echo argumentContent value', }, @@ -5710,7 +5952,7 @@ describe('generateClient', () => { }); test('graphql error handling', async () => { - const spy = mockApiResponse({ + mockApiResponse({ data: null, errors: [{ message: 'some graphql error' }], }); @@ -5745,7 +5987,7 @@ describe('generateClient', () => { // TODO: data should actually be null/undefined, pending discussion and fix. // This is not strictly related to custom ops. - expect(data).toEqual({}); + expect(data).toBeNull(); expect(errors).toEqual([{ message: 'Network error' }]); }); @@ -5755,7 +5997,7 @@ describe('generateClient', () => { // package up a lot of different errors types into `{ errors }` as possible. // But, a clear example where this doesn't occur is request cancellations. - const spy = mockApiResponse( + mockApiResponse( new Promise(resolve => { // slight delay to give us time to cancel the request. setTimeout( diff --git a/packages/api-graphql/package.json b/packages/api-graphql/package.json index d12e7316ee7..b6915138055 100644 --- a/packages/api-graphql/package.json +++ b/packages/api-graphql/package.json @@ -86,7 +86,7 @@ "dependencies": { "@aws-amplify/api-rest": "4.0.28", "@aws-amplify/core": "6.0.28", - "@aws-amplify/data-schema": "^0.16.1", + "@aws-amplify/data-schema": "^0.17.0", "@aws-sdk/types": "3.387.0", "graphql": "15.8.0", "rxjs": "^7.8.1", diff --git a/packages/api-graphql/src/types/index.ts b/packages/api-graphql/src/types/index.ts index f376367a3ba..0ecac34369a 100644 --- a/packages/api-graphql/src/types/index.ts +++ b/packages/api-graphql/src/types/index.ts @@ -40,7 +40,7 @@ export interface GraphQLOptions { userAgentSuffix?: string; } -export interface GraphQLResult { +export interface GraphQLResult { data: T; errors?: GraphQLError[]; extensions?: Record; diff --git a/packages/aws-amplify/package.json b/packages/aws-amplify/package.json index 2a444fa3524..854a1c51b35 100644 --- a/packages/aws-amplify/package.json +++ b/packages/aws-amplify/package.json @@ -335,7 +335,7 @@ "name": "[API] generateClient (AppSync)", "path": "./dist/esm/api/index.mjs", "import": "{ generateClient }", - "limit": "39.0 kB" + "limit": "39.5 kB" }, { "name": "[API] REST API handlers", diff --git a/yarn.lock b/yarn.lock index 399d307b9c0..182e8479aee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,18 +16,18 @@ "@jridgewell/trace-mapping" "^0.3.9" "@aws-amplify/data-schema-types@*": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@aws-amplify/data-schema-types/-/data-schema-types-0.9.0.tgz#aff434696b71bda325f8aa7131d728c8746ab65b" - integrity sha512-hA5M6D+4ET25h9y9+VZ4VRbh/KGdtpm8/skyMvhvAwPmE//kYpuP1x3JDl5A8RiAhqZiYLVK/ckNRnWMYSeyfg== + version "0.10.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/data-schema-types/-/data-schema-types-0.10.0.tgz#ac5bcf51992210534fd7ece9de69932baf0c4bb8" + integrity sha512-HIfSGPcqDu7ZyHzncGiTPDElOKl7N/ZGBF1T0RWlm7jqNVOl6AYzr9dMyPSw7/e8p5SxlKNxhUoyvo7iwkWXww== dependencies: "@aws-amplify/plugin-types" "^0.9.0-beta.1" graphql "15.8.0" rxjs "^7.8.1" -"@aws-amplify/data-schema@^0.16.1": - version "0.16.1" - resolved "https://registry.yarnpkg.com/@aws-amplify/data-schema/-/data-schema-0.16.1.tgz#bed4ed97905c33ffc1cdb6e407507afb39ac7dfd" - integrity sha512-JQfsxLJ0p6hoAjmKuDss/xZCLh7OeT1ZdKLl0r1o8Mky51bTad1wUKHTSFhKpr1nu14OJl7SIeD+yE4FM3nuVQ== +"@aws-amplify/data-schema@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@aws-amplify/data-schema/-/data-schema-0.17.0.tgz#f6950c3e66fcc17acfdceb67f562e87c15e47698" + integrity sha512-UTKz2Jpd7aLPlLql/eY1hINXRsIIW7bUxNU0uVzxUaj8Jk31delAT2qvOeSkCE8He66VSPrpYKXLp3w8tlMUAA== dependencies: "@aws-amplify/data-schema-types" "*" "@types/aws-lambda" "^8.10.134" From 3f07b1179a687795090657d12962267113fe8e17 Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Wed, 24 Apr 2024 13:31:05 -0500 Subject: [PATCH 2/5] chore: Update dependency check workflow (#13287) * chore: Update dependency check workflow. * PR feedback --- .github/workflows/aws-amplify-dependency-check.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/aws-amplify-dependency-check.yml b/.github/workflows/aws-amplify-dependency-check.yml index eba57623525..e3056a4c5c5 100644 --- a/.github/workflows/aws-amplify-dependency-check.yml +++ b/.github/workflows/aws-amplify-dependency-check.yml @@ -27,13 +27,11 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: `⚠️ This PR includes manual changes to the "aws-amplify" package.json file, which can have library-wide implications. + body: `⚠️ This PR includes changes to the "aws-amplify" package.json file, which can have library-wide implications. Please ensure that this PR: - - Does not modify "@aws-amplify/*" dependency versions, which may misalign core dependencies across the library. - - A repository administrator **is required** to review & merge this change.` + - Does not manually change "@aws-amplify/*" dependency versions, which may misalign core dependencies across the library + - Remove any export paths without a major version bump + + A repository administrator **is required** to review this change.` }) - - name: Fail status check - if: steps.aws-amplify-package-check.outputs.any_changed == 'true' - run: exit 1 From 1ba64c4f5d09365cd3df76b46073ac825519cf00 Mon Sep 17 00:00:00 2001 From: Israel Arcos Date: Wed, 24 Apr 2024 13:52:23 -0500 Subject: [PATCH 3/5] fix(auth): Impossibility to sign-in when RandomKeyPassword missing --- .../providers/cognito/signInWithSRP.test.ts | 84 ++++++++++++++++++- .../cognito/tokenProvider/TokenStore.ts | 6 +- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts b/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts index e0f420d98da..628ea1cb3eb 100644 --- a/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts +++ b/packages/auth/__tests__/providers/cognito/signInWithSRP.test.ts @@ -14,8 +14,18 @@ import { import { AuthError } from '../../../src'; import { createKeysForAuthStorage } from '../../../src/providers/cognito/tokenProvider/TokenStore'; import * as clients from '../../../src/providers/cognito/utils/clients/CognitoIdentityProvider'; - jest.mock('../../../src/providers/cognito/utils/dispatchSignedInHubEvent'); + +jest.mock('../../../src/providers/cognito/utils/srp', () => { + return { + ...jest.requireActual('../../../src/providers/cognito/utils/srp'), + getAuthenticationHelper: jest.fn(() => ({ + A: { toString: jest.fn() }, + getPasswordAuthenticationKey: jest.fn(), + })), + getSignatureString: jest.fn(), + } +}) jest.mock('@aws-amplify/core/internals/utils', () => ({ ...jest.requireActual('@aws-amplify/core/internals/utils'), isBrowser: jest.fn(() => false), @@ -56,13 +66,27 @@ function setDeviceKeys() { mockedDeviceMetadata.randomPasswordKey, ); } +type DeviceKey = 'deviceKey' | 'deviceGroupKey' | 'randomPasswordKey'; +function deleteDeviceKey(key: DeviceKey) { + switch (key) { + case 'deviceKey': + localStorage.removeItem(authKeys.deviceKey); + break + case 'deviceGroupKey': + localStorage.removeItem(authKeys.deviceGroupKey); + break + case 'randomPasswordKey': + localStorage.removeItem(authKeys.randomPasswordKey); + break + } +} describe('signIn API happy path cases', () => { - let handleUserSRPAuthflowSpy; + let handleUserSRPAuthflowSpy = jest + .spyOn(initiateAuthHelpers, 'handleUserSRPAuthFlow') beforeEach(() => { - handleUserSRPAuthflowSpy = jest - .spyOn(initiateAuthHelpers, 'handleUserSRPAuthFlow') + handleUserSRPAuthflowSpy .mockImplementation( async (): Promise => authAPITestParams.RespondToAuthChallengeCommandOutput, @@ -163,6 +187,58 @@ describe('signIn API happy path cases', () => { tokenOrchestrator, ); }); + + describe('sign in with device keys', () => { + const initiateAuthSpy = jest + .spyOn(clients, 'initiateAuth') + const respondToAuthChallengeAuthSpy = jest + .spyOn(clients, 'respondToAuthChallenge') + beforeEach(() => { + setDeviceKeys(); + handleUserSRPAuthflowSpy.mockRestore(); + initiateAuthSpy.mockResolvedValueOnce({ + ChallengeName: 'SRP_AUTH', + Session: '1234234232', + $metadata: {}, + ChallengeParameters: { + USER_ID_FOR_SRP: lastAuthUser, + } + }); + respondToAuthChallengeAuthSpy.mockResolvedValueOnce(authAPITestParams.RespondToAuthChallengeCommandOutput); + }) + + afterEach(() => { + initiateAuthSpy.mockClear(); + respondToAuthChallengeAuthSpy.mockClear(); + }) + + test('respondToAuthChallenge should include device key in the request', async () => { + + await signIn({ + username: lastAuthUser, + password: 'XXXXXXXX', + }); + + expect(respondToAuthChallengeAuthSpy).toHaveBeenCalledTimes(1); + const deviceKeyFromRequest = respondToAuthChallengeAuthSpy.mock.calls[0][1].ChallengeResponses?.DEVICE_KEY + expect(deviceKeyFromRequest).toBe('mockedKey') + + }) + const deviceKeys: DeviceKey[] = ['deviceKey', 'deviceGroupKey', 'randomPasswordKey'] + test.each(deviceKeys)('respondToAuthChallenge should not include device key in the request if any device key in storage is deleted', async deviceKey => { + deleteDeviceKey(deviceKey); + await signIn({ + username: lastAuthUser, + password: 'XXXXXXXX', + }); + + expect(respondToAuthChallengeAuthSpy).toHaveBeenCalledTimes(1); + const deviceKeyFromRequest = respondToAuthChallengeAuthSpy.mock.calls[0][1].ChallengeResponses?.DEVICE_KEY + expect(deviceKeyFromRequest).toBe(undefined) + + }) + }) + }); describe('Cognito ASF', () => { diff --git a/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts b/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts index 0f3d502f8e6..53ac3228d85 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts @@ -178,10 +178,10 @@ export class DefaultTokenStore implements AuthTokenStore { authKeys.randomPasswordKey, ); - return randomPassword + return randomPassword && deviceGroupKey && deviceKey ? { - deviceKey: deviceKey ?? undefined, - deviceGroupKey: deviceGroupKey ?? undefined, + deviceKey, + deviceGroupKey, randomPassword, } : null; From aa490b9bcbd2a442d9726937cfa59ef8b7859306 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Date: Thu, 25 Apr 2024 11:11:58 -0700 Subject: [PATCH 4/5] fix: bump package versions (#13254) Co-authored-by: Ashwin Kumar Co-authored-by: AllanZhengYP --- packages/adapter-nextjs/package.json | 4 ++-- packages/auth/package.json | 2 +- packages/aws-amplify/package.json | 6 +++--- packages/predictions/package.json | 4 ++-- packages/pubsub/package.json | 4 ++-- packages/storage/package.json | 2 +- scripts/tsc-compliance-test/package.json | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/adapter-nextjs/package.json b/packages/adapter-nextjs/package.json index 2abd86ebc0c..ffb77a6a853 100644 --- a/packages/adapter-nextjs/package.json +++ b/packages/adapter-nextjs/package.json @@ -1,7 +1,7 @@ { "author": "Amazon Web Services", "name": "@aws-amplify/adapter-nextjs", - "version": "1.0.30", + "version": "1.1.3", "description": "The adapter for the supporting of using Amplify APIs in Next.js.", "peerDependencies": { "aws-amplify": "^6.0.7", @@ -15,7 +15,7 @@ "@types/node": "^20.3.1", "@types/react": "^18.2.13", "@types/react-dom": "^18.2.6", - "aws-amplify": "6.0.30", + "aws-amplify": "6.1.3", "jest-fetch-mock": "3.0.3", "next": ">= 13.5.0 < 15.0.0", "typescript": "5.0.2" diff --git a/packages/auth/package.json b/packages/auth/package.json index be04a4db4d6..fdc84038e5e 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/auth", - "version": "6.0.29", + "version": "6.2.0", "description": "Auth category of aws-amplify", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", diff --git a/packages/aws-amplify/package.json b/packages/aws-amplify/package.json index 854a1c51b35..0c2cea076e8 100644 --- a/packages/aws-amplify/package.json +++ b/packages/aws-amplify/package.json @@ -1,6 +1,6 @@ { "name": "aws-amplify", - "version": "6.0.30", + "version": "6.1.3", "description": "AWS Amplify is a JavaScript library for Frontend and mobile developers building cloud-enabled applications.", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", @@ -278,11 +278,11 @@ "dependencies": { "@aws-amplify/analytics": "7.0.28", "@aws-amplify/api": "6.0.29", - "@aws-amplify/auth": "6.0.29", + "@aws-amplify/auth": "6.2.0", "@aws-amplify/core": "6.0.28", "@aws-amplify/datastore": "5.0.29", "@aws-amplify/notifications": "2.0.28", - "@aws-amplify/storage": "6.0.28", + "@aws-amplify/storage": "6.3.0", "tslib": "^2.5.0" }, "devDependencies": { diff --git a/packages/predictions/package.json b/packages/predictions/package.json index 068f25266ec..a53b4014072 100644 --- a/packages/predictions/package.json +++ b/packages/predictions/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/predictions", - "version": "6.0.28", + "version": "6.1.2", "description": "Machine learning category of aws-amplify", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", @@ -43,7 +43,7 @@ "src" ], "dependencies": { - "@aws-amplify/storage": "6.0.28", + "@aws-amplify/storage": "6.3.0", "@aws-sdk/client-comprehend": "3.398.0", "@aws-sdk/client-polly": "3.398.0", "@aws-sdk/client-rekognition": "3.398.0", diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index 80e050d0837..9818fc8b1ff 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/pubsub", - "version": "6.0.29", + "version": "6.1.1", "description": "Pubsub category of aws-amplify", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", @@ -73,7 +73,7 @@ "mqtt" ], "dependencies": { - "@aws-amplify/auth": "6.0.29", + "@aws-amplify/auth": "6.2.0", "buffer": "4.9.2", "graphql": "15.8.0", "rxjs": "^7.8.1", diff --git a/packages/storage/package.json b/packages/storage/package.json index 2b74515c42d..7086c520b72 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@aws-amplify/storage", - "version": "6.0.28", + "version": "6.3.0", "description": "Storage category of aws-amplify", "main": "./dist/cjs/index.js", "module": "./dist/esm/index.mjs", diff --git a/scripts/tsc-compliance-test/package.json b/scripts/tsc-compliance-test/package.json index d41dd99f56a..dddf4ce167f 100644 --- a/scripts/tsc-compliance-test/package.json +++ b/scripts/tsc-compliance-test/package.json @@ -5,7 +5,7 @@ "private": true, "devDependencies": { "@types/node": "16.18.82", - "aws-amplify": "6.0.30", + "aws-amplify": "6.1.3", "typescript": "4.2.x" }, "scripts": { From d0fdfa6f3ff566d0986ff1301e941f0034cd06b1 Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Thu, 25 Apr 2024 16:17:20 -0500 Subject: [PATCH 5/5] chore: Clean up awkward langauge in README (#13292) --- README.md | 2 +- packages/aws-amplify/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ebb73edc80..ada48b62a75 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ AWS Amplify provides a declarative and easy-to-use interface across different ca Our default implementation works with Amazon Web Services (AWS), but AWS Amplify is designed to be open and pluggable for any custom backend or service. -#### Visit our [Documentation site](https://docs.amplify.aws/) to learn more about AWS Amplify. Please see our [Amplify JavaScript](https://docs.amplify.aws/lib/q/platform/js/) page within our Documentation site for information around the full list of features we support. +#### Visit our [documentation site](https://docs.amplify.aws/) to learn more about AWS Amplify. Please see the [Amplify JavaScript](https://docs.amplify.aws/lib/q/platform/js/) page for information around the full list of features we support. - [Demo Applications](https://github.com/aws-amplify/amplify-js-samples) - [Contributing](https://github.com/aws-amplify/amplify-js/blob/main/CONTRIBUTING.md) diff --git a/packages/aws-amplify/README.md b/packages/aws-amplify/README.md index 0b331af2f4f..a024a31c2d5 100644 --- a/packages/aws-amplify/README.md +++ b/packages/aws-amplify/README.md @@ -2,4 +2,4 @@ AWS Amplify is a JavaScript library for frontend and mobile developers building cloud-enabled applications. The library is a declarative interface across different categories of operations in order to make common tasks easier to add into your application. The default implementation works with Amazon Web Services (AWS) resources but is designed to be open and pluggable for usage with other cloud services that wish to provide an implementation or custom backends. -`aws-amplify` is the AWS Amplify library. Documentation is available [here](https://docs.amplify.aws/lib/q/platform/js/). +Documentation is available [here](https://docs.amplify.aws/lib/q/platform/js/).