I have an institutional Gmail address that I wanted to be able to use inside Emacs. If you’d be interested in this too, do note that setting up email in Emacs is definitely one of the more laborious things one can do. Setting up a Gmail is even more annoying in Mu4e, because Google now only accepts special tokens (oauth2 tokens), not passwords.
I had various stabs at cracking this over the course of multiple weeks, and while I have something that works, it’s a bit of a mess in my opinion, as I had to piece things together from different packages, blog posts, and forum discussions that I came across over those frustrating days.
Unfortunately for you, I don’t think I can provide a full guide, merely an overview and what worked for me. This post is more of a “I wish I knew about this”, than an actual guide. Why? First of all, this is pretty involved in my opinion, so this would probably be a massive blog post. Second, if I learned one thing from pushing through with this, it’s that email in Emacs is a constantly evolving process. I could not make many of the things other people were suggesting work for me, often because some requirement had changed since their posts had been published.
First things first, there are a few components one needs to grasp:
- Emacs email client: gnus, mu4e, notmuch, etc.
- These are the emacs packages that interface with your mailing programs
- IMAP program: offlineimap, mbsync
- These are terminal programs, that, given the right credentials can download your emails to your computer
- SMTP program: msmtp
- Receiving and sending email are completely separate processes, so you also need a program that can use your credentials to authorize sending email.
- Some way of fetching Oauth tokens. In my case mutt-oauth2.py
- Instead of passwords, Google and Outlook require tokens that have a limited lifetime
- So you’ll need a program that can fetch the token when needed.
- GPG agent: gnupg
- Finally, because we’re dealing with sensitive credentials, its standard to store your access tokens in an encrypted file
- I hadn’t used gpg prior to setting this up, and found this was the most complicated part
Only once all of these programs are working together in tandem will you be able to properly use email in Emacs. It’s complex to say the least. I really would not recommend a beginner to try this unless they can follow a guide that exactly matches their situation.
Setting up oauth2
I saw a few mentions of using the python script of the mutt CLI email client online while trying to figure out oauth with emacs. This turned out to be the easiest way to set it up I could find.
First, you download the python script
https://gitlab.com/muttmua/mutt/-/blob/master/contrib/mutt_oauth2.py
Then you need access tokens. Some guides recommend you generate these yourself with the Google Cloud API. I found this part kind of confusing because there’s conflicting information online on what the exact settings need to be and very little in the way of feedback when it’s not working. I had to turn to another hack that I found buried in some forum posts, which is to use the clientId and ClientSecret from Thunderbird. This is pretty hacky as you’re basically abusing the fact that Thunderbird has these tokens available in their public github repository. There’s a comment in the source, recommending you not to use these though as they may not have these keys publicly displayed in the future. So if they are no longer available when you read this, I’m sorry. Let’s hope there are better guides in the future.
Grab the clientId and clientSecret for accounts.google.com here: https://github.com/mozilla/releases-comm-central/blob/master/mailnews/base/src/OAuth2Providers.sys.mjs#L180
Then, we need to edit the oauth python script.
set redirect_url to https://localhost/ and set YOUR_GPGIDENTITY to your email address.
If you have not already, make sure you have created a gpg key for your email address.
Make mutt_oauth2.py executable, then run
./mutt_oauth2.py --authorize yourgpgkeyname
Choose google, then localhostauthcode. It should display a URL, that if followed, will ask if you authorize Thunderbird to access your emails. If you do, you should now see various keys printed to your console. Hurray, these are the tokens we’ll need.
Some notes on GPG
By default you’ll have to run gpg decrypt yourgpgkeyname every now and then to decrypt the token. You can change this by setting a really high time limit in the gpg-agent.conf and restarting the agent
Example:
default-cache-ttl 34560000
max-cache-ttl 34560000
Setting up IMAP
Your IMAP program will need to be configured for your email address. This shouldn’t be too hard. You should be able to follow any existing guide for this step.
I’ve found it helpful to sanity check myself by comparing the values to what Thunderbird or Vivialdi Mail configures automatically.
Here’s what I have in my .offlineimaprc:
[general]
accounts = UciAccount
[Account UciAccount]
localrepository = LocalUciAccount
remoterepository = RepositoryUciAccount
quick = 10
[Repository LocalUciAccount]
type = Maildir
localfolders = ~/Mail/UciAccount
[Repository RepositoryUciAccount]
type = Gmail
remoteuser = <your_email>
ssl_version = tls1_2
auth_mechanisms = XOAUTH2
oauth2_client_id = <your_clientId>
oauth2_client_secret = <your_clientSecret
oauth2_refresh_token = <your_refreshToken>
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
ssl = yes
Do note that my sslcertfile directory is specific to my operating system, NixOS.
After defining this config, you have to run offlineimap in your terminal. Hopefully it starts downloading your emals to that local folder now. Make sure the folder exists before you do this.
Setting up mu4e
This part is also not too hard.
You can follow any guide. I have multiple accounts so my config is more complex (using mu4e-contexts).
For completeness sake, here is the relevant part of my emacs config:
(setq smtpmail-debug-info t)
(setq smtpmail-debug-verbose t)
(setq mu4e-get-mail-command "sh -c 'offlineimap -a UciAccount && mu index'")
(setq mu4e-update-interval 600)
(require 'mu4e)
(setq mu4e-contexts
`(
,(make-mu4e-context
:name "UCI Account"
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches
msg '(:from :to :cc :bcc) "your@mail.com")))
:vars `(
(mu4e-maildir . "~/Mail/UciAccount")
(mu4e-trash-folder . "/UciAccount/[Gmail].Trash")
(mu4e-refile-folder . "/UciAccount/[Gmail].All Mail")
(mu4e-drafts-folder . "/UciAccount/[Gmail].Drafts")
(mu4e-sent-folder . "/UciAccount/[Gmail].Sent Mail")
(user-mail-address . "your@mail.com")
(user-full-name . "Your name")
(smtpmail-smtp-user . "your@mail.com")
(smtpmail-local-domain . "uci.edu")
(smtpmail-default-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl) ; Use SSL for port 465
(smtpmail-auth-supported . (xoauth2)) ; Enable XOAUTH2
))
))
Once you have the neccessary elisp you have to adapt this command and run it in your terminal
mu init --maildir=~/Mail \ --my-address=user1@mail.com
This should index your downloaded mail for access in mu4e.
Sending email
If you successfully followed my gpg advice this hopefully isn’t too painful.
It’s a similar process where you have to set up a configuration file.
Here’s what i have in .msmtprc:
# --- Global Defaults ---
defaults
auth on
tls on
tls_starttls on
logfile ~/.msmtp.log
# --- Account 1: Gmail (OAuth2) ---
account gmail
host smtp.gmail.com
port 587
from your@mail.com
user your@mail.com
auth xoauth2
passwordeval "python3 /home/lm/.emacs.d/mutt_oauth2.py /home/lm/.emacs.d/gmailpw"
Notice that the password eval argument is giving the path to my mutt_oauth2.py script and the location of my gpg file with the tokens.
Final words
Hopefully this is of some help to you. While this isn’t a comprehensive guide, it represents the key learnings I had to make over my many failed attempts to get this to work.
Sanity check that 1) your emails actually downloaded, 2) that sending actually succeeds and arrives for the recipient.
If you figured it out, congratulations, and happy emailing!