Introduction
Because this blog is a static website hosted on GitHub Pages (which I wrote about in my AI-Free Website Design post), I do not have a backend server. That means I cannot simply run a PHP script or a Node.js process to handle contact forms or send out newsletters.
But that hasnât stopped me. I use a hybrid approach: a lightweight service for receiving messages, and pure Python code for sending them.
Getting your messages
To handle incoming form submissions on a static site, you need an endpoint. For years, I have used UseBasin.com.
The setup is incredibly straightforward: you generate an HTML form on their dashboard, copy the action URL, and paste it into your siteâs HTML. You can customise the form styling entirely with your own CSS.
I prefer it over other solutions because it handles spam filtering effectively and allows me to export my subscriber list easily. Once I download the new subscribers as a CSV file to my local machine, I am ready for the fun part: sending the emails with Python!
Sending email messages
We will cover the necessary steps, including setting up your Gmail account, using Pythonâs smtplib module, and configuring all the essential security settings in Gmail.
Prerequisites
Before we start, ensure you have the following:
- Python installed on your system. Download it from the official website.
- Gmail Account to utilise Googleâs SMTP server.
Security Settings
We can now use the Less Secure App Access and Enhanced Security (OAuth2.0).
The main difference between Enhanced Security (OAuth2.0) and Less Secure App Access for sending emails with Python via Gmail boils down to authentication method and security implications:
- Enhanced Security (OAuth2.0) provides robust security features and granular control. Your application doesnât directly expose your Gmail password, significantly reducing the risk of unauthorised access or hacking. To begin with, you create credentials (client ID and secret) through the Google Cloud Platform. Your application will request an access token from Google using these credentials. Google verifies your identity and grants a temporary access token specific to your application and intended actions (e.g., emails). Next, your application uses the access token to authenticate with Gmailâs API and send emails securely.
- Less Secure App Access is a less secure option and should be avoided in production environments or when security is a priority. Opt for this only for testing purposes or if your application cannot use OAuth2.0. Your application directly enters your Gmail username and password to connect to Gmailâs SMTP server while having full access to your Gmail account based on the credentials you provided.
Please note that as of late 2024, Google requires Enhanced Security (OAuth2) for Google Workspace, and âLess secure app accessâ has been largely deprecated. Read the historical context in Google Workspace Updates. If you are building a new application today, you should default to OAuth2.
Suppose you are curious about the security of using Pythonâs e-mail libraries. In that case, I suggest reading the comprehensive article Nothing new, still broken, insecure by default since then: Pythonâs e-mail libraries and certificate verification.
Email parameters and messages
Firstly, we set up the email credentials, recipient, subject, and body.
MAIL_USERNAME = 'your_email@gmail.com' # your_email
password = 'your_password' # is not required for the OAuth2 method
to_email = 'your_email@gmail.com'
subject = 'Test Email'
body = 'This is a test email sent from Python.'
The email.mime library creates email content in a structured format, allowing for more complex email structures, such as attachments and HTML content.
We use MIMEMultipart to create a message container and attach the email body using MIMEText.
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
message = MIMEMultipart()
message['From'] = MAIL_USERNAME
message['To'] = to_email
message['Subject'] = subject
message.attach(MIMEText(body, 'plain'))
Calling message.attach(MIMEText(body, 'plain')) attaches the body of the email as simple text. However, it is often desirable to send emails in HTML format so you can control the styling.
You can further improve the HTML string with inline CSS that can enable a better design, dark/light mode, images and fonts you prefer,
html_string = f"<html><body style='background: black; color: white;'>{body}</body></html>"
body_html = MIMEText(html_string, "html")
message.attach(body_html)
The email client will try to render the last attached (HTML) part first; it will snow the plain text when it fails.
Less Secure App Access
Setting Up Your Gmail Account
To send emails through Gmailâs SMTP server, enable âLess secure app accessâ in your Gmail account settings. This allows third-party apps to access your account.
To enable âLess Secure Appsâ for your Gmail account, youâll need to disable âTwo-Step Verificationâ first if it is disabled.
To turn on the âLess secure app accessâ, go to Google Account Security Settings.
Import Necessary Libraries
We import smtplib for sending emails using the Simple Mail Transfer Protocol (SMTP), ssl for creating a secure SSL context, MIMEText and MIMEMultipart from email.mime.text and email.mime.multipart respectively, for creating the email content.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import ssl
SSL is a security protocol that encrypts communication between a web server and a browser or client, protecting sensitive information such as login credentials and credit card details from unauthorised interception.
Send the Email
Finally, we establish a secure connection with Gmailâs SMTP server using SMTP_SSL, log in, and send the email.
# Create a secure SSL context
context = ssl.create_default_context()
# Send the email using SMTP over SSL
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
# Log in to the SMTP server
server.login(MAIL_USERNAME, password)
# Send the email
server.sendmail(MAIL_USERNAME, to_email, message.as_string())
We create a secure SSL context to secure the connection to the SMTP server with ssl.create_default_context(). The SSL context ensures that the connection to the SMTP server is secure, protecting your login credentials and email content from being intercepted.
This function uses SMTP over SSL with smtplib.SMTP_SSL(âsmtp.gmail.comâ, 465, context=context) is used as the server, maintaining a connection to the Gmail SMTP server on port 465.
The function server.login(MAIL_USERNAME, password) logs us into the SMTP server using your Gmail address and password.
Finally, server.sendmail(MAIL_USERNAME, to_email, message.as_string()) sends the email from your Gmail address to the recipientâs email address using the created email message.
Please notice that if you have two-factor authentication enabled on your Google account, youâll need to generate an app-specific password instead of using your regular Gmail password.
The test e-mail went well for me. Letâs go further with OAuth2 usage.
Using OAuth2.0 for secure authentication
OAuth2.0 enhances security by avoiding the need to store and manage passwords.
Prerequisites
When we explored the Less Secure App Access, we already mentioned the required G-mail account, Python installation, and the smtplib library (included with Python).
For this more secure OAuth2.0-based setup, we have to install the oauth2client and google-auth libraries:
pip install google-auth google-auth-oauthlib google-auth-httplib2
Setting Up Your Gmail Account
To send emails through Gmailâs SMTP server using OAuth2.0, follow these steps:
- Create a Project in Google Cloud Console:
- Go to the Google Cloud Console.
- Create a new project.
- Enable the Gmail API for your project.
- Create OAuth2.0 Credentials:
- Go to the âCredentialsâ page.
- Create OAuth 2.0 Client IDs and download the
.jsoncredentials file.
Please note that you might need to fill out the OAuth consent screen when you do not have an organisation set up. In that form, you can fill out your app name, user support email, developer contact information, and other relevant fields.
You can choose any permission level that fits your app requirements in the scope screen. For sending e-mails, I have enabled the following:
- Gmail API âŠ/auth/gmail.send Send email on your behalf
You can add more scope elements as needed in your application.
Next, we will have to create an OAuth client ID. I have selected a Desktop app with the name âMy blog mailerâ. As a result, you will get the message window âOAuth client created
The client ID and secret can always be accessed from Credentials in APIs & Services, where we download our JSON credentials for further use. I have renamed it âclient_secret_for_my_blog_mailer.json.â
Import Necessary Libraries
We import necessary libraries, os for checking the existence of the token file, Request from google.auth.transport.requests to refresh credentials, Credentials from google.oauth2.credentials to handle the credentials,InstalledAppFlow from google_auth_oauthlib.flow to manage the OAuth2 flow.
import os
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
For sending emails, we will use base64 to encode the message in a format suitable for the Gmail API, build from googleapiclient.discovery to build the Gmail API service, and HTTPError from requests to handle potential HTTP errors during the API request.
import base64
from googleapiclient.discovery import build
from requests import HTTPError
Define OAuth2.0 Scope and Token File
Set the Gmail API scope and specify paths for the token file TOKEN_FILE and credentials files CREDENTIALS_FILE (usually downloaded from the Google Cloud Console)
Scopes required for accessing the Gmail API. In this case, the scope is set to allow sending emails.
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
TOKEN_FILE = 'token.json'
CREDENTIALS_FILE = 'client_secret_for_my_blog_mailer.json'
Please note that we still need the âtoken.jsonâ file; we will get it soon.
Function to Get OAuth2.0 Credentials
The function get_oauth2_credentials() handles the OAuth2.0 flow, storing and refreshing tokens as needed.
def get_oauth2_credentials():
"""
Retrieves OAuth2 credentials for accessing the Gmail API.
This function checks if valid credentials are available locally. If not,
it initiates the OAuth2 flow to obtain new credentials and saves them for
future use.
Returns:
Credentials: OAuth2 credentials for the Gmail API.
"""
creds = None
# Check if the token file exists
if os.path.exists(TOKEN_FILE):
# Load the credentials from the token file
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
# If no valid credentials are available, initiate the OAuth2 flow
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
# Refresh the credentials if they are expired
creds.refresh(Request())
else:
# If no valid credentials are found, initiate the OAuth2 flow
flow = InstalledAppFlow.from_client_secrets_file(CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
# Save the new credentials to the token file for future use
with open(TOKEN_FILE, 'w') as token:
token.write(creds.to_json())
return creds
If the token file exists, we load the credentials from the file. Otherwise, we initiate the OAuth2 flow. We also refresh the credentials using the refresh token. The newly refreshed token is saved into the file âTOKEN_FILEâ for future usage.
When calling the function above, your default web browser window will open and request email access. When you select your Gmail account, you will get a message: âThe authentication flow has completed. You may close this window.â
Send the Email
We can next use these credentials in the function send_email_with_oauth2(). The service = build('gmail', 'v1', credentials=creds) uses the retrieved credentials to build the Gmail API service for version 1 of the API.
def send_email_with_oauth2():
"""
Sends an email using the Gmail API with OAuth2 authentication.
This function retrieves OAuth2 credentials, builds the Gmail API service,
creates a raw email message, and sends it using the Gmail API.
"""
# Retrieve OAuth2 credentials
creds = get_oauth2_credentials()
# Build the Gmail API service
service = build('gmail', 'v1', credentials=creds)
# Create the raw message object
# The 'message' variable should be defined with the email content
raw_message = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}
try:
# Send the email message
sent_message = service.users().messages().send(userId="me", body=raw_message).execute()
print(f'Message {sent_message} was sent')
except HTTPError as error:
# Handle HTTP errors
print(f'Error: {error}')
sent_message = None
send_email_with_oauth2()
We use base64 for creating the raw email message raw_message = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}, which encodes the email message in base64 format. The message variable is an instance of an email message object (e.g., email.mime.text.MIMEText defined before.
Finally, we send the email in the try block using the Gmail API. When catching the HTTPError exceptions, we print an error message and setâ sent_message to None`.
The e-mail was sent well again :)
This approach ensures that the function is self-contained, easy to understand, and handles potential errors gracefully.
Using OAuth2.0 to send emails with Python and Gmail provides a secure way to handle authentication, avoiding the need to manage passwords directly.
For more details, refer to the official Python documentation on smtplib and the Gmail API Overview.
Conclusion
In this post, I have shared the main ideas behind my email messaging setup: capturing subscriptions securely with UseBasin.com, and dispatching newsletters programmatically using Python and Gmail.
Sending emails using Python with smtplib is incredibly satisfying. Once you have the OAuth2 authentication configured, you can automate notifications, error reporting, or even your own bulk email campaigns.
Now itâs your turn to start your email pet project. Best of luck!
Try the following fantastic AI-powered applications.
I am affiliated with some of them (to support my blogging at no cost to you). I have also tried these apps myself, and I liked them.
10web.io builds a website with AI. You can also host your wesbite on 10Web Hosting, and optimise it with PageSpeed Booster.
B12.io Recently, I have found an AI-powered platform that enables you to create professional websites, pages, posts, and emails with ease. I will also give it a try and soon write a new post about B12.io (I am working on my coding post at the moment :).
Mixo.io generates websites instantly using AI. Builds stunning landing pages without any code or design. Includes a built-in email waiting list and all the tools you need to launch, grow, and test your ideas.
UseBasin.com is a comprehensive backend automation platform for handling submissions, processing, filtering, and routing without coding.
Did you like this post? Please let me know if you have any comments or suggestions.
Python posts that might be interesting for youRelated tools you may want to try next.
B12.io Recently, I have found an AI-powered platform that enables you to create professional websites, pages, posts, and emails with ease. I will also give it a try and soon write a new post about B12.io (I am working on my coding post at the moment :).
References
5. Google Account Security Settings