pythonpython-3.xoauth-2.0oauthauthlib

Authlib error: No scheme supplied. Perhaps you meant https://None?


I have simple python code that auth users using github, most of it is copilot generated, but I have checked the docs several time that I am in the right direction

import dotenv
import os
from flask import Flask, redirect, url_for, session
from authlib.integrations.flask_client import OAuth

dotenv.load_dotenv()

client_id = os.getenv("CLIENT_ID")
client_secret = os.getenv("CLIENT_SECRET")

authorize_url = 'https://github.com/login/oauth/authorize'
request_token_url = 'https://github.com/login/oauth/access_token'
api_url = 'https://api.github.com'
base_url = 'https://ec2-3-29-136-253.me-central-1.compute.amazonaws.com/'

app = Flask(__name__)
app.secret_key = "thisissecret"
oauth = OAuth()
oauth.init_app(app)

oauth.register(
    name='github',
    client_id=client_id,
    client_secret=client_secret,
    authorize_url='https://github.com/login/oauth/authorize',
    authorize_params={'scope': 'read'},
    token_url='https://github.com/login/oauth/access_token',
    client_kwargs={'token_endpoint_auth_method': 'client_secret_basic'}
)
@app.route('/')
def home():
    if 'user' in session:
        return f'Hello, {session["user"]["login"]}! <a href="/logout">Logout</a>'
    return 'Welcome to the app! <a href="/login">Login with GitHub</a>'

@app.route('/login')
def login():
    redirect_uri = url_for('authorized', _external=True, _scheme='https')
    return oauth.github.authorize_redirect(redirect_uri)

@app.route('/authorized')
def authorized():
    token = oauth.github.authorize_access_token()
    user = oauth.github.parse_id_token(token)
    session['user'] = user
    return redirect('/')


@app.route('/logout')
def logout():
    session.pop('user', None)
    return redirect('/')

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=443,ssl_context='adhoc',debug=True)

This according to the docs should work fine, but I am getting a weird error

MYIP - - [19/Sep/2023 10:20:38] "GET / HTTP/1.1" 200 -
MYIP - - [19/Sep/2023 10:20:38] "GET /favicon.ico HTTP/1.1" 404 -
MYIP - - [19/Sep/2023 10:20:39] "GET /login HTTP/1.1" 302 -
MYIP - - [19/Sep/2023 10:20:40] "GET /authorized?code=685ead7c1d20c6564e33&state=HwsxzkLN69ehBYBaeboeD6hzCzgghZ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2213, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2193, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\Desktop\hackathon\f.py", line 43, in authorized
    token = oauth.github.authorize_access_token()
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\integrations\flask_client\apps.py", line 101, in authorize_access_token
    token = self.fetch_access_token(**params, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\integrations\base_client\sync_app.py", line 342, in fetch_access_token
    token = client.fetch_token(token_endpoint, **params)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\oauth2\client.py", line 207, in fetch_token        
    return self._fetch_token(
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\oauth2\client.py", line 351, in _fetch_token       
    resp = self.session.post(
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 637, in post
    return self.request("POST", url, data=data, json=json, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\integrations\requests_client\oauth2_session.py", line 109, in request
    return super(OAuth2Session, self).request(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 575, in request
    prep = self.prepare_request(req)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\sessions.py", line 486, in prepare_request        
    p.prepare(
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\models.py", line 368, in prepare
    self.prepare_url(url, params)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\models.py", line 439, in prepare_url
    raise MissingSchema(
requests.exceptions.MissingSchema: Invalid URL 'None': No scheme supplied. Perhaps you meant https://None?
MYIP - - [19/Sep/2023 10:20:41] "GET /authorized?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 304 -
MYIP - - [19/Sep/2023 10:20:41] "GET /authorized?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 -
MYIP - - [19/Sep/2023 10:20:41] "GET /authorized?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 304 -
94.142.38.133 - - [19/Sep/2023 10:20:44] "GET /authorized?code=685ead7c1d20c6564e33&state=HwsxzkLN69ehBYBaeboeD6hzCzgghZ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2213, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2193, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\Desktop\hackathon\f.py", line 43, in authorized
    token = oauth.github.authorize_access_token()
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\integrations\flask_client\apps.py", line 100, in authorize_access_token
    params = self._format_state_params(state_data, params)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python311\Lib\site-packages\authlib\integrations\base_client\sync_app.py", line 234, in _format_state_params
    raise MismatchingStateError()
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
94.142.38.133 - - [19/Sep/2023 10:20:45] "GET /authorized?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
94.142.38.133 - - [19/Sep/2023 10:20:46] "GET /authorized?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
94.142.38.133 - - [19/Sep/2023 10:20:48] "GET /authorized?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -

After debugging I found that the user is correctly being directed to github enter image description here the user is authorized and redirected back to the callback page, but the redirect_uri is not set in the callback request enter image description here

I tried googling the issue, asking chatgpt/copilot, and I tried to set the redirect_uri manually wherever I could but didn't succeed at all


Solution

  • The issue was because I had some missing config in oauth.registery(), the attribute access_token_url was not set, instead it was named token_url, Here is how the code looks before and after fixing it.

    Before:

    oauth.register(
        name='github',
        client_id=client_id,
        client_secret=client_secret,
        authorize_url='https://github.com/login/oauth/authorize',
        authorize_params={'scope': 'read'},
        token_url='https://github.com/login/oauth/access_token',
        client_kwargs={'token_endpoint_auth_method': 'client_secret_basic'}
    )
    

    After:

    oauth.register(
        name='github',
        client_id=client_id,
        client_secret=client_secret,
        authorize_url='https://github.com/login/oauth/authorize',
        authorize_params={'scope': 'read:user read:email'},
        access_token_url='https://github.com/login/oauth/access_token',
        client_kwargs={'token_endpoint_auth_method': 'client_secret_basic'},
        api_base_url='https://api.github.com/user',
    )
    

    Also in def authorized() this line is not needed as authlib auto calls it in authorize_access_token()

    user = oauth.github.parse_id_token(token)
    

    It is even wrong because parse_id_token() needs a nonce after the token, this was an old issue in the docs.

    And to access the user info of the user there is a method in the client for this user_data = oauth.github.get('user').json().