-
Notifications
You must be signed in to change notification settings - Fork 340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update to authlib related to issue #113 - slightly better PR #118
base: master
Are you sure you want to change the base?
Conversation
…enames. Tests are mostly failing due to the problems I was having with sessions, but also because there is no OAuth2Grant, and also Flask-login anonymous users seem to be handled differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some initial comments.
""" | ||
class MyOAuth2ClientMixin(OAuth2ClientMixin): | ||
def check_requested_scopes(self, scopes): | ||
if type(self.scope) == str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think, isinstance()
check is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah definitely - was in hurry and lazy.
@@ -45,6 +45,7 @@ def run( | |||
if upgrade_db: | |||
# After the installed dependencies the app.db.* tasks might need to be | |||
# reloaded to import all necessary dependencies. | |||
import sqlalchemy_utils |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. Probably not.
'x_arg': "Additional arguments consumed by custom env.py scripts", | ||
} | ||
) | ||
def droptables(context, directory='migrations', revision='head', sql=False, tag=None, x_arg=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that all the parameters and help strings are irrelevant to the task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, didn't understand the invoke system. Also db is not imported.
return app.run( host=host, port=port) | ||
|
||
if __name__ == "__main__": | ||
run_app() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is invoke app.run
for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is brought over from another project. I find the debugger works better without using invoke. But it was utility, not meant to be checked in.
@@ -1,26 +0,0 @@ | |||
"""empty message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this is not how we deal with migrations. Even though it is an example, migrations are an essential part of the "real life" example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I broke the migrations flow. I would really need to revert migrations, and redo that part.
if 'form' in locations: | ||
if 'access_token' in request.form: | ||
request.authorization = 'Bearer %s' % request.form['access_token'] | ||
# don't think we need below lines because bearer validator already registered |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that this trick will not work anymore anyway or you just don't see a use-case of passing the token via POST form parameters? The use-case is designed for native HTML forms, where we don't control the request from JS, but, instead, rely on a browser to do all the work, and thus, we cannot set the access token in the header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I didn't understand what it was doing and didn't see a similar thing i the authlib example, so I thought it was related to flask-oauthlib. I will add it back in.
kwargs['session_options']['autocommit'] = True | ||
# if 'session_options' not in kwargs: | ||
# kwargs['session_options'] = {} | ||
# kwargs['session_options']['autocommit'] = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to sort this out one way or another since it is quite a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I had a lot of issues with autocommit, namely with sqlalchemy complaining that a transaction already was started when with db.session.begin():
was called. In authlib, the complaints were about no transaction has started. In my own project I turned it off, because I had my own transaction system, and also I was using postgresql.
@@ -26,7 +20,6 @@ def init_app(app, **kwargs): | |||
Init auth module. | |||
""" | |||
# Bind Flask-Login for current_user | |||
login_manager.request_loader(load_user_from_request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given you drop this, you may as well drop the function. How does this work now, though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually did more work in my other project on this - I think I got login manager working as follows:
from flask import g
from flask.sessions import SecureCookieSessionInterface
from flask_login import user_loaded_from_header
from flask_login import LoginManager as OriginalLoginManager
class CustomSessionInterface(SecureCookieSessionInterface):
"""Prevent creating session from API requests."""
def save_session(self, *args, **kwargs):
if g.get('login_via_header'):
return
return super(CustomSessionInterface, self).save_session(*args,
**kwargs)
@user_loaded_from_header.connect
def user_loaded_from_header(self, user=None):
g.login_via_header = True
class LoginManager(OriginalLoginManager):
def init_app(self, app, add_context_processor=True):
app.session_interface = CustomSessionInterface()
super().init_app(app, add_context_processor=add_context_processor)
# in some other code
from app.extensions import login_manager
from authlib.flask.oauth2 import current_token
@login_manager.request_loader
def load_user_from_request(request):
"""
Load user from OAuth2 Authentication header.
"""
if current_token:
user = current_token.user
if user:
return user
user_id = current_token.user.id
if user_id:
return User.query.get(user_id)
return None
|
||
id = db.Column(db.Integer, primary_key=True) # pylint: disable=invalid-name | ||
|
||
user_id = db.Column(db.ForeignKey('user.id', ondelete='CASCADE'), index=True, nullable=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you drop the index=True
and nullable=False
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure - I was probably having problems upgrading, and was really trying to get the auth going to see if it would work, and probably meant to go back and fix.
class ClientTypes(str, enum.Enum): | ||
public = 'public' | ||
confidential = 'confidential' | ||
|
||
client_type = db.Column(db.Enum(ClientTypes), default=ClientTypes.public, nullable=False) | ||
redirect_uris = db.Column(ScalarListType(separator=' '), default=[], nullable=False) | ||
default_scopes = db.Column(ScalarListType(separator=' '), nullable=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just drop the default_scopes
; it seems like it is a simple rename.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, that's one thing I fixed in my other project. So authlib uses lists differently in the mixins. I'm not a huge fan, but it's convenient to use the mixins. The authlib mixins define scope as a Text and it's a list divided by newlines. Same with grant, and redirect_uri. Then it has @hybrid_property definitions for them that return redirect_uri.splitlines() as an example. I tried to use your @scalarlisttypes but that didn't work because some of the authlib methods assume a textfield with newline split list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a little more time now, so I will try to fix based on these comments, and integrate some of the fixes that I made in my own project.
No description provided.