italia / spid-cie-oidc-django

The SPID/CIE OIDC Federation SDK, written in Python
Apache License 2.0
27 stars 28 forks source link

Cie UserInfoResponse does not respect scopes #293

Open matteo-s opened 9 months ago

matteo-s commented 9 months ago

It looks like the userInfo response supports only claims explicitly requested. When the authorization request contains scope=openid profile and no claims both idToken and userInfo should report the "standard" profile, as per spec

From https://docs.italia.it/italia/spid/spid-cie-oidc-docs/it/versione-corrente/authorization_endpoint.html#parametri-scope-e-claims

Gli attributi dell'utente POSSONO essere richiesti dal RP nell'Authorization Request usando i parametri scope o claims.
---
Gli attributi richiesti tramite il parametro scope sono disponibili sia nell'ID Token e sia nella risposta allo userinfo endpoint.

It looks like the code simply picks explicitly requested claims from attributes with matching names https://github.com/italia/spid-cie-oidc-django/blob/2b0c2eff271ef290f90f62ba8b7a3d508b887543/spid_cie_oidc/provider/views/userinfo_endpoint.py#L78

Maybe related #271

peppelinux commented 9 months ago

yes

at time of the development of this toolchain the cie specifications was in progress, and the feature you have described is something that was defined later on

I'll keep this for the next milestones, thx

matteo-s commented 9 months ago

Thanks for the response. Until then I've got around with a quick&dirty implementation which maybe can help others

        for scope in (
            token.session.authz_request.get(
                "scope", ""
            ).split()
        ):
            if scope == 'profile':
                jwt['name'] = token.session.user.attributes['username']
                jwt['given_name'] = token.session.user.attributes['given_name']
                jwt['family_name'] = token.session.user.attributes['family_name']
                jwt['birthdate'] = token.session.user.attributes['birthdate']
                jwt['https://attributes.eid.gov.it/fiscal_number'] = token.session.user.attributes['https://attributes.eid.gov.it/fiscal_number']

            if scope == 'email':
                jwt['email'] = token.session.user.attributes['email']
                jwt['email_verified'] = token.session.user.attributes['email_verified']
peppelinux commented 9 months ago

please do that in a PR and I'll review, approve and we'll have a new release together

in a settings.py like this https://github.com/italia/spid-cie-oidc-django/blob/main/spid_cie_oidc/provider/settings.py

create a mapping of the scopes2claims

OIDC_SCOPES_CLAIMS_MAP = {
     "profile": ["username", "given_name", "family_name", "birthdate", "https://attributes.eid.gov.it/fiscal_number"],
     "email": ["email", "email_verified"]
}

then in the code do something like this

        for scope in token.session.authz_request.get("scope", "").split():
            _user_attrs = token.session.user.attributes
            for i in OIDC_SCOPES_CLAIMS_MAP.get(scope, []):
                jwt[i] = _user_attrs.get(settings.OIDCFED_PROVIDER_ATTRIBUTES_MAP[i][0])

try to use the local app settings defaults (overloadable also in the settings project) to reduce the constants in the code