Forums

google authorization error 400

I am new to using pythonanywhere and I am currently trying to setup google authentication for a flask app. I have tried several different tutorials and have gotten errors at the end of each. The most recent tutorial that I followed is resulting in the following error when I redirect to the login page for google auth:

Error 400: invalid_request Invalid parameter value for redirect_uri: Non-public domains not allowed: https://adminjp.pythonanywhere/google/auth

The code I am using comes from the authentication part of this tutorial: https://www.mattbutton.com/2019/01/05/google-authentication-with-python-and-flask/

Ive created a .env file and stored my environment variables in it and using dotenv in my WSGI file to access them. I know that the credentials are being accessed and all of my google settings are filled in.

Is there something I am missing here, or is a free pythonanywhere.com account not allowed to use the google oauth2 for authentication? From what I can tell, the issue may be coming from the use of adminjp.pythonanywhere as the uri because its not considered a top level url. ??? I really have no clue how to go about fixing this but I would really like to use google login for my free projects as I am still learning and would not benefit from paid accounts or domains at this point.

Thank you in advance for any help regarding this situation. Ive googled the topic extensively and the only solutions I find are ones for people who are using other coding languages or localhost. If I were a bit farther in my learning I am sure I could adapt one of these solutions, but I'm not.

It looks like you're passing Google https://adminjp.pythonanywhere/google/auth for your redirect_uri, when it should be https://adminjp.pythonanywhere.com/google/auth. Note the missing .com in your version.

Oh wow. Yeah. I did that. What a noob mistake. Now I know what to double check next time though. Now I have that working but there is a different problem with the code from that guy’s tutorial. Something about deprecated Authlib which isn’t really something that belongs in this post. Unless you’re willing to take a look at what’s happening with my current code, is there some up to date tutorial you would recommend that shows you how to setup google login with pythonanywhere free flask web app?

What's the exact error that you're getting? I don't know of a good tutorial for Google login, but might be able to tell you what to do next with a bit more information.

When I am not logged in the site displays as it shoud. I then visit adminjp.pythonanywhere/google/login where i am presented with a normal google login selection. When I choose an account the site redirects and I am sent to the oops something went wrong screen to check my error logs. If I navigate back to adminjp.pythonanywhere/google/logout the site returns to normal with my logged out message. I'm going to include as much info as possible here because I know that the exchange back and forth would probably suck for you trying to help me with bits and pieces. I'm including all of my code and the errors from the logs. I did not see anything in the server logs that looked abnormal or related. I really appreciate any help that you can give, hopefully it's something simple just like my original issue!

.env file:

export FN_AUTH_REDIRECT_URI=https://adminjp.pythonanywhere.com/google/auth
export FN_BASE_URI=https://adminjp.pythonanywhere.com
export FN_CLIENT_ID=6584*******************************2n.apps.googleusercontent.com
export FN_CLIENT_SECRET=lV*********************yw

export FLASK_APP=app.py
export FLASK_DEBUG=1
export FN_FLASK_SECRET_KEY=**********************

app.py:

import functools
import json
import os

import flask

from authlib.client import OAuth2Session
import google.oauth2.credentials
import googleapiclient.discovery

import google_auth

app = flask.Flask(__name__)
app.secret_key = os.environ.get("FN_FLASK_SECRET_KEY", default=False)

app.register_blueprint(google_auth.app)

@app.route('/')
def index():
    if google_auth.is_logged_in():
        user_info = google_auth.get_user_info()
        return '<div>You are currently logged in as ' + user_info['given_name'] + '<div><pre>' + json.dumps(user_info, indent=4) + "</pre>"

    return 'You are not currently logged in.'

google_auth.py:

import functools
import os

import flask

from authlib.client import OAuth2Session
import google.oauth2.credentials
import googleapiclient.discovery

ACCESS_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
AUTHORIZATION_URL = 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent'

AUTHORIZATION_SCOPE ='openid email profile'

AUTH_REDIRECT_URI = os.environ.get("FN_AUTH_REDIRECT_URI", default=False)
BASE_URI = os.environ.get("FN_BASE_URI", default=False)
CLIENT_ID = os.environ.get("FN_CLIENT_ID", default=False)
CLIENT_SECRET = os.environ.get("FN_CLIENT_SECRET", default=False)

AUTH_TOKEN_KEY = 'auth_token'
AUTH_STATE_KEY = 'auth_state'

app = flask.Blueprint('google_auth', __name__)

def is_logged_in():
    return True if AUTH_TOKEN_KEY in flask.session else False

def build_credentials():
    if not is_logged_in():
        raise Exception('User must be logged in')

    oauth2_tokens = flask.session[AUTH_TOKEN_KEY]

    return google.oauth2.credentials.Credentials(
                oauth2_tokens['access_token'],
                refresh_token=oauth2_tokens['refresh_token'],
                client_id=CLIENT_ID,
                client_secret=CLIENT_SECRET,
                token_uri=ACCESS_TOKEN_URI)

def get_user_info():
    credentials = build_credentials()

    oauth2_client = googleapiclient.discovery.build('oauth2', 'v2', credentials=credentials)

    return oauth2_client.userinfo().get().execute()

def no_cache(view):
    @functools.wraps(view)
    def no_cache_impl(*args, **kwargs):
        response = flask.make_response(view(*args, **kwargs))
        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
        response.headers['Pragma'] = 'no-cache'
        response.headers['Expires'] = '-1'
        return response

    return functools.update_wrapper(no_cache_impl, view)

@app.route('/google/login')
@no_cache
def login():
    session = OAuth2Session(CLIENT_ID, CLIENT_SECRET, scope=AUTHORIZATION_SCOPE, redirect_uri=AUTH_REDIRECT_URI)

    uri, state = session.authorization_url(AUTHORIZATION_URL)

    flask.session[AUTH_STATE_KEY] = state
    flask.session.permanent = True

    return flask.redirect(uri, code=302)

@app.route('/google/auth')
@no_cache
def google_auth_redirect():
    req_state = flask.request.args.get('state', default=None, type=None)

    if req_state != flask.session[AUTH_STATE_KEY]:
        response = flask.make_response('Invalid state parameter', 401)
        return response

    session = OAuth2Session(CLIENT_ID, CLIENT_SECRET,
                            scope=AUTHORIZATION_SCOPE,
                            state=flask.session[AUTH_STATE_KEY],
                            redirect_uri=AUTH_REDIRECT_URI)

    oauth2_tokens = session.fetch_access_token(
                        ACCESS_TOKEN_URI,
                        authorization_response=flask.request.url)

    flask.session[AUTH_TOKEN_KEY] = oauth2_tokens

    return flask.redirect(BASE_URI, code=302)

@app.route('/google/logout')
@no_cache
def logout():
    flask.session.pop(AUTH_TOKEN_KEY, None)
    flask.session.pop(AUTH_STATE_KEY, None)

    return flask.redirect(BASE_URI, code=302)

wsgi:

# This file contains the WSGI configuration required to serve up your
# web application at http://<your-username>.pythonanywhere.com/
# It works by setting the variable 'application' to a WSGI handler of some
# description.
#
# The below has been auto-generated for your Flask project

import sys

import os
from dotenv import load_dotenv
project_folder = os.path.expanduser('~/mysite/')  # adjust as appropriate
load_dotenv(os.path.join(project_folder, '.env'))

# add your project directory to the sys.path
project_home = '/home/adminjp/mysite'
if project_home not in sys.path:
    sys.path = [project_home] + sys.path

# import flask app but need to call it "application" for WSGI to work
from app import app as application  # noqa

requirements.txt:

authlib==0.11
flask==1.0.2
google-api-python-client
google-auth
virtualenv
python-dotenv

At first, when I use the code that is provided in the tutorial.. I get the following errors:

2020-04-19 17:56:07,195: /home/adminjp/.local/lib/python3.8/site-packages/authlib/client/oauth2_session.py:97: AuthlibDeprecationWarning: Use "create_authorization_url" instead
2020-04-19 17:56:07,195: It will be compatible before version 0.12.
2020-04-19 17:56:07,195:   deprecate('Use "create_authorization_url" instead', '0.12')
2020-04-19 17:56:56,605: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/googleapiclient/discovery_cache/__init__.py", line 36, in autodetect
    from google.appengine.api import memcache
ModuleNotFoundError: No module named 'google.appengine'
**NO MATCH**
During handling of the above exception, another exception occurred:
**NO MATCH**
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 33, in <module>
    from oauth2client.contrib.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client.contrib.locked_file'
**NO MATCH**
During handling of the above exception, another exception occurred:
**NO MATCH**
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 37, in <module>
    from oauth2client.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client.locked_file'
**NO MATCH**
During handling of the above exception, another exception occurred:
**NO MATCH**
Traceback (most recent call last):
  File "/usr/lib/python3.8/site-packages/googleapiclient/discovery_cache/__init__.py", line 41, in autodetect
    from . import file_cache
  File "/usr/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 40, in <module>
    raise ImportError(
ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
2020-04-19 17:56:56,607: URL being requested: GET https://www.googleapis.com/discovery/v1/apis/oauth2/v2/rest
2020-04-19 17:56:56,616: Error running WSGI application
2020-04-19 17:56:56,622: TypeError: argument should be integer or bytes-like object, not 'str'
2020-04-19 17:56:56,622:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2309, in __call__
2020-04-19 17:56:56,622:     return self.wsgi_app(environ, start_response)
2020-04-19 17:56:56,622: 
2020-04-19 17:56:56,622:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2295, in wsgi_app
2020-04-19 17:56:56,622:     response = self.handle_exception(e)
2020-04-19 17:56:56,622: 
2020-04-19 17:56:56,622:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1741, in handle_exception
2020-04-19 17:56:56,622:     reraise(exc_type, exc_value, tb)
2020-04-19 17:56:56,623: 
2020-04-19 17:56:56,623:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/_compat.py", line 35, in reraise
2020-04-19 17:56:56,623:     raise value
2020-04-19 17:56:56,623: 
2020-04-19 17:56:56,623:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2292, in wsgi_app
2020-04-19 17:56:56,623:     response = self.full_dispatch_request()
2020-04-19 17:56:56,623: 
2020-04-19 17:56:56,623:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1815, in full_dispatch_request
2020-04-19 17:56:56,623:     rv = self.handle_user_exception(e)
2020-04-19 17:56:56,623: 
2020-04-19 17:56:56,623:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1718, in handle_user_exception
2020-04-19 17:56:56,624:     reraise(exc_type, exc_value, tb)
2020-04-19 17:56:56,624: 
2020-04-19 17:56:56,624:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/_compat.py", line 35, in reraise
2020-04-19 17:56:56,624:     raise value
2020-04-19 17:56:56,624: 
2020-04-19 17:56:56,624:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1813, in full_dispatch_request
2020-04-19 17:56:56,624:     rv = self.dispatch_request()
2020-04-19 17:56:56,624: 
2020-04-19 17:56:56,624:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1799, in dispatch_request
2020-04-19 17:56:56,624:     return self.view_functions[rule.endpoint](**req.view_args)
2020-04-19 17:56:56,625: 
2020-04-19 17:56:56,625:   File "/home/adminjp/mysite/app.py", line 21, in index
2020-04-19 17:56:56,625:     user_info = google_auth.get_user_info()
2020-04-19 17:56:56,625: 
2020-04-19 17:56:56,625:   File "/home/adminjp/mysite/google_auth.py", line 44, in get_user_info
2020-04-19 17:56:56,625:     oauth2_client = googleapiclient.discovery.build('oauth2', 'v2', credentials=credentials)
2020-04-19 17:56:56,625: 
2020-04-19 17:56:56,625:   File "/usr/lib/python3.8/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
2020-04-19 17:56:56,625:     return wrapped(*args, **kwargs)
2020-04-19 17:56:56,625: 
2020-04-19 17:56:56,625:   File "/usr/lib/python3.8/site-packages/googleapiclient/discovery.py", line 222, in build
2020-04-19 17:56:56,625:     content = _retrieve_discovery_doc(
2020-04-19 17:56:56,626: 
2020-04-19 17:56:56,626:   File "/usr/lib/python3.8/site-packages/googleapiclient/discovery.py", line 273, in _retrieve_discovery_doc
2020-04-19 17:56:56,626:     resp, content = http.request(actual_url)
2020-04-19 17:56:56,626: 
2020-04-19 17:56:56,626:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1948, in request
2020-04-19 17:56:56,626:     (response, content) = self._request(
2020-04-19 17:56:56,626: 
2020-04-19 17:56:56,626:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1621, in _request
2020-04-19 17:56:56,626:     (response, content) = self._conn_request(
2020-04-19 17:56:56,626: 
2020-04-19 17:56:56,626:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1528, in _conn_request
2020-04-19 17:56:56,626:     conn.connect()
2020-04-19 17:56:56,627: 
2020-04-19 17:56:56,627:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1309, in connect
2020-04-19 17:56:56,627:     sock.connect((self.host, self.port))
2020-04-19 17:56:56,627: 
2020-04-19 17:56:56,627:   File "/usr/lib/python3.8/site-packages/socks.py", line 406, in connect
2020-04-19 17:56:56,627:     self.__negotiatehttp(destpair[0],destpair[1])
2020-04-19 17:56:56,627: 
2020-04-19 17:56:56,627:   File "/usr/lib/python3.8/site-packages/socks.py", line 357, in __negotiatehttp
2020-04-19 17:56:56,627:     while resp.find("\r\n\r\n")==-1:
2020-04-19 17:57:17,342: /home/adminjp/.local/lib/python3.8/site-packages/authlib/client/oauth2_session.py:97: AuthlibDeprecationWarning: Use "create_authorization_url" instead
2020-04-19 17:57:17,342: It will be compatible before version 0.12.
2020-04-19 17:57:17,342:   deprecate('Use "create_authorization_url" instead', '0.12')

I then change the following lines of code in google_auth.py to eliminate two issues (i think):

def get_user_info():
    credentials = build_credentials()

    oauth2_client = googleapiclient.discovery.build('oauth2', 'v2', credentials=credentials)

    return oauth2_client.userinfo().get().execute()

to:

def get_user_info():
    credentials = build_credentials()

    oauth2_client = googleapiclient.discovery.build('oauth2', 'v2', credentials=credentials, cache_discovery=False)

    return oauth2_client.userinfo().get().execute()

and:

@app.route('/google/login')
@no_cache
def login():
    session = OAuth2Session(CLIENT_ID, CLIENT_SECRET, scope=AUTHORIZATION_SCOPE, redirect_uri=AUTH_REDIRECT_URI)

    uri, state = session.authorization_url(AUTHORIZATION_URL)

    flask.session[AUTH_STATE_KEY] = state
    flask.session.permanent = True

    return flask.redirect(uri, code=302)

to:

@app.route('/google/login')
@no_cache
def login():
    session = OAuth2Session(CLIENT_ID, CLIENT_SECRET, scope=AUTHORIZATION_SCOPE, redirect_uri=AUTH_REDIRECT_URI)

    uri, state = session.create_authorization_url(AUTHORIZATION_URL)

    flask.session[AUTH_STATE_KEY] = state
    flask.session.permanent = True

    return flask.redirect(uri, code=302)

which seems to take away two errors but I am still presented with more errors:

2020-04-19 18:40:24,367: URL being requested: GET https://www.googleapis.com/discovery/v1/apis/oauth2/v2/rest
2020-04-19 18:40:24,375: Error running WSGI application
2020-04-19 18:40:24,394: TypeError: argument should be integer or bytes-like object, not 'str'
2020-04-19 18:40:24,394:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2309, in __call__
2020-04-19 18:40:24,394:     return self.wsgi_app(environ, start_response)
2020-04-19 18:40:24,394: 
2020-04-19 18:40:24,394:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2295, in wsgi_app
2020-04-19 18:40:24,395:     response = self.handle_exception(e)
2020-04-19 18:40:24,395: 
2020-04-19 18:40:24,395:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1741, in handle_exception
2020-04-19 18:40:24,395:     reraise(exc_type, exc_value, tb)
2020-04-19 18:40:24,395: 
2020-04-19 18:40:24,395:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/_compat.py", line 35, in reraise
2020-04-19 18:40:24,395:     raise value
2020-04-19 18:40:24,395: 
2020-04-19 18:40:24,395:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 2292, in wsgi_app
2020-04-19 18:40:24,396:     response = self.full_dispatch_request()
2020-04-19 18:40:24,396: 
2020-04-19 18:40:24,396:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1815, in full_dispatch_request
2020-04-19 18:40:24,396:     rv = self.handle_user_exception(e)
2020-04-19 18:40:24,396: 
2020-04-19 18:40:24,396:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1718, in handle_user_exception
2020-04-19 18:40:24,396:     reraise(exc_type, exc_value, tb)
2020-04-19 18:40:24,396: 
2020-04-19 18:40:24,396:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/_compat.py", line 35, in reraise
2020-04-19 18:40:24,396:     raise value
2020-04-19 18:40:24,397: 
2020-04-19 18:40:24,397:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1813, in full_dispatch_request
2020-04-19 18:40:24,397:     rv = self.dispatch_request()
2020-04-19 18:40:24,397: 
2020-04-19 18:40:24,397:   File "/home/adminjp/.local/lib/python3.8/site-packages/flask/app.py", line 1799, in dispatch_request
2020-04-19 18:40:24,397:     return self.view_functions[rule.endpoint](**req.view_args)
2020-04-19 18:40:24,397: 
2020-04-19 18:40:24,397:   File "/home/adminjp/mysite/app.py", line 21, in index
2020-04-19 18:40:24,397:     user_info = google_auth.get_user_info()
2020-04-19 18:40:24,397: 
2020-04-19 18:40:24,397:   File "/home/adminjp/mysite/google_auth.py", line 44, in get_user_info
2020-04-19 18:40:24,398:     oauth2_client = googleapiclient.discovery.build('oauth2', 'v2', credentials=credentials, cache_discovery=False)
2020-04-19 18:40:24,398: 
2020-04-19 18:40:24,398:   File "/usr/lib/python3.8/site-packages/googleapiclient/_helpers.py", line 130, in positional_wrapper
2020-04-19 18:40:24,398:     return wrapped(*args, **kwargs)
2020-04-19 18:40:24,398: 
2020-04-19 18:40:24,398:   File "/usr/lib/python3.8/site-packages/googleapiclient/discovery.py", line 222, in build
2020-04-19 18:40:24,398:     content = _retrieve_discovery_doc(
2020-04-19 18:40:24,398: 
2020-04-19 18:40:24,398:   File "/usr/lib/python3.8/site-packages/googleapiclient/discovery.py", line 273, in _retrieve_discovery_doc
2020-04-19 18:40:24,398:     resp, content = http.request(actual_url)
2020-04-19 18:40:24,399: 
2020-04-19 18:40:24,399:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1948, in request
2020-04-19 18:40:24,399:     (response, content) = self._request(
2020-04-19 18:40:24,399: 
2020-04-19 18:40:24,399:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1621, in _request
2020-04-19 18:40:24,399:     (response, content) = self._conn_request(
2020-04-19 18:40:24,399: 
2020-04-19 18:40:24,399:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1528, in _conn_request
2020-04-19 18:40:24,399:     conn.connect()
2020-04-19 18:40:24,399: 
2020-04-19 18:40:24,399:   File "/usr/lib/python3.8/site-packages/httplib2/__init__.py", line 1309, in connect
2020-04-19 18:40:24,399:     sock.connect((self.host, self.port))
2020-04-19 18:40:24,400: 
2020-04-19 18:40:24,400:   File "/usr/lib/python3.8/site-packages/socks.py", line 406, in connect
2020-04-19 18:40:24,400:     self.__negotiatehttp(destpair[0],destpair[1])
2020-04-19 18:40:24,400: 
2020-04-19 18:40:24,400:   File "/usr/lib/python3.8/site-packages/socks.py", line 357, in __negotiatehttp
2020-04-19 18:40:24,400:     while resp.find("\r\n\r\n")==-1:

Admittedly, I do not fully understand ALL of the code in this google_auth.py file, so I am not sure if my changes are causing a conflict with another piece of code or what. That's all I have.

OK, I tried scratching the whole thing and going with the complete example from google docs for the python server side app. Once everything was setup the way I wanted, I ended up with the same final results. After plenty of frustration I found the solution.

pip3.8 install --user --force-reinstall PySocks

After updating PySocks both versions of the code now authenticate and work perfectly. Thanks for all the help. Idk if that info will help anyone in the future but it seems to be an issue for many google api users on pythonanywhere.

ah great! thanks for letting us know! I'm sure it will help someone somewhere down the road :)

I had a similiar issue but my mistake was in the google API for developers i used HTTP instead of HTTPS might help someone tracking down the problem like i was

Glad to hear that you made it work!