Ответ 1
После большого количества чтения и тестирования я наконец смог реализовать рабочий вход в gmail с помощью OAuth2.
Важно отметить, что двухэтапный процесс с использованием "учетной записи службы" выполнял для меня НЕ. Я все еще не понимаю, почему этот процесс не может быть использован, но учетная запись службы, похоже, не имеет доступа к gmail в той же учетной записи. Это справедливо даже в том случае, если учетная запись службы имеет права "редактировать" и включен API Gmail.
Полезные ссылки
Обзор использования OAuth2 https://developers.google.com/identity/protocols/OAuth2
Руководство по использованию OAuth2 с "Установленными приложениями" https://developers.google.com/identity/protocols/OAuth2InstalledApp
Руководство по настройке учетной записи для использования OAuth2 с "Установленные приложения" https://developers.google.com/api-client-library/python/auth/installed-app
Коллекция подпрограмм OAuth2 без полного API Google https://code.google.com/p/google-mail-oauth2-tools/wiki/OAuth2DotPyRunThrough
Шаг 1 - Получите идентификатор клиента Google
Войдите в систему с учетной записью gmail в https://console.developers.google.com/
Запустите проект, включите Gmail API и создайте новый идентификатор клиента для установленного приложения. Инструкции на https://developers.google.com/api-client-library/python/auth/installed-app#creatingcred
Нажмите кнопку "Загрузить JSON" и сохраните этот файл где-нибудь, что будет недоступно для общественности (возможно, это не в репозитории кода).
Шаг 2 - Получите инструменты Python Google OAuth2
Загрузите файл oauth2.py script из https://code.google.com/p/google-mail-oauth2-tools/wiki/OAuth2DotPyRunThrough
Шаг 3 - Получить URL авторизации
Используйте script со второго шага, чтобы получить URL-адрес, разрешающий авторизовать свой проект Google.
В терминале:
python oauth2.py --user={[email protected]} --client_id={your client_id from the json file} --client_secret={your client_secret from the json file} --generate_oauth2_token
Шаг 4 - Получить код авторизации
Вставьте URL-адрес с шага 3 в свой браузер и нажмите кнопку "принять".
Скопируйте код с веб-страницы.
Вставьте код в терминал и нажмите enter. Вы получите:
To authorize token, visit this url and follow the directions: https://accounts.google.com/o/oauth2/auth?client_id{...}
Enter verification code: {...}
Refresh Token: {...}
Access Token: {...}
Access Token Expiration Seconds: 3600
Шаг 5 - Сохранить токен обновления
Скопируйте токен обновления с терминала и сохраните его где-нибудь. В этом примере я сохраняю его в текстовом файле в формате json с ключом "Обновить токен". Но он также может быть сохранен в частной базе данных.
Убедитесь, что токен обновления не доступен публике!
Шаг 6 - Создайте скрученный аутентификатор
Вот пример работы аутентификатора OAuth2. Для этого требуется выполнить команду oauth2.py script с шага 2.
import json
import oauth2
from zope.interface import implementer
from twisted.internet import threads
MY_GMAIL = {your gmail address}
REFRESH_TOKEN_SECRET_FILE = {name of your refresh token file from Step 5}
CLIENT_SECRET_FILE = {name of your cliend json file from Step 1}
@implementer(imap4.IClientAuthentication)
class GmailOAuthAuthenticator():
authName = "XOAUTH2"
tokenTimeout = 3300 # 5 mins short of the real timeout (1 hour)
def __init__(self, reactr):
self.token = None
self.reactor = reactr
self.expire = None
@defer.inlineCallbacks
def getToken(self):
if ( (self.token==None) or (self.reactor.seconds() > self.expire) ):
rt = None
with open(REFRESH_TOKEN_SECRET_FILE) as f:
rt = json.load(f)
cl = None
with open(CLIENT_SECRET_FILE) as f:
cl = json.load(f)
self.token = yield threads.deferToThread(
oauth2.RefreshToken,
client_id = cl['installed']['client_id'],
client_secret = cl['installed']['client_secret'],
refresh_token = rt['Refresh Token'] )
self.expire = self.reactor.seconds() + self.tokenTimeout
def getName(self):
return self.authName
def challengeResponse(self, secret, chal):
# we MUST already have the token
# (allow an exception to be thrown if not)
t = self.token['access_token']
ret = oauth2.GenerateOAuth2String(MY_GMAIL, t, False)
return ret
Шаг 7 - Зарегистрируйте аутентификатор для протокола
В IMAP4ClientFactory:
def buildProtocol(self, addr):
p = self.protocol(self.ctx)
p.factory = self
x = GmailOAuthAuthenticator(self.reactor)
p.registerAuthenticator(x)
return p
Шаг 8 - Используйте токен доступа для аутентификации
Вместо использования "login", получите токен доступа (если необходимо), а затем используйте аутентификацию.
Изменив код примера из вопроса:
@defer.inlineCallbacks
def serverGreeting(self, caps):
# log in
try:
# the line below no longer works for gmail
# yield self.login(mailuser, mailpass)
if GmailOAuthAuthenticator.authName in self.authenticators:
yield self.authenticators[AriGmailOAuthAuthenticator.authName].getToken()
yield self.authenticate("")
try:
yield self.uponAuthentication()
except Exception as e:
uponFail(e, "uponAuthentication")
except Exception as e:
uponFail(e, "logging in")