Skip to content

Commit

Permalink
10007: Have initiate auth handle AWS specific errors
Browse files Browse the repository at this point in the history
  • Loading branch information
rachelschneiderman committed Mar 8, 2024
1 parent 556be48 commit 0635e45
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ describe('changePasswordInteractor', () => {
});

describe('when the user is attempting to log in with a temporary password', () => {
let mockInitiateAuthResponse: InitiateAuthResponse;
let mockInitiateAuthResponse;
let mockRespondToAuthChallengeResponse: RespondToAuthChallengeResponse;
let mockUserWithPendingEmail: UserRecord;

beforeEach(() => {
mockInitiateAuthResponse = {
ChallengeName: ChallengeNameType.NEW_PASSWORD_REQUIRED,
Session: '0943fbef-a573-484a-8164-a1a5a35f8f3e',
session: '0943fbef-a573-484a-8164-a1a5a35f8f3e',
};

mockRespondToAuthChallengeResponse = {
Expand Down Expand Up @@ -80,12 +79,11 @@ describe('changePasswordInteractor', () => {
});

it('should throw an error when the user is NOT in NEW_PASSWORD_REQUIRED state', async () => {
mockInitiateAuthResponse = {
AuthenticationResult: {},
};
const mockIntiateAuthError = new Error('NewPasswordRequired');
mockIntiateAuthError.name = 'NewPasswordRequired';
applicationContext
.getUserGateway()
.initiateAuth.mockResolvedValue(mockInitiateAuthResponse);
.initiateAuth.mockRejectedValue(mockInitiateAuthResponse);

await expect(
changePasswordInteractor(applicationContext, {
Expand Down Expand Up @@ -121,7 +119,7 @@ describe('changePasswordInteractor', () => {
USERNAME: mockEmail,
},
ClientId: applicationContext.environment.cognitoClientId,
Session: mockInitiateAuthResponse.Session,
Session: mockInitiateAuthResponse.session,
});
});

Expand Down
27 changes: 14 additions & 13 deletions web-api/src/business/useCases/auth/changePasswordInteractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,19 @@ export const changePasswordInteractor = async (
}

if (tempPassword) {
const initiateAuthResult = await applicationContext
.getUserGateway()
.initiateAuth(applicationContext, {
email,
password: tempPassword,
});

if (
initiateAuthResult.ChallengeName !==
ChallengeNameType.NEW_PASSWORD_REQUIRED
) {
throw new Error('User is not in `FORCE_CHANGE_PASSWORD` state');
let initiateAuthResult;

try {
initiateAuthResult = await applicationContext
.getUserGateway()
.initiateAuth(applicationContext, {
email,
password: tempPassword,
});
} catch (err: any) {
if (err.name !== 'NewPasswordRequired') {
throw new Error('User is not in `FORCE_CHANGE_PASSWORD` state');
}
}

const result = await applicationContext
Expand All @@ -66,7 +67,7 @@ export const changePasswordInteractor = async (
USERNAME: email,
},
ClientId: applicationContext.environment.cognitoClientId,
Session: initiateAuthResult.Session,
Session: initiateAuthResult.session,
});

if (
Expand Down
25 changes: 10 additions & 15 deletions web-api/src/business/useCases/auth/loginInteractor.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
ChallengeNameType,
InitiateAuthCommandOutput,
InvalidPasswordException,
NotAuthorizedException,
UserNotConfirmedException,
Expand All @@ -17,13 +15,11 @@ describe('loginInteractor', () => {
it('should throw an error when the user attempts to log in and they are in a NEW_PASSWORD_REQUIRED state', async () => {
const mockEmail = '[email protected]';
const mockPassword = 'MyPa$Sword!';
const mockNewPasswordRequiredResponse: InitiateAuthCommandOutput = {
$metadata: {},
ChallengeName: ChallengeNameType.NEW_PASSWORD_REQUIRED,
};
const mockNewPasswordRequiredError = new Error('NewPasswordRequired');
mockNewPasswordRequiredError.name = 'NewPasswordRequired';
applicationContext
.getUserGateway()
.initiateAuth.mockResolvedValue(mockNewPasswordRequiredResponse);
.initiateAuth.mockRejectedValue(mockNewPasswordRequiredError);

await expect(
loginInteractor(applicationContext, {
Expand Down Expand Up @@ -111,9 +107,11 @@ describe('loginInteractor', () => {
it('should throw an error when initiateAuth does not return access, id, and refresh tokens', async () => {
const mockEmail = '[email protected]';
const mockPassword = 'MyPa$Sword!';
const initiateAuthError = new Error('InitiateAuthError');
initiateAuthError.name = 'InitiateAuthError';
applicationContext
.getUserGateway()
.initiateAuth.mockResolvedValue({ AuthenticationResult: {} });
.initiateAuth.mockRejectedValue(initiateAuthError);

await expect(
loginInteractor(applicationContext, {
Expand Down Expand Up @@ -208,13 +206,10 @@ describe('loginInteractor', () => {
it('should return the access, id, refresh tokens to the user when the user is successfully authenticated', async () => {
const mockEmail = '[email protected]';
const mockPassword = 'MyPa$Sword!';
const mockSuccessFullLoginResponse: InitiateAuthCommandOutput = {
$metadata: {},
AuthenticationResult: {
AccessToken: 'TEST_ACCESS_TOKEN',
IdToken: 'TEST_ID_TOKEN',
RefreshToken: 'TEST_REFRESH_TOKEN',
},
const mockSuccessFullLoginResponse = {
accessToken: 'TEST_ACCESS_TOKEN',
idToken: 'TEST_ID_TOKEN',
refreshToken: 'TEST_REFRESH_TOKEN',
};
applicationContext
.getUserGateway()
Expand Down
28 changes: 5 additions & 23 deletions web-api/src/business/useCases/auth/loginInteractor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
AdminCreateUserCommandInput,
ChallengeNameType,
} from '@aws-sdk/client-cognito-identity-provider';
import { AdminCreateUserCommandInput } from '@aws-sdk/client-cognito-identity-provider';
import {
InvalidRequest,
NotFoundError,
Expand All @@ -15,35 +12,20 @@ export const loginInteractor = async (
{ email, password }: { email: string; password: string },
): Promise<{ idToken: string; accessToken: string; refreshToken: string }> => {
try {
const result = await applicationContext
return await applicationContext
.getUserGateway()
.initiateAuth(applicationContext, { email, password });

if (result?.ChallengeName === ChallengeNameType.NEW_PASSWORD_REQUIRED) {
const PasswordChangeError = new Error('NewPasswordRequired');
PasswordChangeError.name = 'NewPasswordRequired';
throw PasswordChangeError;
}

if (
!result.AuthenticationResult?.AccessToken ||
!result.AuthenticationResult?.IdToken ||
!result.AuthenticationResult?.RefreshToken
) {
} catch (err: any) {
if (err.name === 'InitiateAuthError') {
throw new Error('Unsuccessful authentication');
}

return {
accessToken: result.AuthenticationResult.AccessToken,
idToken: result.AuthenticationResult.IdToken,
refreshToken: result.AuthenticationResult.RefreshToken,
};
} catch (err: any) {
await authErrorHandling(applicationContext, {
email,
error: err,
sendAccountConfirmation: true,
});

throw err;
}
};
Expand Down
37 changes: 34 additions & 3 deletions web-api/src/gateways/user/initiateAuth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { AuthFlowType } from '@aws-sdk/client-cognito-identity-provider';
import {
AuthFlowType,
ChallengeNameType,
} 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 },
) {
): Promise<{
accessToken: string;
idToken: string;
refreshToken: string;
session: string | undefined;
}> {
const result = await applicationContext.getCognito().initiateAuth({
AuthFlow: AuthFlowType.USER_PASSWORD_AUTH,
AuthParameters: {
Expand All @@ -14,5 +22,28 @@ export async function initiateAuth(
ClientId: applicationContext.environment.cognitoClientId,
});

return result;
if (result.ChallengeName) {
if (result.ChallengeName === ChallengeNameType.NEW_PASSWORD_REQUIRED) {
const PasswordChangeError = new Error('NewPasswordRequired');
PasswordChangeError.name = 'NewPasswordRequired';
throw PasswordChangeError;
}
}

if (
!result.AuthenticationResult?.AccessToken ||
!result.AuthenticationResult?.IdToken ||
!result.AuthenticationResult?.RefreshToken
) {
const InitiateAuthError = new Error('InitiateAuthError');
InitiateAuthError.name = 'InitiateAuthError';
throw InitiateAuthError;
}

return {
accessToken: result.AuthenticationResult.AccessToken,
idToken: result.AuthenticationResult.IdToken,
refreshToken: result.AuthenticationResult.RefreshToken,
session: result.Session,
};
}

0 comments on commit 0635e45

Please sign in to comment.