IdentityPython / pyop

OpenID Connect Provider (OP) library in Python.
Apache License 2.0
87 stars 33 forks source link

Support restricting client access to claims and scopes #39

Closed vladimir-mencl-eresearch closed 1 year ago

vladimir-mencl-eresearch commented 3 years ago

Hi,

I'm running a SATOSA instance with OIDC front-end and SAML backend as a bridge for connecting OIDC RPs to our SAML federation.

I'm lacking a way of configuring attribute access for individual RPs/clients. So far, any client can request any scope or claim - and if it's configured in SATOSA, the client gets it. I have configured attributes available in the SAML federation as claims available via OIDC, and bundled them together into scopes (using extra_scopes in OIDC front-end configuration).

I think I have not missed anything and this is just not supported with SATOSA / pyop.

I have thought about possible options and I can imagine supporting additional keys in the client database, where each client could have:

      allowed_claims: [list]
      allowed_scopes: [list]
      denied_claims: [list]
      denied_scopes: [list]

I envision the following semantics:

For the mapping of claims and scopes, pyop gets the extra_scopes passed from SATOSA OpenIDConnectFrontend, so any references to scopes would be unfolded to lists of claims.

I can imagine hooking this filtering into handle_userinfo_request and authorize in pyop/src/provider.py.

I'm happy to implement this and send a PR - just creating an issue first to see whether I'm heading in the right direction and whether this approach would be appropriate for the project.

Your thoughts, @c00kiemon5ter ?

Cheers, Vlad

c00kiemon5ter commented 3 years ago

hello @vladimir-mencl-eresearch

first, let me tell you that eduTEAMS is working on a new OIDC frontend that is based on oidc-op and those features are being worked on (if not already there). There is a plan to make it open source, but it has been delayed to due changes on the database schema and token format handled by oidc-op, thus I don't know the exact schedule. pyop is in use and supported until we get that new frontend. The plan is to freeze development on pyop and focus on solely oidc-op when that is in place. Some of the latest additions on pyop though, are nice (re, redis and mongo with auto-expiration (ttl) on certain records).

The current frontend will of course continue to be available and users will be able to use it. The new frontend will co-exist side by side with the current and the users will choose which one they want to use (or both), or have a strategy to migrate.


I'm lacking a way of configuring attribute access for individual RPs/clients

coming to the main topic, which I would probably call "attribute filtering". There are (at least) two ways to look at this:

  1. the general idea of satosa is that it abstracts the protocols into its own representation - the internal data. "Micro-services" (plugins) operate on that protocol-agnostic representation of the information. Regardless of the protocol that the information originated from, or the protocol that the information will be converted to, those operations will do the same thing. Thus, the first approach is to have this functionality built into a micro-service, that you control and for which you can develop rules as complex as you need. The benefit is that the same actions (filtering in this case) will apply regardless of the protocol of the entities. This is how this is usually done. In practice you can set a configuration that maps a list of internal attributes to (outgoing) SPs/RPs and/or (originating) IdPs/OPs, that is parsed by your code and applies the filtering as needed:
module: ...
name: ...
config:
  filter:
    sp_x:
      - eptid
      - eppn
      - name
      - email
    op_y:
      - sub
      - email

    # or more complex
    sp_z:
      op_w:
        - address
        - subject_id
  1. the other way, is as you say, to make this the responsibility of the relevant backend/frontend, and add this functionality there. And so, for example, pysaml2 offers this functionality and it is used by the SAML frontend (if configured). However, the way this works is limited to what pysaml2 at the time thought it was useful. There is no way (currently at least) to extend this or retrofit it if ones requirements differ from the assumptions of the code that is there. This is the reason that I would prefer to use a micro-service for this.

Having said this, I do not think that this is a feature that should be discouraged. I would however propose to think this in relation to what I wrote at the start. If this is going to take a lot of time, only to be replaced with a different frontend it may not be worth the effort, when the alternative of using a micro-service could work in your case and be a more stable choice.


Now, I said "attribute filtering", and this assumes two things:

You said "attribute access", which may mean that you want to deny processing the request and stop the flow once you know that a claim or scope has been requested, that you do not support, or recognize, or allow for that service/RP. In that case, you need to operate on the OP side and this should be implemented on the frontend. When the request is translated to internal data, only what is defined to be mapped (by internal_attribute.yaml) will be available to the micro-services. If you need to know more about the request then you should be looking at the frontend itself.


on the implementation on pyop:

pyop is based on pyoidc (aka oic) and many of the things it does is by wrapping existing functionality of that library. I see that pyoidc has such filtering though, from what I understand, this is not per-client but a global setting. It should be possible to initialize the correct object through pyop given the appropriate capabilities which are managed on the frontend or pyop level, provided pyop is extended to allow this configuration to go through.

Alternatively, as this is a simple operation (filtering), we may not bother with the library and do this entirely on our own. I think this is what you suggest. I'm fine with that and it's probably easier and simpler.


I hope this is useful. Let me know if that changes how think about this problem, or if I missed an important point of your question.

vladimir-mencl-eresearch commented 3 years ago

Hi @c00kiemon5ter ,

Thanks a lot for the detailed reply - much appreciated.

Good to know there would be a new OIDC frontend soon - in that case, I might just wait for that frontend....

Yes, you labelled it right as Attribute Filtering. I did not use the term because I thought of it as too SAML specific, but you are right, it is attribute filtering I am after.

I did not think of a micro service, as I did not expect a microservice would "know" the target OIDC client identity ... and be able to to do specific per-client attribute filtering. I'd see the per-client rules as naturally fitting into client configuration / cdb.json.

Analogy: when registering a client with Google (with Google having the role of OIDC Provider), I have to specifically select the scopes the client will be allowed to request.... and this is stored as part of the client registration.

In the context of SATOSA, I interpreted the scopes as attribute bundles - just groupings of attributes for easier / simpler configuration. Given the bundles are defined as extra_scopes in the client configuration, it felt natural. But having scopes and claims/attributes listed separately might make things too complex ... and maybe listing just claims/attributes might make reasoning about them (and the code) simpler.

Also, it's an interesting point what should happen when a client requests scopes not permitted. I thought of just filtering the claims and returning what the client is entitled to - you are raising a valid question whether it should possibly be returning an error instead.

Thanks again for the detailed reply. Good to know that pyop is likely to be replaced. I'll likely just wait for the new frontend ... but if this becomes burning in the meantime (such has having a significant uptake of the service, with more clients registering), I might do what I proposed here ...

Thanks again, Vlad

vladimir-mencl-eresearch commented 3 years ago

Hi @c00kiemon5ter ,

Reading the release notes for SATOSA 8.0.0 also made me aware there is now an AttributePolicy microservice. (I missed it earlier because it did not have a corresponding config file under examples - I might add one now).

I've been able to get attribute filtering working for a single RP with this microservice. It's still not the perfect solution - the config file for the microservice would have to be generated from the list of clients, but it would be a possible workaround, so noting it here.

And I see the new OIDC front-end is making great progress in IdentityPython/SATOSA#378 - so I might wait for that to be completed and then explore again.

Cheers, Vlad

peppelinux commented 3 years ago

Hi @vladimir-mencl-eresearch FYI https://github.com/IdentityPython/SATOSA/pull/389#issuecomment-934689324

vladimir-mencl-eresearch commented 1 year ago

Hi @peppelinux , I've now tried checking where this is at.

I see that IdentityPython/SATOSA#378 was closed with the oidcop code being maintained separately at https://github.com/UniversitaDellaCalabria/SATOSA-oidcop

What are the long term plans for SATOSA around OpenIDConnect front-end support?

Is pyop going to stay? Is oidcop going to become the recommended front-end? With SATOSA possibly switching from a monolithic project (all frontends/backends included) to a modular approach providing the glue for externally maintained frontend/backend modules?

I've tried looking at the related issues and pull requests and could see that oidcop is not getting merged into SATOSA, but could not tell what the overall direction on this is.

Thanks a lot in advance for getting back to me.

Cheers, Vlad

c00kiemon5ter commented 1 year ago

Hello Vlad and thanks for coming back to this.

Satosa is designed in such a way that separates the core from protocol adapters that essentially translate to/from an internal structure from/to a protocol specific representation. The point of this separation is to facilitate extensibility of supported protocols, choice of implementation and features, independent evolution of the core and the adapters (internal and external), and separation of concerns (maintain focus). We have discussed within IdPy about going further and enforcing this by separating satosa to a -core repo and managing each plugin (micro-service, backend and frontend) separately on their own repos, with their own dependencies, lifecycles and releases.

It is totally fine to use a frontend that is not bundled with satosa or is not under IdPy. I actually think that we should be moving towards such a model instead of centralizing the development and responsibility of code reviews, quality assurance and maintenance on the current satosa repo. In this regard we have a dedicated section on our docs about external resources and contributions by members and users of IdPy. The work that Giuseppe has done, including the satosa-oidcop frontend, is there. From the point of satosa, there should be no "exclusive" or "official" adapter, but we should be talking about choice, preference and flexibility as a feature of the software, in-line with open source development. This is why we have that section in the docs and I want to do more to make this even more visible.

The other frontend that is being developed by eduTEAMS is not open yet. But there is an internal release that works on top of idpy-oidc (which is the successor of oidc-op). That project is reaching a point that is stable to be released but needs more documentation. If you're interested to give it a try and helps us move faster I can contact you to make this happen.

vladimir-mencl-eresearch commented 1 year ago

Hi @c00kiemon5ter , thanks for the detailed reply.

I've now had a go at getting OidcOpFrontend working with my setup.

After some substantial effort, I got it working.

To share the results of the effort, I've filed the following issues (+1 PR):

Overall, it seems to me that OidcOpFrontend will need some additional work before it can be used as a replacement for the current pyop-based OpenIDConnectFrontend. My main concerns with OidcOpFrontend have been:

I hope you find this helpful.

As for the next steps, I'll probably try filtering the attributes with the AttributePolicy microservice. If you'd like to share the eduTEAMS OIDC fronted with me, I'd be keen to have a look as well.

Thanks for all the effort put into this!

Cheers, Vlad

vladimir-mencl-eresearch commented 1 year ago

Hi @c00kiemon5ter ,

FYI, I've been able to get the attribute filtering working with the AttributePolicy microservice - so that pretty much satisfies what I originally requested in this issue. I think we can thus close this issue as resolved.

Cheers, Vlad