IdentityPython / djangosaml2

Django SAML2 Service Provider based on pySAML2
Apache License 2.0
254 stars 143 forks source link

Question : Multiple tenant SP has to offer ACS dynamically #364

Open Malshtur opened 1 year ago

Malshtur commented 1 year ago

Hello,

I am working to port SAML to a multi tenant application where each tenant has its own database. So every tenant manage its own users, objects, etc.

The issue I am trying to resolve has to do with our authentication scheme requiring to specify what tenant the user authenticates to. I have considered the three following approaches :

The latter is my preffered way to go but I can't wrap my finger around if it is possible with djangosaml2. Could you point me in the right direction if you support it ? If it is not the case I am only trying to find how to do it at pysaml2 level but I have no luck so far.

Thanks for your time.

peppelinux commented 1 year ago

Yes, option 3 is very good

you just have to inherit https://github.com/IdentityPython/djangosaml2/blob/master/djangosaml2/views.py#L408 and then you can do overload of the method post_login, custom_redirect or whatever

then you have to map your new ACS to a specific urls (urls.py of your project) and also map it in the metadata, defining its url/webpath in the pysaml2 configuration, here (to get signed metadata automatically):

https://github.com/IdentityPython/djangosaml2/blob/master/docs/source/contents/setup.rst#pysaml2-specific-files-and-configuration

Malshtur commented 1 year ago

Thanks for your reactivity and the details, truly fast !

Just to be sure we are talking about the same thing, in saml-core : `AssertionConsumerServiceURL [Optional] Specifies by value the location to which the message MUST be returned to the requester. The responder MUST ensure by some means that the value specified is in fact associated with the requester. [SAMLMeta] provides one possible mechanism; signing the enclosing

message is another. This attribute is mutually exclusive with the AssertionConsumerServiceIndex attribute and is typically accompanied by the ProtocolBinding attribute.` From your response, I guess that it would lead to metadata modification on SP side and to update them on the IDP too. This is problematic in many deployment. So that is why I wanted to use the second option : signing the enclosing AuthnRequest and have the AssertionConsumerServiceUrl dynamically set in the request without changing the metadata. I hope that I am a bit clearer and please forgive me if I did not understand you the first time. EDIT: Did you mention the hook or overload to do something like selecting the tenant right after reponse is received in order to pass it to the application level ? EDIT 2 : For clarity, what I was thinking is having a new AssertionConsumerService without altering the in metadata as offers the AssertionConsumerServiceUrl field in AuthnRequest.
peppelinux commented 1 year ago

for the security of the solution the IDP should only response to a known ACS, taken from the metadata of the requester

Malshtur commented 1 year ago

In general, i completly agree but if and only if the AuthnRequest is signed then the underlying security is the same because it is based on the same cryptographic keys.

The only case i could think of is that the underlying SP is vulnerable to injections that could lead to sign an AuthnRequest with a malicious AssertionConsumerServiceUrl. This could also modify metadata nonetheless.

The standard allows both as I mentionned above, but if I were to use djangosaml2 as you explained I have to share metadata dynamically between SP and IDP to refresh the list of AssertionConsumerService, right ?

peppelinux commented 1 year ago

right