From 6b0a85a6f61ee78340b1d0eeb0e666258fe8fe79 Mon Sep 17 00:00:00 2001 From: Kelley R Date: Thu, 12 Dec 2024 15:36:38 -0500 Subject: [PATCH 1/2] Improve code documentation --- verify-retry/assets/index.html | 167 ++++++++++++------------- verify-retry/functions/check-verify.js | 48 +++---- verify-retry/functions/start-verify.js | 28 +++-- 3 files changed, 129 insertions(+), 114 deletions(-) diff --git a/verify-retry/assets/index.html b/verify-retry/assets/index.html index bfd2d6e6..9f6b3e9c 100644 --- a/verify-retry/assets/index.html +++ b/verify-retry/assets/index.html @@ -75,7 +75,7 @@ class="logo" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" - viewbox="0 0 60 60" + viewBox="0 0 60 60" > Twilio Logo - + @@ -169,7 +169,7 @@ function showError(error) { console.error(error); - showStatus(error, (color = "#a94442")); + showStatus(error, color = "#a94442"); } function showStatus(message, color = "gray") { @@ -184,7 +184,21 @@ ).innerHTML = `${message}`; } + function showOtpForm() { + document.getElementById("login").style.display = "none"; + document.getElementById("otp").style.display = "block"; + } + + function showCallFallback(attempts) { + let minAttemptForVoice = 3; + if (attempts < minAttemptForVoice) return; + + document.getElementById("call").style.display = "block"; + } + function getRetryTimeout(attemptNumber) { + // progressively back off the retry timeout + // the more times someone requests a code the longer it will take to retry const retryTimeouts = { 1: 30, 2: 40, @@ -200,18 +214,6 @@ return retryTimeouts[attemptNumber] || defaultTimeout; } - function showOtpForm() { - document.getElementById("login").style.display = "none"; - document.getElementById("otp").style.display = "block"; - } - - function showCallFallback(attempts) { - let minAttemptForVoice = 3; - if (attempts < minAttemptForVoice) return; - - document.getElementById("call").style.display = "block"; - } - let timer; let timeRemaining; let retryButton = document.getElementById("retry"); @@ -240,9 +242,10 @@ } } + // save the phone number to use for both sending and checking verifications var to; - function sendVerificationToken(event, channel = "sms") { + async function sendVerificationToken(event, channel = "sms") { event.preventDefault(); let statusMessage = channel == "call" ? "☎️ calling..." : "Sending verification code..."; @@ -254,43 +257,43 @@ data.append("channel", channel); data.append("to", to); - fetch("./start-verify", { - method: "POST", - body: data, - }) - .then((response) => { - if (response.status == 429) { - clearStatus(); - showOtpForm(); - showError( - `You have attempted to verify the phone number ${to} too many times. If you received a code, enter it below. Otherwise, please wait 10 minutes and ` - ); - showReset("try again."); - } else if (response.status >= 400) { - clearStatus(); - document.getElementById("otp").style.display = "none"; - document.getElementById("login").style.display = "flex"; - - return response.json().then(({ message }) => { - showError(message); - }); - } else { - showOtpForm(); - return response.json().then((json) => { - if (json.success) { - showStatus(json.message + `.`); - startCountdown(json.attempts); - } else { - showError(json.message); - } - showReset("Edit phone number."); - }); - } - }) - .catch(() => { - showError(`Something went wrong while sending code to ${to}.`); - showReset("Edit phone number."); + try { + let response = await fetch("./start-verify", { + method: "POST", + body: data, }); + + if (response.status == 429) { + clearStatus(); + showOtpForm(); + showError( + `You have attempted to verify the phone number ${to} too many times. If you received a code, enter it below. Otherwise, please wait 10 minutes and ` + ); + showReset("try again."); + } else if (response.status >= 400) { + clearStatus(); + document.getElementById("otp").style.display = "none"; + document.getElementById("login").style.display = "flex"; + + return response.json().then(({ message }) => { + showError(message); + }); + } else { + showOtpForm(); + return response.json().then((json) => { + if (json.success) { + showStatus(json.message + `.`); + startCountdown(json.attempts); + } else { + showError(json.message); + } + showReset("Edit phone number."); + }); + } + } catch (error) { + showError(`Something went wrong while sending code to ${to}.`); + showReset("Edit phone number."); + }; } function clearStatus() { @@ -301,17 +304,17 @@ document.getElementById("call").style.display = "none"; } - function retrySend(event) { + async function retrySend(event) { clearStatus(); sendVerificationToken(event); } - function sendVoiceToken(event) { + async function sendVoiceToken(event) { clearStatus(); - sendVerificationToken(event, (channel = "call")); + sendVerificationToken(event, channel = "call"); } - function validateToken(event) { + async function validateToken(event) { event.preventDefault(); clearStatus(); showStatus("Checking code..."); @@ -323,36 +326,32 @@ var tags = document.querySelectorAll("input[name=tags]:checked"); tags.forEach((tag) => data.append("tags", tag.value)); - fetch("./check-verify", { - method: "POST", - body: data, - }) - .then((response) => { - return response.json(); - }) - .then((json) => { - if (json.success) { - clearStatus(); - document.getElementById("otp").style.display = "none"; - showStatus(json.message, (color = "#3c763d")); - } else { - showError(`${json.message} Check the code sent to ${to}.`); - } - showReset( - "Use a different phone number or start a new verification." - ); - document.getElementById("code").value = ""; - }) - .catch((error) => { - showError(error); + try { + let response = await fetch("./check-verify", { + method: "POST", + body: data, }); + + let json = await response.json(); + + if (json.success) { + clearStatus(); + document.getElementById("otp").style.display = "none"; + showStatus(json.message, "#3c763d"); + } else { + showError(`${json.message} Check the code sent to ${to}.`); + } + showReset("Use a different phone number or start a new verification."); + document.getElementById("code").value = ""; + } catch (error) { + showError(error); + } } - document - .getElementById("login") - .addEventListener("submit", sendVerificationToken); - document.getElementById("retry").addEventListener("click", retrySend); - document.getElementById("call").addEventListener("click", sendVoiceToken); - document.getElementById("otp").addEventListener("submit", validateToken); + document.getElementById("login") + .addEventListener("submit", (event) => sendVerificationToken(event)); + document.getElementById("retry").addEventListener("click", (event) => retrySend(event)); + document.getElementById("call").addEventListener("click", (event) => sendVoiceToken(event)); + document.getElementById("otp").addEventListener("submit", (event) => validateToken(event)); diff --git a/verify-retry/functions/check-verify.js b/verify-retry/functions/check-verify.js index 8db83371..cfe27dd1 100644 --- a/verify-retry/functions/check-verify.js +++ b/verify-retry/functions/check-verify.js @@ -15,39 +15,46 @@ * "message": string * } */ -const assets = Runtime.getAssets(); -const { detectMissingParams, VerificationException } = require( - assets['/utils.js'].path -); -async function checkVerification(client, service, to, code) { +/** + * Checks the verification status of a given code for a specified service and recipient. + * + * @param {Object} client - The client object used to interact with the verification service. + * @param {string} service - The ID of the verification service. + * @param {string} to - The recipient's phone number or email address. + * @param {string} code - The verification code to check. + * @returns {Promise} - A promise that resolves to a success message if the verification is approved. + * @throws {VerificationException} - Throws an exception if the verification code is incorrect. + */ +async function verifyToken(client, service, to, code) { const check = await client.verify .services(service) - .verificationChecks.create({ - to, - code, - }); + .verificationChecks.create({ to, code }); if (check.status === 'approved') { return 'Verification success.'; - // eslint-disable-next-line no-else-return - } else { - throw new VerificationException(401, 'Incorrect token.'); } + + throw new VerificationException(401, 'Incorrect token.'); } +/** + * Twilio Function to handle verification check. + * + * @param {Object} context - The context object containing environment variables. + * @param {Object} event - The event object containing the request parameters. + * @param {Function} callback - The callback function to return the response. + */ exports.handler = async function (context, event, callback) { const response = new Twilio.Response(); response.appendHeader('Content-Type', 'application/json'); - /* - * uncomment to support CORS - * response.appendHeader('Access-Control-Allow-Origin', '*'); - * response.appendHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); - * response.appendHeader('Access-Control-Allow-Headers', 'Content-Type'); - */ - try { + const assets = Runtime.getAssets(); + const { detectMissingParams, VerificationException } = require( + assets['/utils.js'].path + ); + const missingParams = detectMissingParams(['to', 'code'], event); if (missingParams.length > 0) { throw new VerificationException( @@ -59,8 +66,7 @@ exports.handler = async function (context, event, callback) { const client = context.getTwilioClient(); const service = context.VERIFY_SERVICE_SID; const { to, code } = event; - - const message = await checkVerification(client, service, to, code); + const message = await verifyToken(client, service, to, code); response.setStatusCode(200); response.setBody({ diff --git a/verify-retry/functions/start-verify.js b/verify-retry/functions/start-verify.js index 8d40f3b8..80379bc9 100644 --- a/verify-retry/functions/start-verify.js +++ b/verify-retry/functions/start-verify.js @@ -1,7 +1,7 @@ /** * Start Verification * - * This Function shows you how to send a verification token for Twilio Verify. + * This Twilio Function shows you how to send a verification token for Twilio Verify. * * Pre-requisites * - Create a Verify Service (https://www.twilio.com/console/verify/services) @@ -17,11 +17,20 @@ * } */ +// Load assets from the Runtime, used to access utility functions const assets = Runtime.getAssets(); const { detectMissingParams, VerificationException } = require( assets['/utils.js'].path ); +/** + * Retrieves the line type of a given phone number with the Lookup API. + * + * @param {Object} client - The Twilio client. + * @param {string} to - The phone number to check. + * @returns {Promise} - The line type of the phone number. + * @throws {VerificationException} - If the phone number is invalid. + */ async function getLineType(client, to) { try { const response = await client.lookups.v2 @@ -37,17 +46,18 @@ async function getLineType(client, to) { } } +/** + * This function handles the verification process. + * It sends a verification token to the provided phone number with the Verify API. + * + * @param {Object} context - The context object containing environment variables and Twilio client. + * @param {Object} event - The event object containing the request parameters. + * @param {Function} callback - The callback function to return the response. + */ exports.handler = async function (context, event, callback) { const response = new Twilio.Response(); response.appendHeader('Content-Type', 'application/json'); - /* - * uncomment to support CORS - * response.appendHeader('Access-Control-Allow-Origin', '*'); - * response.appendHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); - * response.appendHeader('Access-Control-Allow-Headers', 'Content-Type'); - */ - try { const missingParams = detectMissingParams(['to'], event); if (missingParams.length > 0) { @@ -63,7 +73,7 @@ exports.handler = async function (context, event, callback) { const lineType = await getLineType(client, to); - let channel = typeof event.channel === 'undefined' ? 'sms' : event.channel; + let channel = event.channel ?? 'sms'; let message = `Sent ${channel} verification to: ${to}`; if (lineType === 'landline') { From 249043512a12ff23515d41c8e6127af342d70a8a Mon Sep 17 00:00:00 2001 From: Kelley R Date: Thu, 12 Dec 2024 16:25:46 -0500 Subject: [PATCH 2/2] Don't always trust Copilot review suggestions --- verify-retry/functions/check-verify.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/verify-retry/functions/check-verify.js b/verify-retry/functions/check-verify.js index cfe27dd1..35378ce5 100644 --- a/verify-retry/functions/check-verify.js +++ b/verify-retry/functions/check-verify.js @@ -15,6 +15,10 @@ * "message": string * } */ +const assets = Runtime.getAssets(); +const { detectMissingParams, VerificationException } = require( + assets['/utils.js'].path +); /** * Checks the verification status of a given code for a specified service and recipient. @@ -50,11 +54,6 @@ exports.handler = async function (context, event, callback) { response.appendHeader('Content-Type', 'application/json'); try { - const assets = Runtime.getAssets(); - const { detectMissingParams, VerificationException } = require( - assets['/utils.js'].path - ); - const missingParams = detectMissingParams(['to', 'code'], event); if (missingParams.length > 0) { throw new VerificationException(