Skip to content
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

how to add support for SAML federations #3749

Open
MichaelMenge opened this issue Apr 23, 2024 · 7 comments
Open

how to add support for SAML federations #3749

MichaelMenge opened this issue Apr 23, 2024 · 7 comments

Comments

@MichaelMenge
Copy link

I want to allow authentication by all IDPs in a saml federation (e.g. DFN-AAI or eduGAIN).

I am willing to try to implement this feature myself, but I am not a programmer but a system administrator.
Therefore i would like to get feedback on how to best implement the support for SAML federations.

At the moment a manual configuration for each idp in the federation is possible, but:

  • configuring hundreds of idps and keeping them up to date is error prone.
  • the "sp" and "advanced" configuration would be duplicated for each "idp"
  • the sp-metadata for each idp would differ because of the client_id/organization slug in the sls and acs URLs.
    But some federations distributed the metadata via an aggregate file, and therefore the sp metadata must be identical
    for all idps
  • The List of hundreds of idps would clutter the "accounts/login" page

My goals are:

  • configure all IDPs with one aggregate metadata file
  • use "exclude_entity_ids" and "overwrite_mapping" to exclude or change mapping for some idps
  • create a Discovery Service (DS) / "Where are you from" Site or allow a redirect to a DS/WAYF URL
    DFN-eduGAIN-WAYF
    to help the user find the correct IDP
  • the "provider_id" should be set to the entity_id of the selected idp

Below is a skeleton example configuration

SOCIALACCOUNT_PROVIDERS = {
  "saml": {
    ...
    "APPS": [
      {
        "name": "eduGAIN"
        "client_id": "edugain"
        "settings": {
           # Default mapping
           "attribute_mapping": {
              "uid": "http://schemas.auth0.com/clientID",
              "email_verified": "http://schemas.auth0.com/email_verified",
              "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
           },
        "federation": {
           # metadata aggregate
           "metadata_url":   "https://mds.edugain.org/edugain-v2.xml",
           # List of idp entity_ids to exclude               
           "exclude_entity_ids": [],
           # Array of entity_ids and new attribute_mapping to overwrite the default mapping
           "overwrite_mapping": [].
           "ds_url": "https://wayf.aai.dfn.de/DFN-AAI-eduGAIN/wayf/www/WAYF.php",
        },
       "sp": {
         ...
       },
      "advanced": {
        ...
      },
     }
    ]
  }
}        
@pennersr
Copy link
Owner

I think it would make most sense to override the list_apps() adapter method:

https://docs.allauth.org/en/latest/socialaccount/adapter.html#allauth.socialaccount.adapter.DefaultSocialAccountAdapter.list_apps

By default, it reads SocialApp records from the database, as well as apps configured in settings. But, there is nothing stopping you from doing:

def list_apps(self, request, provider=None, client_id=None):
  apps = super().list_apps(request, provider=provider, client_id=client_id)
  for some_config in some_source:
    app = SocialApp(provider="saml", provider_id=...)  # convert some_config
    if client_id  and client_id != app.client_id:
      continue
    if provider and app.provider_id != provider and app.provider != provider:
      continue
    apps.append(app)
  return apps

As for this:

The List of hundreds of idps would clutter the "accounts/login" page

Isn't that just a matter of altering the login template? Though, we could streamline that with supporting app.settings['visible'] = False.

Would this approach address your needs?

@MichaelMenge
Copy link
Author

@pennersr Thank you for your input. I had not considered the option to override the default adapter.

While i still think that the support for saml federations in gjango-allauth might be useful in the long run,
I agree that overriding list_apps and changing the login template is the faster and direct approach.

@pennersr
Copy link
Owner

Ok, let's try it this way first.

@MichaelMenge
Copy link
Author

@pennersr I was able to configure multiple IdP with my own list_apps() method as long as each had a different client_id. But this leads to a problem regarding the client_id/organization slug in the acs URLs
The sls URLs may have the same problem, but I have not tested this.

The SP Metadata information is managed via a national service portal and is shared with all IdPs in the
DFN AAI / eduGAIN. The IdPs check if the URLs used are listed in the metadata information
for the SP. I can provide multiple acs and sls URLs but wildcards are not supported.
At the moment there are ~400 IdPs in the DFN AAI and ~5600 in eduGAIN. Adding and managing
that many URLs in my SP metadata information is not feasible.

So for the saml authentication to work with federations, i see two possible implementations

  1. a special acs URL should be used, that is independent of the client_id.
  2. multiple IdPs with the same client_id should be supported.

The SAML Response has to be parsed in both cases to extract the entityID of the IdP,
to look up the configuration as the client_id is not provided with the URL in the first case,
or is not sufficient access the correct configuration.

At the moment I tend to the independent special URL implementation, as it seems to have
the least impact on existing code.

@pennersr pennersr reopened this May 16, 2024
@pennersr
Copy link
Owner

Just to be sure that I am fully grasping the issue -- you are implementing the SP side of things. So you have ~400 IdPs, meaning, You have 400 acs urls /accounts/saml/{1..400}/acs/, as well as 400 metada urls /accounts/saml/{1..400}/metadata/. So, the issue is that you need to manually configure those 400 metadata urls over at the portal? Because here you mention:

Adding and managing that many URLs in my SP metadata information is not feasible.

... suggesting that you are not managing those in the portal but in your SP metadata somehow?

But suppose there is just one special acs URL and a user is using that to authenticate. How could you lookup the IdP without having it available in the URL?

@pennersr
Copy link
Owner

Thinking out loud how this could be supported out of the box:

  • You would only setup one SAML app, with the actual IdP information left blank.
  • Then, over at the ACS view, we could dynamically extract the issuer using base64.b64decode(request.POST['SAMLResponse']).
  • Then, we would need to fetch this URL https://mds.edugain.org/edugain-v2.xml and see if the issuer is known.
  • If known, we can dynamically put together the settings.idp configuration of the app, and continue as is now.

I guess this is what you were suggesting in your initial comment? I am not sure though what "ds_url": "https://wayf.aai.dfn.de/DFN-AAI-eduGAIN/wayf/www/WAYF.php" is meant for?

@MichaelMenge
Copy link
Author

first, I am new to saml authentication and I am a sysadmin and not a professional programmer,
so take my information with a grain of salt.

Just to be sure that I am fully grasping the issue -- you are implementing the SP side of things. So you have ~400 IdPs, meaning, You have 400 acs urls /accounts/saml/{1..400}/acs/, as well as 400 metada urls /accounts/saml/{1..400}/metadata/. So, the issue is that you need to manually configure those 400 metadata urls over at the portal? Because here you mention:

Yes I am only implementing the SP side. For security reasons the IdPs do not fetch the metadata from SP,
but use the metadata provided by the federation via the portal. So as far as I know the metadata url is not relevant.

Adding and managing that many URLs in my SP metadata information is not feasible.

... suggesting that you are not managing those in the portal but in your SP metadata somehow?

But suppose there is just one special acs URL and a user is using that to authenticate. How could you lookup the IdP without having it available in the URL?

The IdP entetyID is included in the SAMLResponse

Thinking out loud how this could be supported out of the box:

* You would only setup one SAML app, with the actual IdP information left blank.

My Initial idea was to provide a metadata file with all the IdPs , or I could create a configuration
for each IdP a my own list_apps() method. The later case might be usefull to overwrite some settings
e.g. Attribute Mappings

* Then,  over at the ACS view, we could dynamically extract the issuer using `base64.b64decode(request.POST['SAMLResponse'])`.

yes,

* Then, we would need to fetch this URL https://mds.edugain.org/edugain-v2.xml and see if the issuer is known.

I would use a local copy with only the IdP metadata, but yes, Use the entiryID to extract the config parameters like certs,

* If known, we can dynamically put together the `settings.idp` configuration of the app, and continue as is now.

I guess this is what you were suggesting in your initial comment? I am not sure though what "ds_url": "https://wayf.aai.dfn.de/DFN-AAI-eduGAIN/wayf/www/WAYF.php" is meant for?

The discovery service url aka "Where Are You From" is a fancy version of the IdP selection that uses cookies and geoip information to pre-select the last used, or the most likely IdP and lets you search for your organisation
if you want to use an other IdP

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

No branches or pull requests

2 participants