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

AUT-2164: Create new service to check for fraudulent emails #1615

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export const API_ENDPOINTS = {
VERIFY_MFA_CODE: "/verify-mfa-code",
ACCOUNT_RECOVERY: "/account-recovery",
CHECK_REAUTH_USER: "/check-reauth-user",
CHECK_EMAIL_FRAUD_BLOCK: "/check-email-fraud-block",
};

export const ERROR_MESSAGES = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
createApiResponse,
getRequestConfig,
Http,
http,
} from "../../utils/http";
import { API_ENDPOINTS } from "../../app.constants";
import { ApiResponseResult } from "../../types";
import {
CheckEmailFraudBlockInterface,
CheckEmailFraudBlockResponse,
} from "./types";

export function checkEmailFraudBlockService(
axios: Http = http
): CheckEmailFraudBlockInterface {
const checkEmailFraudBlock = async function (
email: string,
sessionId: string,
sourceIp: string,
clientSessionId: string,
persistentSessionId: string
): Promise<ApiResponseResult<CheckEmailFraudBlockResponse>> {
const response = await axios.client.post<CheckEmailFraudBlockResponse>(
API_ENDPOINTS.CHECK_EMAIL_FRAUD_BLOCK,
{
email: email.toLowerCase(),
},
getRequestConfig({
sessionId: sessionId,
sourceIp: sourceIp,
clientSessionId: clientSessionId,
persistentSessionId: persistentSessionId,
})
);
return createApiResponse<CheckEmailFraudBlockResponse>(response);
};
return {
checkEmailFraudBlock,
};
}
16 changes: 16 additions & 0 deletions src/components/check-email-fraud-block/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiResponseResult, DefaultApiResponse } from "../../types";

export interface CheckEmailFraudBlockInterface {
checkEmailFraudBlock: (
email: string,
sessionId: string,
sourceIp: string,
clientSessionId: string,
persistentSessionId: string
) => Promise<ApiResponseResult<CheckEmailFraudBlockResponse>>;
}

export interface CheckEmailFraudBlockResponse extends DefaultApiResponse {
email: string;
isBlockedStatus: string;
}
17 changes: 16 additions & 1 deletion src/components/enter-email/enter-email-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import {
timestampNMinutesFromNow,
timestampNSecondsFromNow,
} from "../../utils/lock-helper";
import { checkEmailFraudBlockService } from "../check-email-fraud-block/checkEmailFraudBlockService";
import { CheckEmailFraudBlockInterface } from "../check-email-fraud-block/types";
import { logger } from "../../utils/logger";

export const RE_ENTER_EMAIL_TEMPLATE =
"enter-email/index-re-enter-email-account.njk";
Expand Down Expand Up @@ -59,7 +62,8 @@ export function enterEmailCreateGet(req: Request, res: Response): void {

export function enterEmailPost(
service: EnterEmailServiceInterface = enterEmailService(),
checkReauthService: CheckReauthServiceInterface = checkReauthUsersService()
checkReauthService: CheckReauthServiceInterface = checkReauthUsersService(),
checkEmailFraudService: CheckEmailFraudBlockInterface = checkEmailFraudBlockService()
): ExpressRouteFunc {
return async function (req: Request, res: Response) {
const email = req.body.email;
Expand Down Expand Up @@ -126,6 +130,17 @@ export function enterEmailPost(
result.data.lockoutInformation.length > 0
)
setUpAuthAppLocks(req, result.data.lockoutInformation);

const checkEmailFraudResponse =
await checkEmailFraudService.checkEmailFraudBlock(
email,
sessionId,
req.ip,
clientSessionId,
persistentSessionId
);
logger.info(`checkEmailFraudResponse: ${checkEmailFraudResponse.data}`);

req.session.user.enterEmailMfaType = result.data.mfaMethodType;
req.session.user.redactedPhoneNumber = result.data.phoneNumberLastThree;
const nextState = result.data.doesUserExist
Expand Down
47 changes: 34 additions & 13 deletions src/components/enter-email/tests/enter-email-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@ import { SendNotificationServiceInterface } from "../../common/send-notification
import { mockResponse, RequestOutput, ResponseOutput } from "mock-req-res";
import { CheckReauthServiceInterface } from "../../check-reauth-users/types";
import { createMockRequest } from "../../../../test/helpers/mock-request-helper";
import { CheckEmailFraudBlockInterface } from "../../check-email-fraud-block/types";

describe("enter email controller", () => {
let req: RequestOutput;
let res: ResponseOutput;
let clock: sinon.SinonFakeTimers;
const date = new Date(Date.UTC(2024, 1, 1));

const checkReauthSuccessfulFakeService: CheckReauthServiceInterface = {
checkReauthUsers: sinon.fake.returns({
success: true,
}),
} as unknown as CheckReauthServiceInterface;

const checkEmailFraudFakeSuccessfulService: CheckEmailFraudBlockInterface = {
checkEmailFraudBlock: sinon.fake.returns({
success: true,
data: { email: "[email protected]", isBlockedStatus: "Pending" },
}),
} as unknown as CheckEmailFraudBlockInterface;

beforeEach(() => {
res = mockResponse();
clock = sinon.useFakeTimers({
Expand Down Expand Up @@ -145,7 +159,11 @@ describe("enter email controller", () => {
req.body.email = "test.test.com";
res.locals.sessionId = "dsad.dds";

await enterEmailPost(fakeService)(req as Request, res as Response);
await enterEmailPost(
fakeService,
checkReauthSuccessfulFakeService,
checkEmailFraudFakeSuccessfulService
)(req as Request, res as Response);

expect(fakeService.userExists).to.have.been.calledOnce;
expect(res.redirect).to.have.calledWith(PATH_NAMES.ENTER_PASSWORD);
Expand All @@ -162,7 +180,11 @@ describe("enter email controller", () => {
req.body.email = "test.test.com";
res.locals.sessionId = "sadl990asdald";

await enterEmailPost(fakeService)(req as Request, res as Response);
await enterEmailPost(
fakeService,
checkReauthSuccessfulFakeService,
checkEmailFraudFakeSuccessfulService
)(req as Request, res as Response);

expect(res.redirect).to.have.calledWith(PATH_NAMES.ACCOUNT_NOT_FOUND);
expect(fakeService.userExists).to.have.been.calledOnce;
Expand Down Expand Up @@ -190,7 +212,11 @@ describe("enter email controller", () => {
req.body.email = "[email protected]";
res.locals.sessionId = "sadl990asdald";

await enterEmailPost(fakeService)(req as Request, res as Response);
await enterEmailPost(
fakeService,
checkReauthSuccessfulFakeService,
checkEmailFraudFakeSuccessfulService
)(req as Request, res as Response);

const expectedLockTime = new Date(
date.getTime() + lockTTlInSeconds * 1000
Expand Down Expand Up @@ -450,16 +476,11 @@ describe("enter email controller", () => {
}),
} as unknown as EnterEmailServiceInterface;

const successfulFakeService: CheckReauthServiceInterface = {
checkReauthUsers: sinon.fake.returns({
success: true,
}),
} as unknown as CheckReauthServiceInterface;

await enterEmailPost(fakeService, successfulFakeService)(
req as Request,
res as Response
);
await enterEmailPost(
fakeService,
checkReauthSuccessfulFakeService,
checkEmailFraudFakeSuccessfulService
)(req as Request, res as Response);

expect(res.redirect).to.have.calledWith(PATH_NAMES.ENTER_PASSWORD);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import request from "supertest";
import { describe } from "mocha";
import { expect, sinon } from "../../../../test/utils/test-utils";
import nock = require("nock");
import * as cheerio from "cheerio";
import decache from "decache";
import {
Expand All @@ -13,6 +12,7 @@ import { CheckReauthServiceInterface } from "../../check-reauth-users/types";
import { AxiosResponse } from "axios";
import { createApiResponse } from "../../../utils/http";
import { DefaultApiResponse } from "../../../types";
import nock = require("nock");

describe("Integration::enter email", () => {
let token: string | string[];
Expand Down Expand Up @@ -167,6 +167,13 @@ describe("Integration::enter email", () => {
email: "[email protected]",
doesUserExist: true,
});
nock(baseApi)
.post(API_ENDPOINTS.CHECK_EMAIL_FRAUD_BLOCK)
.once()
.reply(HTTP_STATUS_CODES.OK, {
email: "[email protected]",
isBlockedStatus: "Pending",
});

request(app)
.post(PATH_NAMES.ENTER_EMAIL_SIGN_IN)
Expand All @@ -185,6 +192,13 @@ describe("Integration::enter email", () => {
email: "[email protected]",
doesUserExist: false,
});
nock(baseApi)
.post(API_ENDPOINTS.CHECK_EMAIL_FRAUD_BLOCK)
.once()
.reply(HTTP_STATUS_CODES.OK, {
email: "[email protected]",
isBlockedStatus: "Pending",
});

request(app)
.post(PATH_NAMES.ENTER_EMAIL_SIGN_IN)
Expand Down Expand Up @@ -236,6 +250,14 @@ describe("Integration::enter email", () => {
doesUserExist: true,
});

nock(baseApi)
.post(API_ENDPOINTS.CHECK_EMAIL_FRAUD_BLOCK)
.once()
.reply(HTTP_STATUS_CODES.OK, {
email: "[email protected]",
isBlockedStatus: "Pending",
});

request(app)
.post(PATH_NAMES.ENTER_EMAIL_SIGN_IN)
.type("form")
Expand Down
Loading