1

We are testing with Firebase Authentication, and we're checking out python SDK. Attempting a simple get user operation from our Firebase users results in the following error:

TransportError: HTTPSConnectionPool(host='accounts.google.com', port=443): Max retries exceeded with url: /o/oauth2/token (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.",))

We tested from local server then from a staging GAE URL, as our initial thoughts were that maybe communicating with Firebase requires that the request comes from an Https URL. But, in both cases, we saw the same error. It's probably something else then.

Code used extract:

import firebase_admin
from firebase_admin import auth
from firebase_admin import credentials

cred = credentials.Certificate("custom/conf/conf.json")
default_app = firebase_admin.initialize_app(cred)

user_uid = '901234753'
user = auth.get_user(user_uid)
print 'Successfully fetched user data: {0}'.format(user.uid)

Any clue what may have caused this error and how to resolve it?


Update 1:

Adding SSL in app.yaml as suggested by @Mihail Russu in his comment

- name: ssl
version: latest

generated a new error: ImportError: cannot import name RAND_egd

in line:

from _ssl import RAND_add, RAND_egd, RAND_status, SSL_ERROR_ZERO_RETURN, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,,SSL_ERROR_INVALID_ERROR_CODE

As per the page "ImportError: cannot import name RAND_egd " and the page "Fix RAND_egd import error in SDK..." I edited socket.py located at /Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine (on Mac) to match their suggestions:

from _ssl import \
    RAND_add, \
    RAND_status, \
    SSL_ERROR_ZERO_RETURN, \
    SSL_ERROR_WANT_READ, \
    SSL_ERROR_WANT_WRITE, \
    SSL_ERROR_WANT_X509_LOOKUP, \
    SSL_ERROR_SYSCALL, \
    SSL_ERROR_SSL, \
    SSL_ERROR_WANT_CONNECT, \
    SSL_ERROR_EOF, \
    SSL_ERROR_INVALID_ERROR_CODE
try:
    from _ssl import RAND_egd
except ImportError:
    # LibreSSL does not provide RAND_egd
    pass

Update 1 result:

New errors generated:

-> local dev server: ('Connection aborted.', error(13, 'Permission denied'))

-> GAE: ('Connection broken: IncompleteRead(100 bytes read)', IncompleteRead(100 bytes read))

Anyone had a successful implementation of Firebase Auth with GAE? This feels a bit like going down the rabbit hole, thought using the SDK would be as easy as the javascript version of firebase auth..

Khaled
  • 907
  • 1
  • 8
  • 18

2 Answers2

2

If you are using AppEngine Standard Environment, make sure you have the following in your app.yaml:

libraries:
- name: ssl
  version: latest

App Engine supports the native Python SSL library for the Python 2.7 runtime via the SSL library, which you must add to your app.

See more at https://cloud.google.com/appengine/docs/standard/python/sockets/ssl_support

Mihail Russu
  • 2,526
  • 1
  • 17
  • 27
  • I read through the link provided, but I am not sure why SSL library is required if GAE already supports HTTPS and certificates can be issued for example via LetsEncrypt. Moreover, adding `- name: ssl version: latest` introduces a new error: `from _ssl import RAND_add, RAND_egd, RAND_status, SSL_ERROR_ZERO_RETURN, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE,...,SSL_ERROR_INVALID_ERROR_CODE` `ImportError: cannot import name RAND_egd` Would you happen to know why is all this required in the first place? is there a way around this? Other APIs we deal with don't need any of this! – Khaled Mar 25 '18 at 14:32
  • 1
    `I am not sure why SSL library is required if GAE already supports HTTPS and certificates can be issued for example via LetsEncrypt` - AppEngine's frontend takes care of https certificates, not your app. For your app to be able to create & handle outgoing https connections an ssl library is required, most (all?) of which are written C which means you cannot simply include them with your app (as only pure python modules are allowed) so Google came up with a small list of whitelisted C libraries which can be included via `app.yaml`'s `libraries` section. – Mihail Russu Mar 26 '18 at 10:01
0

Update 2 & resolution

Following on the errors resulted from update 1 found:

  1. The page TransportError when running within App Engine which mentions a workaround for the same error ('Connection aborted.', error(13, 'Permission denied')).
  2. And this stack overflow answer for the error ('Connection broken: IncompleteRead(80 bytes read)', IncompleteRead(80 bytes read)) which also appeared later.

The answer from Mihail and the above pages had finally made it possible to use firebase auth to fetch user form Firebase Auth Users

For reference, the final code snippets would be:

app.yaml :

- name: ssl
  version: latest

appengine_config.py :

# appengine_config.py
import os
import google
import imp
import inspect

from google.appengine.ext import vendor

# Add any libraries install in the "lib" folder.
vendor.add('lib')

# whitelists the socket and ssl libraries for the dev app server, and uses the system-level socket library instead of the one packaged with the App Engine SDK. Your mileage may vary.    
if os.environ.get('SERVER_SOFTWARE', '').startswith('Development'):
    from google.appengine.tools.devappserver2.python import sandbox
    sandbox._WHITE_LIST_C_MODULES += ['_ssl', '_socket']

    runtime_path = os.path.realpath(inspect.getsourcefile(inspect))
    runtime_dir = os.path.dirname(runtime_path)

    # Patch and reload the socket module implementation.
    system_socket = os.path.join(runtime_dir, 'socket.py')
    imp.load_source('socket', system_socket)

    # Patch and reload the ssl module implementation.
    system_ssl = os.path.join(runtime_dir, 'ssl.py')
    imp.load_source('ssl', system_ssl)

socket.py :

from _ssl import \
    RAND_add, \
    RAND_status, \
    SSL_ERROR_ZERO_RETURN, \
    SSL_ERROR_WANT_READ, \
    SSL_ERROR_WANT_WRITE, \
    SSL_ERROR_WANT_X509_LOOKUP, \
    SSL_ERROR_SYSCALL, \
    SSL_ERROR_SSL, \
    SSL_ERROR_WANT_CONNECT, \
    SSL_ERROR_EOF, \
    SSL_ERROR_INVALID_ERROR_CODE
try:
    from _ssl import RAND_egd
except ImportError:
    # LibreSSL does not provide RAND_egd
    pass

code :

# firebase
import firebase_admin
from firebase_admin import auth
from firebase_admin import credentials

# handle requests for GAE
import requests
import requests_toolbelt.adapters.appengine

requests_toolbelt.adapters.appengine.monkeypatch()

# initialize firebase
cred = credentials.Certificate("custom/conf/conf.json")
default_app = firebase_admin.initialize_app(cred)

user_uid = '901234753'
user = auth.get_user(user_uid)
print 'Successfully fetched user data: {0}'.format(user.uid)

I did get a couple of warning though that I need to check out later:

AppEnginePlatformWarning: urllib3 is using URLFetch on Google App 
Engine sandbox instead of sockets. To use sockets directly instead of 
URLFetch see https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.AppEnginePlatformWarning)

WARNING  2018-03-26 00:33:31,894 urlfetch_stub.py:534] Stripped prohibited headers from URLFetch request: ['Content-Length']
WARNING  2018-03-26 00:33:33,079 urlfetch_stub.py:534] Stripped prohibited headers from URLFetch request: ['Content-Length']
Khaled
  • 907
  • 1
  • 8
  • 18