Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

10007 DOD: Move cognito specific functions to user gateway #10278

Merged
merged 38 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2fda86b
10007: Create user gateway method for refreshing id token
rachelschneiderman Mar 1, 2024
7ab3880
10007: Remove import
rachelschneiderman Mar 1, 2024
bf24e30
10007: Add basic initiateAuth function for user gateway, still a WIP
rachelschneiderman Mar 1, 2024
8f01ddb
10007: Fixing tests, update initiateAuth command that was missed in c…
rachelschneiderman Mar 1, 2024
8e3664f
Merge branch '10007-DOD' of https://github.com/flexion/ef-cms into 10…
rachelschneiderman Mar 8, 2024
556be48
10007: Fix unit test
rachelschneiderman Mar 8, 2024
0635e45
10007: Have initiate auth handle AWS specific errors
rachelschneiderman Mar 8, 2024
611e84f
10007: Change password from temp password can't use initiate auth fro…
rachelschneiderman Mar 8, 2024
c7cd506
10007: Extract confirmForgotPassword to changePassword user gateway f…
rachelschneiderman Mar 8, 2024
b37cc0c
10007: Throw new password needed error when trying to initiate auth w…
rachelschneiderman Mar 8, 2024
5e8d68e
10007: Move createUserInteractor to web api, extract disable user int…
rachelschneiderman Mar 8, 2024
7300afd
10007: Add admin user to mockUsers
rachelschneiderman Mar 8, 2024
c2208ee
10007: Update types, fix tests
rachelschneiderman Mar 8, 2024
6fcf347
10007: Add test coverage
rachelschneiderman Mar 8, 2024
764f8ee
10007: Disable prefer destructuring rule for line that doesn't need d…
rachelschneiderman Mar 8, 2024
3761501
10007: Disable user now accepts email like all other user gateway fun…
rachelschneiderman Mar 8, 2024
10eabb5
10007: Extract forgot password cognito call to user gateway
rachelschneiderman Mar 8, 2024
f40ee46
10007: Fix test after updating disableUser args
rachelschneiderman Mar 8, 2024
eb7aad0
10007: Extract adminUpdateUserAttributes to an updateUser function in…
rachelschneiderman Mar 8, 2024
98d6b2d
10007: Refactor to use updateUser function
rachelschneiderman Mar 8, 2024
4e1c61a
10007: Refactor to use updateUser function
rachelschneiderman Mar 8, 2024
e469f63
10007: Refactor to use updateUser function
rachelschneiderman Mar 8, 2024
391b9c6
10007: Fixing tests
rachelschneiderman Mar 11, 2024
f19a2e1
10007: Extract adminCreateUser to createUser function in user gateway
rachelschneiderman Mar 11, 2024
751dc21
10007: Move shared function to resend temporary password into use cas…
rachelschneiderman Mar 11, 2024
b8e2a1f
Merge branch '10007-DOD' of https://github.com/flexion/ef-cms into 10…
rachelschneiderman Mar 11, 2024
348decf
10007: Call user gateway function instead of cognito directly
rachelschneiderman Mar 11, 2024
d704f46
10007: Call user gateway function instead of cognito directly
rachelschneiderman Mar 11, 2024
ef48b3c
10007: Don't call an interactor from an interactor. Inline create pet…
rachelschneiderman Mar 11, 2024
b0ddaf9
Merge branch '10007-DOD' of https://github.com/flexion/ef-cms into 10…
rachelschneiderman Mar 11, 2024
b649fc7
Merge branch '10007-DOD' of https://github.com/flexion/ef-cms into 10…
rachelschneiderman Mar 11, 2024
513e7b4
10007: Fixing unit test
rachelschneiderman Mar 11, 2024
c895bc4
10007: Extract confirm sign up to user gateway function
rachelschneiderman Mar 12, 2024
19ef516
10007: Extract sign up to user gateway function
rachelschneiderman Mar 12, 2024
3d9a147
10007: Refactor and simplify creating practitioner user
rachelschneiderman Mar 12, 2024
f648af9
10007: Fixing unit test
rachelschneiderman Mar 12, 2024
210eb7c
10007: Fixing unit test
rachelschneiderman Mar 13, 2024
8704afd
Merge branch '10007-DOD' of https://github.com/flexion/ef-cms into 10…
rachelschneiderman Mar 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 36 additions & 57 deletions web-api/src/business/useCases/auth/changePasswordInteractor.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
AuthFlowType,
ChallengeNameType,
CodeMismatchException,
ExpiredCodeException,
Expand Down Expand Up @@ -66,7 +65,7 @@ describe('changePasswordInteractor', () => {
};

applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue(mockInitiateAuthResponse);

applicationContext
Expand All @@ -85,7 +84,7 @@ describe('changePasswordInteractor', () => {
AuthenticationResult: {},
};
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue(mockInitiateAuthResponse);

await expect(
Expand All @@ -97,16 +96,12 @@ describe('changePasswordInteractor', () => {
}),
).rejects.toThrow('User is not in `FORCE_CHANGE_PASSWORD` state');

expect(applicationContext.getCognito().initiateAuth).toHaveBeenCalledWith(
{
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: mockPassword,
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
},
);
expect(
applicationContext.getUserGateway().initiateAuth,
).toHaveBeenCalledWith(applicationContext, {
email: mockEmail,
password: mockPassword,
});
});

it('should update the user`s password in persistence when they are in NEW_PASSWORD_REQUIRED state and their change password request is valid', async () => {
Expand Down Expand Up @@ -279,7 +274,7 @@ describe('changePasswordInteractor', () => {
});

applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue(mockInitiateAuthResponse);
});

Expand Down Expand Up @@ -314,16 +309,12 @@ describe('changePasswordInteractor', () => {
Password: mockPassword,
Username: mockEmail,
});
expect(applicationContext.getCognito().initiateAuth).toHaveBeenCalledWith(
{
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: mockPassword,
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
},
);
expect(
applicationContext.getUserGateway().initiateAuth,
).toHaveBeenCalledWith(applicationContext, {
email: mockEmail,
password: mockPassword,
});
expect(result).toEqual({
accessToken: mockToken,
idToken: mockToken,
Expand All @@ -332,7 +323,7 @@ describe('changePasswordInteractor', () => {
});

it('should throw an error if initiate auth does not return the correct tokens', async () => {
applicationContext.getCognito().initiateAuth.mockResolvedValue({});
applicationContext.getUserGateway().initiateAuth.mockResolvedValue({});

await expect(
changePasswordInteractor(applicationContext, {
Expand All @@ -343,21 +334,17 @@ describe('changePasswordInteractor', () => {
}),
).rejects.toThrow(`Unable to change password for email: ${mockEmail}`);

expect(applicationContext.getCognito().initiateAuth).toHaveBeenCalledWith(
{
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: mockPassword,
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
},
);
expect(
applicationContext.getUserGateway().initiateAuth,
).toHaveBeenCalledWith(applicationContext, {
email: mockEmail,
password: mockPassword,
});
});

it('should throw an InvalidRequest error if initiateAuth returns a CodeMismatchException', async () => {
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValueOnce(
new CodeMismatchException({ $metadata: {}, message: '' }),
);
Expand All @@ -371,21 +358,17 @@ describe('changePasswordInteractor', () => {
}),
).rejects.toThrow('Forgot password code is expired or incorrect');

expect(applicationContext.getCognito().initiateAuth).toHaveBeenCalledWith(
{
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: mockPassword,
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
},
);
expect(
applicationContext.getUserGateway().initiateAuth,
).toHaveBeenCalledWith(applicationContext, {
email: mockEmail,
password: mockPassword,
});
});

it('should throw an InvalidRequest error if initiateAuth returns a ExpiredCodeException', async () => {
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValueOnce(
new ExpiredCodeException({ $metadata: {}, message: '' }),
);
Expand All @@ -399,16 +382,12 @@ describe('changePasswordInteractor', () => {
}),
).rejects.toThrow('Forgot password code is expired or incorrect');

expect(applicationContext.getCognito().initiateAuth).toHaveBeenCalledWith(
{
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: mockPassword,
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
},
);
expect(
applicationContext.getUserGateway().initiateAuth,
).toHaveBeenCalledWith(applicationContext, {
email: mockEmail,
password: mockPassword,
});
});
});
});
31 changes: 11 additions & 20 deletions web-api/src/business/useCases/auth/changePasswordInteractor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
AuthFlowType,
ChallengeNameType,
} from '@aws-sdk/client-cognito-identity-provider';
import { ChallengeNameType } from '@aws-sdk/client-cognito-identity-provider';
import { ChangePasswordForm } from '@shared/business/entities/ChangePasswordForm';
import { InvalidEntityError, NotFoundError } from '@web-api/errors/errors';
import { MESSAGE_TYPES } from '@web-api/gateways/worker/workerRouter';
Expand Down Expand Up @@ -47,14 +44,10 @@ export const changePasswordInteractor = async (

if (tempPassword) {
const initiateAuthResult = await applicationContext
.getCognito()
.initiateAuth({
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: tempPassword,
USERNAME: email,
},
ClientId: applicationContext.environment.cognitoClientId,
.getUserGateway()
.initiateAuth(applicationContext, {
email,
password: tempPassword,
});

if (
Expand Down Expand Up @@ -135,14 +128,12 @@ export const changePasswordInteractor = async (
Username: email,
});

const result = await applicationContext.getCognito().initiateAuth({
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: password,
USERNAME: email,
},
ClientId: applicationContext.environment.cognitoClientId,
});
const result = await applicationContext
.getUserGateway()
.initiateAuth(applicationContext, {
email,
password,
});

if (
!result.AuthenticationResult?.AccessToken ||
Expand Down
20 changes: 10 additions & 10 deletions web-api/src/business/useCases/auth/loginInteractor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('loginInteractor', () => {
ChallengeName: ChallengeNameType.NEW_PASSWORD_REQUIRED,
};
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue(mockNewPasswordRequiredResponse);

await expect(
Expand All @@ -41,7 +41,7 @@ describe('loginInteractor', () => {
message: '',
});
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockWrongEmailOrPasswordError);

await expect(
Expand All @@ -60,7 +60,7 @@ describe('loginInteractor', () => {
message: 'Password attempts exceeded',
});
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockTooManyAttemptsError);

await expect(
Expand All @@ -79,7 +79,7 @@ describe('loginInteractor', () => {
message: '',
});
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockWrongEmailOrPasswordError);

await expect(
Expand All @@ -97,7 +97,7 @@ describe('loginInteractor', () => {
'Totally unexpected, unhandled error.',
);
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockWrongEmailOrPasswordError);

await expect(
Expand All @@ -108,11 +108,11 @@ describe('loginInteractor', () => {
).rejects.toThrow(mockWrongEmailOrPasswordError);
});

it('should throw an error if initiateAuth does not return access, id, and refresh tokens', async () => {
it('should throw an error when initiateAuth does not return access, id, and refresh tokens', async () => {
const mockEmail = '[email protected]';
const mockPassword = 'MyPa$Sword!';
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue({ AuthenticationResult: {} });

await expect(
Expand All @@ -131,7 +131,7 @@ describe('loginInteractor', () => {
message: '',
});
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockWrongEmailOrPasswordError);
applicationContext.getUserGateway().getUserByEmail.mockResolvedValue({
email: mockEmail,
Expand All @@ -154,7 +154,7 @@ describe('loginInteractor', () => {
message: '',
});
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockRejectedValue(mockWrongEmailOrPasswordError);
applicationContext
.getUserGateway()
Expand Down Expand Up @@ -182,7 +182,7 @@ describe('loginInteractor', () => {
},
};
applicationContext
.getCognito()
.getUserGateway()
.initiateAuth.mockResolvedValue(mockSuccessFullLoginResponse);

const result = await loginInteractor(applicationContext, {
Expand Down
16 changes: 4 additions & 12 deletions web-api/src/business/useCases/auth/loginInteractor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
AuthFlowType,
ChallengeNameType,
} from '@aws-sdk/client-cognito-identity-provider';
import { ChallengeNameType } from '@aws-sdk/client-cognito-identity-provider';
import {
InvalidRequest,
NotFoundError,
Expand All @@ -15,14 +12,9 @@ export const loginInteractor = async (
{ email, password }: { email: string; password: string },
): Promise<{ idToken: string; accessToken: string; refreshToken: string }> => {
try {
const result = await applicationContext.getCognito().initiateAuth({
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: password,
USERNAME: email,
},
ClientId: applicationContext.environment.cognitoClientId,
});
const result = await applicationContext
.getUserGateway()
.initiateAuth(applicationContext, { email, password });

if (result?.ChallengeName === ChallengeNameType.NEW_PASSWORD_REQUIRED) {
const PasswordChangeError = new Error('NewPasswordRequired');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('renewIdTokenInteractor', () => {
message: 'Refresh token is expired',
});
applicationContext
.getPersistenceGateway()
.getUserGateway()
.renewIdToken.mockRejectedValue(mockError);

await expect(
Expand All @@ -23,7 +23,7 @@ describe('renewIdTokenInteractor', () => {
it('should rethrow when an error occurs that is not recognized', async () => {
const mockError = new Error('Cognito exploded!!!');
applicationContext
.getPersistenceGateway()
.getUserGateway()
.renewIdToken.mockRejectedValue(mockError);

await expect(
Expand All @@ -35,16 +35,14 @@ describe('renewIdTokenInteractor', () => {

it('should make a call to get an id token and refresh token', async () => {
const expectedRefresh = 'sometoken';
applicationContext.getPersistenceGateway().renewIdToken.mockResolvedValue({
idToken: 'abc',
});
applicationContext.getUserGateway().renewIdToken.mockResolvedValue('abc');

const result = await renewIdTokenInteractor(applicationContext, {
refreshToken: expectedRefresh,
});

expect(
applicationContext.getPersistenceGateway().renewIdToken.mock.calls[0][1],
applicationContext.getUserGateway().renewIdToken.mock.calls[0][1],
).toEqual({ refreshToken: expectedRefresh });
expect(result).toEqual({
idToken: 'abc',
Expand Down
4 changes: 2 additions & 2 deletions web-api/src/business/useCases/auth/renewIdTokenInteractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const renewIdTokenInteractor = async (
idToken: string;
}> => {
try {
const { idToken } = await applicationContext
.getPersistenceGateway()
const idToken = await applicationContext
.getUserGateway()
.renewIdToken(applicationContext, { refreshToken });

return {
Expand Down
18 changes: 18 additions & 0 deletions web-api/src/gateways/user/initiateAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AuthFlowType } from '@aws-sdk/client-cognito-identity-provider';
import { ServerApplicationContext } from '@web-api/applicationContext';

export async function initiateAuth(
applicationContext: ServerApplicationContext,
{ email, password }: { email: string; password: string },
) {
const result = await applicationContext.getCognito().initiateAuth({
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
PASSWORD: password,
USERNAME: email,
},
ClientId: applicationContext.environment.cognitoClientId,
});

return result;
}
Loading
Loading