Home Manager Setup for offline mail for your ETH mail

So if you used to have an offline mail setup for your ETH mail, you probably know that they decided to change their mail system a couple of months ago. Not only did this change break my old, pretty standard, setup, and as far as I know, they made the change overnight without giving a heads-up.

Since then, I have had to use the Outlook webmail client, which, other than being proprietary software, is not ideal1. But since I rarely sent emails, it wasn't such a big issue. However, when looking for a thesis project, sending many emails is unavoidable, so working with Outlook started to get more and more annoying.

I recently found a blog post that talked about setting up Isync and Msmtp for Office365's OAUTH2 mail, which covers most of the stuff I needed, but some Home Manager/Nix specific things were not covered.

Setting up OAUTH2

I will assume you already have a GPG key, otherwise see the official docs. First, Copy the mutt_oauth2 script from Mutt's repo and placed it in a file named mutt_oauth2. Then in a Home Manager-managed file in the same directory, put the following.

{
  home.packages = [
    (pkgs.writers.writePython3Bin "mutt_oauth2" {
      flakeIgnore = [ "E501" "E265" "F401" "E226" "W504" ];
    }(builtins.readFile ./mutt_oauth2.py))
  ];
}

Before switching to a new generation, you have to edit the mutt_oauth2 file. In the ENCRYPTION_PIPE variable, put in the mail address of your GPG key, which you will use to decrypt and encrypt your OAUTH token (see later).

ENCRYPTION_PIPE = ['gpg', '--encrypt', '--recipient', '<>']

Then in the microsoft section of registrations, do

'client_id': '08162f7c-0fd2-4200-a84a-f25a4db0b584',
'client_secret': 'TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82',

Don't ask me why this is needed.

Now create an OAUTH2 token with the mutt_oauth2 script

$ mutt_oauth2 -t </path/to/oauth/token> --authorize
# write "microsoft" for the first question
# "localhostauthcode" for the second
# <username>@ethz.ch for the third

I would recommend putting the token file in your pass store, if you use pass.

Isync

Isync doesn't have support for OAUTH2 by default, so we install an additional package and override the default Isync package.

{
  home.packages = [
    pkgs.cyrus-sasl-xoauth2
  ]
  programs.mbsync = {
    enable = true;
    package = (pkgs.isync.override { withCyrusSaslXoauth2 = true; });
  };
}

For the accounts.email.accounts.<username> part (you might want to adjust some of these to your preference; consult the Man page for details)

{
  accounts.email.accounts = {
    <username> = rec {
      address = "xingchen@student.ethz.ch";
      maildir = {
        path = "</some/path>";
      };
      folders = {
        inbox = "";
      };
      userName = "<username>";
      realName = "<name>";
      passwordCommand = "mutt_oauth2 -t </path/to/oauth/token>";
      imap = {
        host = "outlook.office365.com";
        port = 993;
        tls = {
          enable = true;
        };
      };

      mbsync = {
        enable = true;
        create = "both";
        expunge = "both";
        subFolders = "Maildir++";
        remove = "imap";
        patterns = [ "*" ];
        extraConfig = {
          account = {
            User = "<username>@ethz.ch";
            AuthMechs = "XOAUTH2";
          };
          channel = {
            SyncState = "*";
          }
        };
      };
    };
  };
} # Who said only Lisp has many brackets?

Msmtp

The Msmtp part is the simplest, simply add the following.

{
  programs.msmtp.enable = true;
  accounts.email.accounts = {
    <username> = rec {
      smtp = {
        host = "outlook.office365.com";
        port = 587;
        tls = {
          enable = true;
        };
      };

      msmtp.extraConfig = {
        protocol = "smtp";
        tls = "on";
        tls_starttls = "on";
        auth = "xoauth2";
        user = "<username>@ethz.ch";
        passwordeval = "mutt_oauth2 -t </path/to/oauth/token>";
      };
    };
  };
}

Now you should be able to fetch and send your ETH mails!


  1. https://useplaintext.email/↩︎