-
Notifications
You must be signed in to change notification settings - Fork 219
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
Dynamic Provider #70
base: master
Are you sure you want to change the base?
Dynamic Provider #70
Conversation
Travis reports 1 test is failing on Python 2.6. |
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.
Thanks for this!
Could you look at my comments, and preferably look at adding some documentation for this?
# Backwards compatible: When provider argument is ommitted, | ||
# the value would be None, then in method load_secrets, the | ||
# client_secrets will be tried to load... | ||
with app.app_context(): |
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.
This would need to move to init_app
, since when we get here, the developer might not yet have provided an app
instance.
with app.app_context(): | ||
self.init_provider(provider) | ||
|
||
def __exit__(self, exception_type, exception_value, traceback): |
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.
What is the use of this? I don't think there's an __enter__
, so this class could not be used as a context manager anyway?
I also don't see a reason why this should support context manager calling methods?
# get stuff from the app's config, which may override stuff set above | ||
if app is not None: | ||
|
||
if 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.
Why did you change this line? The intention is that it'll be called with either a flask.Flask
instance or None
.
""" | ||
|
||
secrets = self.load_secrets(provider) | ||
assert secrets != None, "Problem with loading secrets" |
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.
Note that assert
statements get removed when a python optimized bytecode file is written, as happens in packaging on various linux distros.
Please turn this into an explicit check and raise ValueError
.
Do setup for a specific provider | ||
|
||
:param provider: The provider to initialize. | ||
:type provider: Dictionary with at lease 'base_url' item |
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.
s/lease/least/
def load_secrets(self, provider): | ||
|
||
try: | ||
static_secrets = current_app.config.get('OIDC_CLIENT_SECRETS', 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.
Maybe return an error if provider
is not None
in this case, to avoid confusing people?
|
||
registration = provider.get('registration', None) | ||
|
||
if not registration: |
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.
So, while I think dynamic discovery is a reasonable idea, fully automatic dynamic registration without any configuration (e.g. provider['dynamic_registration'] = True
) might be confusing to people.
Specifically because depending on the IdP in use, if people re-register every server start, their previously issued tokens might suddenly become unusable because they can no longer retrieve the token information.
Because of this, I think making the registration explicit would be a good idea.
if not provider: | ||
raise Exception("No Provider specified") | ||
|
||
try: |
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.
This function is kind of long.
Maybe add two helper functions def _perform_discovery()
and def _perform_dynamic_registration()
?
|
||
try: | ||
try: | ||
jwks_keys = json.load( |
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 that if we fail to load a jwks_uri, we should just error out entirely, and not even continue starting, since JWKS is required for the protocol.
except Exception as e: | ||
raise Exception("Error in preparing result: {}".format(str(e))) | ||
|
||
raise Exception("No secrets loaded !") |
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.
Is there any way for us to arrive at this line of code?
@HarryKodden If you want this resolved i'm maintaining a fork of this with some added extensibility. Feel free to open a PR here: |
Add ability to connect to an OIDC OP at Runtime. For backwards compatibility the existing config option OIDC_CLIENT_SECRETS is still supported.
Usage for dynamic OP connectivity is:
Initialize the Flask App as normal:
Specify the Provider to connect with:
Now finalize the configuration for this OIDC Provider (meaning the discovery will take place)
And use the authentication as normal: