firebase-rest-api icon indicating copy to clipboard operation
firebase-rest-api copied to clipboard

[Bug]: Invalid URL supplied to fauth.sign_in_with_oauth_credential

Open Lxstr opened this issue 1 year ago • 2 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Environment

-   OS: Docker Alpine 3.19
-   Python: python3.12

What happened?

I have this bug occur randomly, maybe 20% of the time. Not sure if it could be a requests issue. This also occurs in the live deployment.

http://127.0.0.1:5000/oauth2callback/?state=XXXXXXX&code=XXXXXX&scope=email+openid+https://www.googleapis.com/auth/userinfo.email&authuser=0&hd=XXXXXX.tech&prompt=consent

Code Snippet

def signin_oauth(url):
    user = fauth.sign_in_with_oauth_credential(url)

@auth_bp.route("/oauth2callback/")
def oauth2callback():
    current_app.logger.info(f"Request URL: {request.url}")
    current_app.logger.info("oauth2callback")
    try:
        # Bug also occurs if I use the commented out code below
        # base = BaseConfig.URL if BaseConfig.URL else ""
        # url = urljoin(base, request.url.lstrip("/"))
        # url = f"{BaseConfig.URL.rstrip('/')}" + request.full_path
        auth.signin_oauth(request.url)
    except Exception as e:
        raise

Relevant log output

File "/app/app/auth.py", line 155, in signin_oauth
flaskapp-1  |     user = fauth.sign_in_with_oauth_credential(url)
flaskapp-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/firebase/auth/__init__.py", line 468, in sign_in_with_oauth_credential
flaskapp-1  |     token = self._token_from_auth_url(oauth2callback_url)
flaskapp-1  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/firebase/auth/__init__.py", line 516, in _token_from_auth_url
flaskapp-1  |     request_object = self.requests.post(request_ref, headers=headers, data=data)
flaskapp-1  |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/requests/sessions.py", line 637, in post
flaskapp-1  |     return self.request("POST", url, data=data, json=json, **kwargs)
flaskapp-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/requests/sessions.py", line 575, in request
flaskapp-1  |     prep = self.prepare_request(req)
flaskapp-1  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/requests/sessions.py", line 486, in prepare_request
flaskapp-1  |     p.prepare(
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/requests/models.py", line 368, in prepare
flaskapp-1  |     self.prepare_url(url, params)
flaskapp-1  |   File "/usr/local/lib/python3.12/site-packages/requests/models.py", line 439, in prepare_url
flaskapp-1  |     raise MissingSchema(
flaskapp-1  | requests.exceptions.MissingSchema: Invalid URL 'None': No scheme supplied. Perhaps you meant https://None?

Anything else?

No response

Lxstr avatar Mar 31 '24 06:03 Lxstr

I suspect the bug is in _token_host(self.provider_id), somehow the self.provider_id is being unset or maybe not set as I am seeing it as None when the bug occurs.

	def _token_from_auth_url(self, url):
		""" Fetch tokens using the authorization code from given URL.
		:type url: str
		:param url: The URL redirected to after successful
			authorization from the provider.
		:return: The OAuth credential (an ID token).
		:rtype: dict
		"""

		request_ref = _token_host(self.provider_id)

It seems the only place that it is set is at the end of create_authentication_uri(self, provider_id). Perhaps this isn't thread or multi-process safe?

Perhaps it should also be set in _token_from_auth_url and it's parent function sign_in_with_oauth_credential.

I do not know the implication of this for those who use both facebook and google login, maybe they can just use different redirect url stem?

Lxstr avatar Mar 31 '24 07:03 Lxstr

It looks like in this repo the code_verifier and nonce also have to be 'remembered' between creating the auth uri and getting the callback? I couldn't find reference to how these are used?

When I check the reference at https://developers.google.com/identity/protocols/oauth2/web-server#example they use google-auth-oauthlib they use google-auth-oauthlib which in turn uses requests-oauthlib for some things such as handling the url in fetch_token.

The example seems to store state in the flask session between requests. In google-auth-oauthlib: "The state is used when completing the flow to verify that the request originated from your application."

On the other hand in requests-oauthlib they say state is to prevent CSRF, not sure if those are actually fully the same thing.

The REST example beside that one does not store the state between requests.

I am curious if you knew about google-auth-oauthlib? I wasn't aware. In the example I linked they recommend using their api package (rather than REST) I wonder if there are any benefits that outweigh the increased dependencies.

Lxstr avatar Mar 31 '24 10:03 Lxstr