4

I am writing a script that uses Google's authenticated login. I am currently getting an access token and the user's email address and passing it to my function that connects to gmail using imap and then does some stuff with the emails. I'm generating the auth string like i've seen others do online however I am receiving this error:

Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/flask/app.py", line 2000, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1991, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1567, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/Harrison/Desktop/Uber/UberStats.py", line 60, in index
    return Uber_Cost(email_address, access_token)
  File "/Users/Harrison/Desktop/Uber/UberStats.py", line 103, in Uber_Cost
    mail.authenticate('XOAUTH2', lambda x: auth_string)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/imaplib.py", line 364, in authenticate
    raise self.error(dat[-1])
error: [AUTHENTICATIONFAILED] Invalid credentials (Failure)

I am printing out the access code as well as the email address that i'm logging in with, so I know those values aren't null. Am I generating the auth string wrong? Am I not authenticating with imap properly?

Here's my code:

from flask import Flask, request, url_for, session, redirect, jsonify
from flask_oauth import OAuth
import json
import imaplib
import email
from bs4 import BeautifulSoup
import base64





GOOGLE_CLIENT_ID = '****'
GOOGLE_CLIENT_SECRET = '***'
REDIRECT_URI = '/authorized'  # one of the Redirect URIs from Google APIs console

SECRET_KEY = 'Uber'
DEBUG = True

app = Flask(__name__)
app.secret_key = 'Uber'
oauth = OAuth()

google = oauth.remote_app('google',
                          base_url='https://www.google.com/accounts/',
                          authorize_url='https://accounts.google.com/o/oauth2/auth',
                          request_token_url=None,
                          request_token_params={'scope': 'https://www.googleapis.com/auth/userinfo.email',
                                                'response_type': 'code'},
                          access_token_url='https://accounts.google.com/o/oauth2/token',
                          access_token_method='POST',
                          access_token_params={'grant_type': 'authorization_code'},
                          consumer_key=GOOGLE_CLIENT_ID,
                          consumer_secret=GOOGLE_CLIENT_SECRET)


@app.route('/')
def index():
    access_token = session.get('access_token')
    if access_token is None:
        return redirect(url_for('login'))

    access_token = access_token[0]
    from urllib2 import Request, urlopen, URLError

    headers = {'Authorization': 'OAuth '+access_token}
    req = Request('https://www.googleapis.com/oauth2/v1/userinfo',
                  None, headers)
    try:
        res = urlopen(req)
    except URLError, e:
        if e.code == 401:
            # Unauthorized - bad token
            session.pop('access_token', None)
            return redirect(url_for('login'))
        return res.read()
    j = json.loads(res.read())
    email_address = j['email']
    print email_address, access_token
    return Uber_Cost(email_address, access_token)


@app.route('/login')
def login():
    callback=url_for('authorized', _external=True)
    return google.authorize(callback=callback)



@app.route(REDIRECT_URI)
@google.authorized_handler
def authorized(resp):
    access_token = resp['access_token']
    session['access_token'] = access_token, ''
    return redirect(url_for('index'))


@google.tokengetter
def get_access_token():
    return session.get('access_token')


def GenerateOAuth2String(username, access_token, base64_encode=True):
    auth_string = 'user=%s\1auth=Bearer %s\1\1' % (username, access_token)
    if base64_encode:
        auth_string = base64.b64encode(auth_string)
    return auth_string




def Uber_Cost(email_address, access_token):


    auth_string = GenerateOAuth2String(email_address, access_token, base64_encode=False)



    mail = imaplib.IMAP4_SSL('imap.gmail.com')
    mail.debug = 4
    mail.authenticate('XOAUTH2', lambda x: auth_string)
    mail.select('INBOX')
Harrison
  • 5,095
  • 7
  • 40
  • 60

1 Answers1

0

It looks like you've got the authenticate() method down based on your latest code.

You also need the https://mail.google.com/ OAuth scope to authenticate with the IMAP and SMTP servers.

That is add it to your scope request, and make sure your app is configured for this scope on the Google App Console:

request_token_params={'scope': 'https://www.googleapis.com/auth/userinfo.email https://mail.google.com/',
                                            'response_type': 'code'},

The Google OAUTH2 protocol and scopes are documented at their developer page.

Max
  • 10,701
  • 2
  • 24
  • 48
  • The code is updated as to what I currently have. Where would that go? In the request token parameters? – Harrison Nov 17 '16 at 22:03
  • Yes, I believe scopes are space separated. I'm not sure, but you may also need to go to your app console and add it to your project. – Max Nov 17 '16 at 22:09
  • BTW, if you change scopes, you will probably need to flush any current tokens you have. – Max Nov 17 '16 at 22:17
  • How would I flush the tokens? And would adding gmail to the scope be the solution to authentication failed? – Harrison Nov 17 '16 at 22:55
  • If the auth module is caching them somewhere, delete the cache. And yes, scopes are permissions. If you don't have permission to reach their IMAP server, it's going to give you an authentication error. – Max Nov 18 '16 at 14:18
  • what would the syntax look like when I add gmail to the scope? – Harrison Nov 18 '16 at 14:19
  • That worked! Thank you so much! Would you happen to know how I could use my own custom domain name that I purchased rather than using localhost? – Harrison Nov 18 '16 at 19:43
  • Use localhost for /what/? This seems like a separate question. – Max Nov 18 '16 at 19:51
  • I'll post a new question. – Harrison Nov 18 '16 at 20:05
  • 1
    I've awarded you the bounty, thanks. Here is the other question I posted http://stackoverflow.com/questions/40685275/use-custom-domain-for-flask-app-with-google-authenticated-login – Harrison Nov 19 '16 at 16:04