From 6bf191a028e6571316ebd83703328ae4c39d005f Mon Sep 17 00:00:00 2001 From: Alireza Lotfi Date: Tue, 13 Sep 2022 10:23:04 -0400 Subject: [PATCH] django-mailbox-oauth-imap: support office365 oauth --- django_mailbox/models.py | 10 ++++- django_mailbox/transports/__init__.py | 1 + django_mailbox/transports/office365.py | 58 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 django_mailbox/transports/office365.py diff --git a/django_mailbox/models.py b/django_mailbox/models.py index 616a132..5531624 100644 --- a/django_mailbox/models.py +++ b/django_mailbox/models.py @@ -31,7 +31,7 @@ from django_mailbox import utils from django_mailbox.signals import message_received from django_mailbox.transports import Pop3Transport, ImapTransport, \ MaildirTransport, MboxTransport, BabylTransport, MHTransport, \ - MMDFTransport, GmailImapTransport + MMDFTransport, GmailImapTransport, Office365ImapTransport logger = logging.getLogger(__name__) @@ -216,6 +216,14 @@ class Mailbox(models.Model): archive=self.archive ) conn.connect(self.username, self.password) + elif self.type == 'office365': + conn = Office365ImapTransport( + self.location, + port=self.port if self.port else None, + ssl=True, + archive=self.archive + ) + conn.connect(self.username, self.password) elif self.type == 'pop3': conn = Pop3Transport( self.location, diff --git a/django_mailbox/transports/__init__.py b/django_mailbox/transports/__init__.py index 7aeef95..6e31435 100644 --- a/django_mailbox/transports/__init__.py +++ b/django_mailbox/transports/__init__.py @@ -8,3 +8,4 @@ from django_mailbox.transports.babyl import BabylTransport from django_mailbox.transports.mh import MHTransport from django_mailbox.transports.mmdf import MMDFTransport from django_mailbox.transports.gmail import GmailImapTransport +from django_mailbox.transports.office365 import Office365ImapTransport diff --git a/django_mailbox/transports/office365.py b/django_mailbox/transports/office365.py new file mode 100644 index 0000000..91278ad --- /dev/null +++ b/django_mailbox/transports/office365.py @@ -0,0 +1,58 @@ +import logging +import urllib3 +import json + +from django_mailbox.transports.imap import ImapTransport +from django.conf import settings + + +logger = logging.getLogger(__name__) + + +class Office365ImapTransport(ImapTransport): + def connect(self, username, password): + # Try to use oauth2 first. It's much safer + try: + self._connect_oauth(username) + except (TypeError, ValueError) as e: + logger.warning("Couldn't do oauth2 because %s" % e) + self.server = self.transport(self.hostname, self.port) + typ, msg = self.server.login(username, password) + self.server.select() + + def _connect_oauth(self, username): + # username should be an email address that has already been authorized + # for gmail access + + access_token = None + while access_token is None: + try: + access_token = self.get_office365_access_token() + except Exception as e: + raise ValueError( + f"Could not acquire the access token for {username} exception details={e}" + ) + + auth_string = f"user={username}\1auth=Bearer {access_token}\1\1" + self.server = self.transport(self.hostname, self.port) + self.server.authenticate("XOAUTH2", lambda x: auth_string) + self.server.select() + + def get_office365_access_token(): + url = f"https://login.microsoftonline.com/{settings.MICROSOFT_0365_TENENT_ID}/oauth2/v2.0/token" + oauth_params = { + "client_id": settings.MICROSOFT_0365_CLIENT_ID, + "client_secret": settings.MICROSOFT_0365_CLIENT_SECRET, + "grant_type": "client_credentials", + "scope": "https://graph.microsoft.com/.default", + } + http = urllib3.PoolManager() + response = http.request("POST", url, oauth_params) + if response.status == 200: + token = json.loads(response.data).get("access_token") + logger.info(f"[get_office365_access_token] token aquired") + return token + else: + raise Exception( + f"[get_office365_access_token] Failed to authenticate with O365 {response.data}" + )