Skip to content

Dynamic Provider - #70

Open
HarryKodden wants to merge 7 commits into
puiterwijk:masterfrom
HarryKodden:dynamic_op
Open

Dynamic Provider#70
HarryKodden wants to merge 7 commits into
puiterwijk:masterfrom
HarryKodden:dynamic_op

Conversation

@HarryKodden

Copy link
Copy Markdown

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:

app = Flask(__name__)
oidc =  OpenIDConnect(app)

Specify the Provider to connect with:

my_provider = { 
   'base_url': 'https://example.org' , 
   'registration': { 'client_id': 'xxx', 'client_secret': 'yyy' }
} 

Now finalize the configuration for this OIDC Provider (meaning the discovery will take place)

oidc.init_provider(my_provider)

And use the authentication as normal:

@app.route('/private')
@oidc.require_login
def private():
   ...

@HarryKodden

Copy link
Copy Markdown
Author

Travis reports 1 test is failing on Python 2.6.
This because 2.6 python fails on the expression number in {2,3,5,7}
Since this is part of rsa package, not sure what i can do about that...

@puiterwijk puiterwijk left a comment

Copy link
Copy Markdown
Owner

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?

Comment thread flask_oidc/__init__.py
# 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():

Copy link
Copy Markdown
Owner

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.

Comment thread flask_oidc/__init__.py
with app.app_context():
self.init_provider(provider)

def __exit__(self, exception_type, exception_value, traceback):

Copy link
Copy Markdown
Owner

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?

Comment thread flask_oidc/__init__.py
# get stuff from the app's config, which may override stuff set above
if app is not None:

if app:

Copy link
Copy Markdown
Owner

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.

Comment thread flask_oidc/__init__.py
"""

secrets = self.load_secrets(provider)
assert secrets != None, "Problem with loading secrets"

Copy link
Copy Markdown
Owner

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.

Comment thread flask_oidc/__init__.py
Do setup for a specific provider

:param provider: The provider to initialize.
:type provider: Dictionary with at lease 'base_url' item

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/lease/least/

Comment thread flask_oidc/__init__.py
def load_secrets(self, provider):

try:
static_secrets = current_app.config.get('OIDC_CLIENT_SECRETS', None)

Copy link
Copy Markdown
Owner

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?

Comment thread flask_oidc/__init__.py

registration = provider.get('registration', None)

if not registration:

Copy link
Copy Markdown
Owner

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.

Comment thread flask_oidc/__init__.py
if not provider:
raise Exception("No Provider specified")

try:

Copy link
Copy Markdown
Owner

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()?

Comment thread flask_oidc/__init__.py

try:
try:
jwks_keys = json.load(

Copy link
Copy Markdown
Owner

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.

Comment thread flask_oidc/__init__.py
except Exception as e:
raise Exception("Error in preparing result: {}".format(str(e)))

raise Exception("No secrets loaded !")

Copy link
Copy Markdown
Owner

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?

@svintit

svintit commented Jul 22, 2020

Copy link
Copy Markdown

@HarryKodden If you want this resolved i'm maintaining a fork of this with some added extensibility. Feel free to open a PR here:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants