OfflineIMAP / offlineimap

Read/sync your IMAP mailboxes (python2) [LEGACY: move to offlineimap3]
http://www.offlineimap.org
Other
1.78k stars 360 forks source link

OAuth2 connection fails only for refresh token #667

Open iffsid opened 4 years ago

iffsid commented 4 years ago

Running offlineimap with the access token works, but running it with the refresh token produces a 401 error.

Running offlineimap -d imap doesn't provide any extra info.

I have verified that my password/token extraction function ( get_password_emacs) returns valid things--by using it for the access token (which works).

General information

Configuration file offlineimaprc

# -*- mode: Conf -*-

[general]
accounts = gmail
ui = basic
socktimeout = 40
pythonfile = ~/.offlineimap/parse.py

[Account gmail]
localrepository = gmail-local
remoterepository = gmail-remote
# postsynchook = mu index
synclabels = yes
#status_backend = sqlite
quick = -1
labelsheader = X-Keywords
filterheaders = X-Keywords
ignorelabels = \Inbox, \Starred, \Sent, \Draft, \Spam, \Trash, \Important

[Repository gmail-local]
type = GmailMaildir
localfolders = ~/Maildir
sep = .
nametrans = lambda folder: re.sub('spam', '[Gmail].Spam',
                           re.sub('drafts', '[Gmail].Drafts',
                           re.sub('sent', '[Gmail].Sent Mail',
                           re.sub('trash', '[Gmail].Trash', folder))))

## GMail OAuth2 setup
## https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap.conf#L764
[Repository gmail-remote]
type = Gmail
auth_mechanisms = XOAUTH2
ssl = yes
ssl_version = tls1_2
starttls = no
sslcacertfile = /etc/ssl/certs/ca-certificates.crt

remoteuser = <redacted>
remotepasseval = get_password_emacs("imap.gmail.com", "993")

oauth2_client_id = <redacted>
oauth2_client_secret = get_password_emacs("imap.api", "9999")
oauth2_request_url = https://accounts.google.com/o/oauth2/token
oauth2_refresh_token = get_password_emacs("imap.refresh", "9999")

realdelete = no
usecompression = yes
folderfilter = lambda foldername: foldername in ['INBOX',
                                                 '[Gmail]/Sent Mail',
                                                 '[Gmail]/Drafts',
                                                 '[Gmail]/Spam',
                                                 '[Gmail]/Trash']
maxsyncaccounts = 5
maxconnections  = 5
nametrans = lambda folder: re.sub('.*Spam$', 'spam',
                           re.sub('.*Drafts$', 'drafts',
                           re.sub('.*Sent Mail$', 'sent',
                           re.sub('.*Trash$', 'trash', folder))))

pythonfile (if any)

import subprocess
def get_output(cmd):
    # Bunch of boilerplate to catch the output of a command:
    pipe = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    (output, errout) = pipe.communicate()
    assert pipe.returncode == 0 and not errout
    return output
def get_password_emacs(host, port):
    cmd = "emacsclient --eval '(offlineimap-get-password \"%s\" \"%s\")'" % (host,port)
    return get_output(cmd).strip().lstrip('"').rstrip('"')

Logs, error

$   offlineimap 
OfflineIMAP 7.2.4
  Licensed under the GNU GPL v2 or any later version (with an OpenSSL exception)
imaplib2 v2.57 (bundled), Python v2.7.17, OpenSSL 1.1.1d  10 Sep 2019
*** Processing account gmail
Establishing connection to imap.gmail.com:993 (gmail-remote)
ERROR: While attempting to sync account 'gmail'
  ('http error', 401, 'Unauthorized', <httplib.HTTPMessage instance at 0x7fcb4950dc80>) (configuration is: {'client_secret': 'get_password_emacs("imap.api", "9999")', 'grant_type': 'refresh_token', 'refresh_token': 'get_password_emacs("imap.refresh", "9999")', 'client_id': '<redacted>'})
*** Finished account 'gmail' in 0:00
ERROR: Exceptions occurred during the run!
ERROR: While attempting to sync account 'gmail'
  ('http error', 401, 'Unauthorized', <httplib.HTTPMessage instance at 0x7fcb4950dc80>) (configuration is: {'client_secret': 'get_password_emacs("imap.api", "9999")', 'grant_type': 'refresh_token', 'refresh_token': 'get_password_emacs("imap.refresh", "9999")', 'client_id': '<redacted>'})

Traceback:
  File "/usr/lib64/python2.7/site-packages/offlineimap/accounts.py", line 293, in syncrunner
    self.__sync()
  File "/usr/lib64/python2.7/site-packages/offlineimap/accounts.py", line 369, in __sync
    remoterepos.getfolders()
  File "/usr/lib64/python2.7/site-packages/offlineimap/repository/IMAP.py", line 452, in getfolders
    imapobj = self.imapserver.acquireconnection()
  File "/usr/lib64/python2.7/site-packages/offlineimap/imapserver.py", line 586, in acquireconnection
    self.__authn_helper(imapobj)
  File "/usr/lib64/python2.7/site-packages/offlineimap/imapserver.py", line 450, in __authn_helper
    if func(imapobj):
  File "/usr/lib64/python2.7/site-packages/offlineimap/imapserver.py", line 384, in __authn_xoauth2
    imapobj.authenticate('XOAUTH2', self.__xoauth2handler)
  File "/usr/lib64/python2.7/site-packages/offlineimap/bundled_imaplib2.py", line 734, in authenticate
    typ, dat = self._simple_command('AUTHENTICATE', mechanism.upper())
  File "/usr/lib64/python2.7/site-packages/offlineimap/bundled_imaplib2.py", line 1740, in _simple_command
    return self._command_complete(self._command(name, *args), kw)
  File "/usr/lib64/python2.7/site-packages/offlineimap/bundled_imaplib2.py", line 1466, in _command
    literal = literator(data, rqb)
  File "/usr/lib64/python2.7/site-packages/offlineimap/bundled_imaplib2.py", line 2331, in process
    ret = self.mech(self.decode(data))
  File "/usr/lib64/python2.7/site-packages/offlineimap/imapserver.py", line 257, in __xoauth2handler
    six.reraise(type(e), type(e)(msg), exc_info()[2])
  File "/usr/lib64/python2.7/site-packages/offlineimap/imapserver.py", line 251, in __xoauth2handler
    self.oauth2_request_url, urllib.urlencode(params)).read()
  File "/usr/lib64/python2.7/urllib.py", line 89, in urlopen
    return opener.open(url, data)
  File "/usr/lib64/python2.7/urllib.py", line 217, in open
    return getattr(self, name)(url, data)
  File "/usr/lib64/python2.7/urllib.py", line 462, in open_https
    data)
  File "/usr/lib64/python2.7/urllib.py", line 381, in http_error
    result = method(url, fp, errcode, errmsg, headers, data)
  File "/usr/lib64/python2.7/urllib.py", line 693, in http_error_401
    errcode, errmsg, headers)
  File "/usr/lib64/python2.7/urllib.py", line 388, in http_error_default
    raise IOError, ('http error', errcode, errmsg, headers)

Steps to reproduce the error

Run offlineimap