Skip to content

Commit

Permalink
ATO-814: Added error handling to cognito sms lambda.
Browse files Browse the repository at this point in the history
  • Loading branch information
kalpaitch committed Aug 6, 2024
1 parent 770c244 commit 9ae32d7
Show file tree
Hide file tree
Showing 9 changed files with 1,149 additions and 1,059 deletions.
1 change: 1 addition & 0 deletions backend/cognito/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
},
"dependencies": {
"@aws-sdk/client-ses": "^3.609.0",
"@aws-lambda-powertools/logger": "2.6.0",
"notifications-node-client": "^7.0.0",
"esbuild": "^0.23.0",
"@aws-crypto/client-node": "^3.2.0"
Expand Down
28 changes: 20 additions & 8 deletions backend/cognito/src/handlers/send-security-code-text-message.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {CustomSMSSenderTriggerEvent} from "aws-lambda";
import {Context, CustomSMSSenderTriggerEvent, APIGatewayProxyResult} from "aws-lambda";

Check warning on line 1 in backend/cognito/src/handlers/send-security-code-text-message.ts

View workflow job for this annotation

GitHub Actions / Linting

'APIGatewayProxyResult' is defined but never used
import {NotifyClient} from "notifications-node-client";
import {toByteArray} from "base64-js";
import {buildClient, CommitmentPolicy, KmsKeyringNode} from "@aws-crypto/client-node";
import {Logger} from "@aws-lambda-powertools/logger";

const {decrypt} = buildClient(CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);
const generatorKeyId = process.env.DECRYPTION_KEY_ARN;
Expand All @@ -10,19 +11,30 @@ const keyring = new KmsKeyringNode({generatorKeyId});
const apiKey = process.env.NOTIFY_API_KEY;
const templateId = process.env.SECURITY_CODE_TEXT_MESSAGE_TEMPLATE;
const notifyClient = new NotifyClient(apiKey);
const logger = new Logger({
serviceName: "self-service-experience"
});

export const lambdaHandler = async (event: CustomSMSSenderTriggerEvent, context: Context): Promise<void> => {
logger.addContext(context);

export const lambdaHandler = async (event: CustomSMSSenderTriggerEvent): Promise<void> => {
if (!event.request.code) {
logger.error("No request code provided, failed to send sms.");
throw new Error("Missing code parameter");
}

const plainTextCode = await decryptText(event.request.code);
const phoneNumber = event.request.userAttributes.phone_number;
try {
const plainTextCode = await decryptText(event.request.code);
const phoneNumber = event.request.userAttributes.phone_number;

await notifyClient.sendSms(templateId, phoneNumber, {
personalisation: {code: plainTextCode},
reference: null
});
await notifyClient.sendSms(templateId, phoneNumber, {
personalisation: {code: plainTextCode},
reference: null
});
} catch (e) {
logger.error("Failed to send security code sms", e);
throw e;
}
};

async function decryptText(encryptedText: string): Promise<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ process.env.SECURITY_CODE_TEXT_MESSAGE_TEMPLATE = templateId;

import {toByteArray} from "base64-js";
import {lambdaHandler} from "../../src/handlers/send-security-code-text-message";
import {smsSenderTrigger} from "../mocks";
import {lambdaContext, smsSenderTrigger} from "../mocks";

describe("Custom SMS sender", () => {
it("Send SMS with a security code", async () => {
await lambdaHandler(smsSenderTrigger(phoneNumber, encryptedCode));
await lambdaHandler(smsSenderTrigger(phoneNumber, encryptedCode), lambdaContext);

expect(sendSms).toBeCalledTimes(1);
expect(sendSms.mock.calls[0][0]).toBe(templateId);
Expand All @@ -52,6 +52,6 @@ describe("Custom SMS sender", () => {
});

it("Throw an error for missing code", async () => {
await expect(lambdaHandler(smsSenderTrigger(phoneNumber))).rejects.toThrow(/missing.*code/i);
await expect(lambdaHandler(smsSenderTrigger(phoneNumber), lambdaContext)).rejects.toThrow(/missing.*code/i);
});
});
25 changes: 24 additions & 1 deletion backend/cognito/tests/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import {CustomMessageForgotPasswordTriggerEvent, CustomSMSSenderTriggerEvent} from "aws-lambda";
import {Context, CustomMessageForgotPasswordTriggerEvent, CustomSMSSenderTriggerEvent} from "aws-lambda";

export const smsSenderTrigger = (number: string, code?: string) =>
({request: {code: code, userAttributes: {phone_number: number}}} as unknown as CustomSMSSenderTriggerEvent);

export const lambdaContext: Context = {
callbackWaitsForEmptyEventLoop: false,
functionName: "mocked",
functionVersion: "mocked",
invokedFunctionArn: "mocked",
memoryLimitInMB: "mocked",
awsRequestId: "mocked",
logGroupName: "mocked",
logStreamName: "mocked",
getRemainingTimeInMillis(): number {
return 999;
},
done(): void {
return;
},
fail(): void {
return;
},
succeed(): void {
return;
}
};

export const TEST_PROTOCOL = "https";
export const TEST_HOST = "some.service.gov.uk";
export const TEST_CODE_PARAM = "12345";
Expand Down
Loading

0 comments on commit 9ae32d7

Please sign in to comment.