Proxying OAuth2 IMAP connections for Outlook and Gmail

All larger email providers are slowly but steadily moving away from regular password-based authentication for email protocols like IMAP and SMTP. There are multiple options to do so. For example, iCloud requires you to generate application passwords for IMAP. Gmail is pushing OAuth2, even though it also allows application passwords. Outlook is moving to OAuth2 for good.

Moving away from regular passwords is great for security reasons but not for application developers. Let's say you run a backup script that regularly logs in to your IMAP account and fetches all new emails. Trivial to achieve when using a password, overly complicated with OAuth2. From where exactly is your script supposed to get that OAuth2 access token anyway?

Hint - you can get a valid OAuth2 access token from EmailEngine, but this is not the topic for this post.

And what if the underlying IMAP library cannot use OAuth2 as an authentication mechanism if you have a token? And even if the server still allows regular passwords for authentication, do you really want your account password to be lying around in some random server that runs the backup script?

Proxying IMAP connections with EmailEngine

EmailEngine includes an IMAP proxy interface that fixes these issues. Your script can connect to that interface like a regular IMAP server, authenticate with a password and then do its stuff as usual.

The password in question would not be the regular account password, though. Instead, you would need to generate a token in EmailEngine that is valid for a specific user and scope (in this case, the scope would be "imap-proxy").

First, your script authenticates against the IMAP interface using the generated token as a password. EmailEngine will then establish a connection to the actual IMAP server, authenticate itself with real account credentials (either by using a password or OAuth2) and then return that established IMAP session to your calling client.

Read here how to setup EmailEngine to work with Outook OAuth2.

In addition, you could easily rotate these passwords (deleting and creating a password token is just an API call away). EmailEngine logs token usage, and you can also set IP address restrictions from where a token is even allowed to be used.

Enable proxy interface

Navigate to Config -> IMAP Proxy Interface. Setup port, host (you probably want to change the host to "0.0.0.0" as the default would only allow connections from localhost), and TLS settings. After you save the settings, EmailEngine spawns an IMAP server on the selected port.

You can verify if the IMAP interface is running from CLI.

– If TLS is not enabled use telnet or nc -c

$ nc -c localhost 2993
* OK EmailEngine IMAP Proxy ready for requests from 127.0.0.1

– If TLS is enabled, use openssl s_client

$ openssl s_client -crlf -connect localhost:2993
... (certificate info)...
* OK EmailEngine IMAP Proxy ready for requests from 127.0.0.1

Generate a token for IMAP authentication

Now that we can connect to the IMAP interface, we need an authentication token as the password. You can generate it with an API call. Make sure that the scopes array includes "imap-proxy". The example also defines IP address restrictions (you can use the generated token from 127.x.x.x IP addresses), but this is optional.

curl -XPOST "http://127.0.0.1:3000/v1/token" \
    -H "Authorization: Bearer <root-token>" \
    -H "Content-type: application/json" \
    -d '{
        "account": "example",
        "scopes": ["smtp", "imap-proxy"],
        "description": "Access token for testing",
        "restrictions": {
            "addresses": ["127.0.0.0/8"]
        }
    }'
{
  "token": "6cad01dae08...458576a026c1ec"
}

IMAP access

Once we have the token, we can use it to authenticate the IMAP session. EmailEngine then connects to the real IMAP server and returns a valid IMAP session.

A LOGIN example 6cad01dae08...458576a026c1ec
* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY...
A OK example authenticated

If, on the other hand, IP restrictions are set, and we try to log in from an unlisted IP address, the authentication operation will fail:

A LOGIN example 6cad01dae08...458576a026c1ec
A NO [AUTHENTICATIONFAILED] Access denied, traffic not accepted from this IP

Deleting a token

Finally, once we do not need this authentication token anymore or we want to invalidate it for security reasons, we can delete it with the API.

curl -XDELETE \
    "http://127.0.0.1:3000/v1/token/6cad01dae08...458576a026c1ec"
{
  "deleted":true
}

Using regular email clients

If you mix imap-proxy scope with the smtp scope and enable the SMTP interface, you can use EmailEngine as the IMAP server for regular email clients.

EmailEngine is used as a proxy server for an iCloud account in Thunderbird