Institutional Gmail in Emacs

Some tips for staying sane while in Oauth purgatory
emacs
Author

Luke

Published

December 28, 2025

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:

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!