-
-
Notifications
You must be signed in to change notification settings - Fork 101
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
Support for multiple IDPs? #117
Comments
I would be happy to include this as a new feature, but this is not something I personally have time to look at in the short term. If you (or someone reading this) is willing to create a PR I would be happy to review it! |
I'd be happy to help if I can find the time. Is there any documentation on how to setup and run the test suite locally? I see a Vagrant file, but I think some simple documentation in the contributions files on how to get this setup be very helpful. |
I’ve actually only used our company ADFS setup, so never used the Vagrant file personally. I’d have to look into that. PRs for docs on how to use it are very welcome. 😄 |
As for the test suite you can look at this file. |
@JonasKs that file link is helpful. I'm not a poetry user. FYI, I figured out that I can't install this library as editable using pip.
Installing requirements causes this error:
Poetry fails to include setuptools in the generated setup.py file per this bug: |
I tend to just edit files directly in the venv when I’m testing. I’m not on a PC tonight so can’t really look into it. :/ you could go back a few commits and restore the setup.py locally while you test, tho. |
@JonasKs Any suggestions on how you would like to the additional settings to be broken out? Right now the DJANGO_ADFS_AUTH dict is flat and I'm trying to figure out how backwards compatible you want to be. There's already some shims in place for older settings keys that are deprecated. I was thinking something like this. We could continue to support the current configuration or allow people to setup the AUTH_ADFS = {
'IDPS': {
# Key for WAYF, WAYF is not used if only one IDP is defined
'internal': {
'SERVER': 'adfs.example.com',
'CLIENT_ID': 'xxxxx',
'RELYING_PARTY_ID': 'internal-example-com',
'AUDIENCE': 'microsoft:identityserver:internal-example-com',
'CLAIM_MAPPING': {
'first_name': 'FirstName',
'last_name': 'LastName',
'email': 'Email',
'phone_number': 'TelephoneNumber',
'ad_object_guid': 'objectGUID',
},
'CA_BUNDLE': False,
'USERNAME_CLAIM': 'UserName',
'GROUPS_CLAIM': None,
}
},
'CREATE_NEW_USERS': False,
'DISABLE_SSO': True,
'LOGIN_EXEMPT_URLS': [
'^api', # Assuming you API is available at /api
],
'CUSTOM_FAILED_RESPONSE_VIEW': 'security.views.user_denied_openid'
} The key for the WAYF is used for a WAYF page (if more than one IDP is defined) and also in the OAuth2CallbackView as a querystring parameter as part of the callback so we know which IDP to use the auth code against. Thoughts? |
I'd like for both to be supported, as breaking it would require a major version bump. Your suggestion seems clean to me. 👍 |
@JonasKs Thanks for the feedback. In order to make things easier, I would like to propose a dev could continue using the current config key or the new |
Yes, that's perfect. 👍 |
As I mentioned earlier in the ticket, you can run all tests by running |
@JonasKs Thanks for pointing out the Vagrant stuff in the demo section. I never thought to look there -- I was specifically looking on how to run tests in the Contributing readme: https://github.com/snok/django-auth-adfs/blob/master/CONTRIBUTING.rst Maybe the comment you made could be used to have a section in Contributing on how to run the tests? |
Agree! I'll submit it with my next PR, unless someone beats me to it 👍 |
What is the state of this issue? At our company we implemented a workaround for now, but would greatly appreciate if this was supported. I can share the basic idea of our solution, perhaps that's helpful. AUTH_ADFS = {"SETTINGS_CLASS": "path.to.MainAuthAdfsSettings"} # set a main settings class as entry-point class MainSettings:
def __init__(self):
self.deferred_settings = DefaultSettings()
def __getattr__(self, item):
return getattr(self.deferred_settings, item) There is one main settings class which holds a reference to some "actual" settings class. Initially the default settings are set. # Custom settings class for one IDP
class CustomIDPSettings(DefaultSettings):
def __init__(self):
self.SERVER = ...
self.CALLBACK_URL = ... # a custom setting we added to define a callback url for each IDP class CustomOAuth2LoginView(OAuth2LoginView):
def get(self, request):
_patch_django_auth_adfs(CustomIDPSettings)
return redirect(django_auth_adfs.config.provider_config.build_authorization_endpoint(request))
class CustomOAuth2LogoutView(OAuth2LogoutView):
def get(self, request):
_patch_django_auth_adfs(CustomIDPSettings)
return super().get(request)
class CustomOAuth2CallbackView(OAuth2CallbackView):
def get(self, request):
_patch_django_auth_adfs(CustomIDPSettings)
return super().get(request) We have overwritten Login, Logout and Callback view classes. They dont do much, except calling the super class. However before that, the settings are patched. from functools import partial
def _patch_django_auth_adfs(settings_cls):
settings = settings_cls()
# deferred settings object is patched
django_auth_adfs.config.settings.deferred_settings = settings_cls()
# provider config timestamp is set to None, to invalidate the current config
django_auth_adfs.config.provider_config._config_timestamp = None
# redirect uri is patched so the request contains the correct `redirect_uri`
django_auth_adfs.config.provider_config.redirect_uri = partial(
_redirect_uri, # custom method
callback_url_name=settings.CALLBACK_URL, # a custom setting
provider_config=django_auth_adfs.config.provider_config,
)
def _redirect_uri(request, provider_config, callback_url_name: str):
provider_config.load_config()
return request.build_absolute_uri(reverse(callback_url_name)) We added I hope this is helpful to someone how needs a quick and dirty workaround until this feature is implemented. |
Hi! Thanks for providing a workaround. State is that well tested and documented pull requests are welcome, but I am not going to implement it myself. |
Hi all, I'm actively working on a PR for this feature. Watch this space. |
Just an update on the PR - we are currently blocked by jpadilla/pyjwt#433 Right now I could get the authentication portion to work, but tokens would be broken because tokens are checked against valid issuers and right now PyJWT only allows one valid issuer. We could do a big try/catch and try other issuers, but it seems like support in PyJWT is coming soon. |
Multi-tenant scenarios:
Single-tenant scenarios:
I'm also the author of the In the documentation, I provide an example on how to configure such a There's multiple solutions here:
Personally I like doing as I do in |
Also, thank you for the PR! I didn't have time to check it out before now (I was in the US for thanksgiving 🦃). |
@peterfarrell what workaround do you currently have for this jpadilla/pyjwt/433? Allowing multiple issuers issue is currently stale. ☠️ |
A PR to swap out |
Right now this library is limited to a single IDP. I have need where users could authenticate from 2 different IDPs.
Django SAML2 library has the ability to configure multiple IDPs and offers a WAYF page if the IDP is unknown.
https://github.com/knaperek/djangosaml2#pysaml2-specific-files-and-configuration
https://app.example.com/oauth2/login?next=/&idp=adfs.example.com
https://app.example.com/oauth2/login?next=/&idp=adfs.different.com
If only one IDP is configured, then nothing changes. If multiple IDPs are configured and the IDP is not in the login request then a WAYF view would be served. The value in the IDP would be the current
SERVER
valueWould there be interest in a PR to add support for this? If I had time, I'd work on one.
The text was updated successfully, but these errors were encountered: