In this tutorial, we’ll create a secure contact form using Cloudflare Pages with Pages Functions to handle form submissions and send emails through Oracle’s Email Delivery API.
Prerequisites
Before getting started, ensure you have:
A Cloudflare account with Pages enabled
An Oracle Cloud Infrastructure (OCI) account with Email Delivery service configured
OCI API key and configuration details ready:
Tenancy OCID
User OCID
API key fingerprint
Private key in PEM format
An approved sender email address in OCI Email Delivery
Project Structure
We’ll create two main files:
public/index.html - Contains the contact form
functions/api/contact.js - Handles form submission and email sending
1. Creating the Contact Form
First, let’s create the contact form in public/index.html. Replace <TURNSTILE_SITE_KEY> with your actual Cloudflare Turnstile site key:
The onRequestPost function exclusively handles POST requests and ignores other HTTP methods, since form submissions use POST requests. It implements proper error handling and response redirects to ensure a smooth user experience.
The handleRequest function coordinates the entire contact form processing workflow. It extracts form data, validates the Turnstile token for spam protection, and forwards the message to Oracle Email Delivery.
The extractFormData function safely extracts all required fields from the form submission. This includes the user’s name, email, message content, and the Turnstile response token used for spam verification.
The validateToken function validates the Turnstile widget response from the frontend to confirm that form submissions are from real users and blocks automated bots. It sends a verification request to Cloudflare’s Turnstile API with the user’s IP address, the token, and your secret key.
The forwardMessage function orchestrates the email creation and delivery process through Oracle’s Email Delivery API. Since Oracle doesn’t provide a direct SDK for Cloudflare Pages Functions, we need to manually construct and sign API requests for authentication.
The formatDate function formats the submission timestamp in a human-readable format with timezone information. This timestamp appears in the email notifications to show exactly when the form was submitted.
The generateEmailContent function creates both HTML and plain text versions of the email notification. The HTML version includes a nicely formatted table showing the form submission details, while the text version provides the same information in a simple format for email clients that don’t support HTML.
91
92
93
94
95
96
97
functiongenerateEmailContent(name,email,message,emailDate){consthtml=`<!DOCTYPE html><html lang="en-gb" dir="ltr"><head><meta charset="utf8"><title>Contact</title><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><p>Someone just submitted your form on <a rel="noopener noreferrer" href="https://halimdaud.com/">https://halimdaud.com/</a>.</p><p>Here's what they had to say:</p><table style="font-family:'Trebuchet MS',Arial,Helvetica,sans-serif;border-collapse:collapse;width:100%"><tbody><tr><th style="border:1px solid #ddd;padding:12px 8px;text-align:left;background-color:#354d91;color:#fff">Name</th><th style="border:1px solid #ddd;padding:12px 8px;text-align:left;background-color:#354d91;color:#fff">Value</th></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>name</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap">${name}</pre></td></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>email</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap"><a href="mailto:${email}">${email}</a></pre></td></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>message</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap">${message}</pre></td></tr></tbody></table><br><p style="text-align:center">Submitted at ${emailDate}</p><br></body></html>`;consttext=`Someone just submitted your form on https://halimdaud.com/.\n\nHere's what they had to say:\n\nname: ${name}\nemail: ${email}\nmessage: ${message}\n\nSubmitted at ${emailDate}`;return{html,text};}
OCI Configuration Extraction
The extractOciConfig function safely extracts Oracle Cloud Infrastructure configuration from environment variables. This includes your tenancy OCID, user OCID, API key fingerprint, and private key needed for API authentication.
Oracle’s Email Delivery API requires properly signed requests for authentication. The following functions handle the complex process of building and signing HTTP requests according to OCI’s authentication requirements.
Main Request Builder
The buildOciRequest function coordinates the entire request building process. It constructs the URL, prepares the request body, builds headers, creates the signing string, generates the signature, and assembles the final authenticated request.
The buildEmailRequestBody function constructs the JSON payload for Oracle’s Email Delivery API. It includes sender information, recipient details, email subject, both HTML and text content, and sets up the reply-to address to the form submitter’s email.
functionbuildEmailRequestBody(html,text,email,env,compartmentId){returnJSON.stringify({sender:{senderAddress:{email:env.SENDER_EMAIL,name:env.SENDER_NAME},compartmentId:compartmentId},recipients:{to:[{email:env.RECEIVER_EMAIL}]},subject:"New submission from https://halimdaud.com/",bodyHtml:html,bodyText:text,replyTo:[{email:email}]});}
Request Headers Preparation
The buildRequestHeaders function creates all the necessary HTTP headers for the OCI API request. This includes the date, content type, accept headers, and most importantly, the SHA-256 hash of the request body which is required for OCI’s signature verification.
The buildSigningString function creates the string that will be digitally signed for authentication. While the order of headers in the signing string doesn’t matter according to OCI documentation, you must specify the exact same order in the headers parameter of the Authorization header. This implementation uses a consistent order: request target, date, host, content SHA-256, content type, and content length.
The createSignature function handles the cryptographic signing process. It imports your private key and uses it to sign the signing string, producing a digital signature that proves the request’s authenticity to Oracle’s servers.
The buildAuthorizationHeader function constructs the final authorization header that contains all the signature information. This header tells Oracle’s API which key was used, what algorithm was used for signing, which headers were included, and provides the actual signature.
The sendEmailViaOci function executes the final API request to Oracle’s Email Delivery service. It handles both successful responses and error cases, providing appropriate logging for debugging purposes.
asyncfunctionsendEmailViaOci(requestConfig){const{url,method,body,headers}=requestConfig;constrequest=newRequest(url,{method,body,headers});constresponse=awaitfetch(request);letdata;if(!response.ok){data=awaitresponse.text();console.error("OCI Email API error:",data);}else{data=awaitresponse.json();console.log("Email sent successfully:",data);}returndata;}
Cryptographic Utility Functions
These essential functions handle the RSA cryptographic operations required for OCI API authentication.
Private Key Import
The importPrivateKey function converts a PEM-formatted private key string into a CryptoKey object that can be used for digital signing. It strips the PEM headers and footers, decodes the base64 content, and imports it using the Web Crypto API.
asyncfunctionimportPrivateKey(pem){// fetch the part of the PEM string between header and footer
constpemContents=pem.substring(PEM_HEADER.length,pem.length-PEM_FOOTER.length-1,);// convert from base64 to ArrayBuffer
constbinaryDer=Buffer.from(pemContents,"base64");constkey=awaitcrypto.subtle.importKey("pkcs8",binaryDer,{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256",},true,["sign"],);returnkey;}
Message Signing
The signMessage function performs the actual digital signing operation. It takes the imported private key and the signing string, then uses the RSASSA-PKCS1-v1_5 algorithm with SHA-256 hashing to create a digital signature that proves the request’s authenticity.
Set up the following environment variables in your Cloudflare Pages project:
TURNSTILE_SECRET_KEY - The Cloudflare Turnstile secret key
OCI_TENANCY - The Oracle Cloud Infrastructure tenancy OCID
OCI_USER - The OCI user OCID
OCI_FINGERPRINT - The API key fingerprint
OCI_PRIVATE_KEY - The private key in PEM format
SENDER_EMAIL - The approved sender email address from OCI Email Delivery
SENDER_NAME - The name to display as the sender
RECEIVER_EMAIL - The email address to receive the contact form submissions
Deployment
Deploy your project to Cloudflare Pages using their Git integration or direct upload. The Pages Functions will automatically handle the form submissions and email sending.
Your contact form is now ready to use! When users submit the form, it will validate the Turnstile token and send the message through Oracle’s Email Delivery service.