CZ-NIC / pyoidc

A complete OpenID Connect implementation in Python
Other
718 stars 259 forks source link

Cannot add `scope` to registration request #856

Open christian-hawk opened 1 year ago

christian-hawk commented 1 year ago

I would like to add the scope param to my dynamic registration request. But I realized that oic does not allow it the way I'm trying. scope param is being ignored.

As register docstring states:

"""
        Register the client at an OP.

        :param url: The OPs registration endpoint
        :param registration_token: Initial Access Token for registration endpoint
        :param kwargs: parameters to the registration request
        :return:
        """

so, example:

op_url = 'https://my-op-url.com'
client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
op_data = client.provider_config(op_url)
registration_args = {'redirect_uris': redirect_uris,
                             'response_types': ['code'],
                             'grant_types': ['authorization_code'],
                             'application_type': 'web',
                             'client_name': 'My own RP',
                             'token_endpoint_auth_method': 'client_secret_post',
                             'scope': 'openid' }
reg_info = client.register(op_data['registration_endpoint'], **registration_args)

And the scope param is not sent in request.

I checked req.parameters(), which gets me the following params:

['redirect_uris', 'response_types', 'grant_types', 'application_type', 'contacts', 'client_name', 
'logo_uri', 'client_uri', 'policy_uri', 'tos_uri', 'jwks', 'jwks_uri', 'sector_identifier_uri', 
'subject_type', 'id_token_signed_response_alg', 'id_token_encrypted_response_alg', 
'id_token_encrypted_response_enc', 'userinfo_signed_response_alg', 
'userinfo_encrypted_response_alg', 'userinfo_encrypted_response_enc', 
'request_object_signing_alg', 'request_object_encryption_alg', 
'request_object_encryption_enc', 'token_endpoint_auth_method', 
'token_endpoint_auth_signing_alg', 'default_max_age', 'require_auth_time', 
'default_acr_values', 'initiate_login_uri', 'request_uris', 'post_logout_redirect_uris', 
'frontchannel_logout_uri', 'frontchannel_logout_session_required', 
'backchannel_logout_uri', 'backchannel_logout_session_required']

It looks like any other param then not any of those, is ignored.

After researching OAuth docs, in RFC7591 Section 1.3 , I found the following:

The following client metadata fields are defined by this specification. The implementation and use of all client metadata fields is OPTIONAL, unless stated otherwise.

scope String containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. The semantics of values in this list are service specific. If omitted, an authorization server MAY register a client with a default set of scopes.

schlenk commented 1 year ago

Why do you think you need to register the scope for OpenID connect? Does some OP force that?

For OpenID Dynamic Registration (https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata ) scope isn't a supported value. So the default oic.oic.Client does not implement it.

OAuth 2.0 does allow it though, but we only implement the OAuth2 registration in the extensions, https://github.com/CZ-NIC/pyoidc/blob/444bd6845e13b06c14fbaefccbc0c47059aa2364/src/oic/extension/client.py#L390, see the RegistrationRequest class https://github.com/CZ-NIC/pyoidc/blob/f6c590cd8d5834f7e2a2d746ded934549e1fd5f8/src/oic/extension/message.py#L97 this allows scope as part of the registration.

christian-hawk commented 1 year ago

Why do you think you need to register the scope for OpenID connect? Does some OP force that?

It does not force, but it register with no scope at all.

For OpenID Dynamic Registration (https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata ) scope isn't a supported value. So the default oic.oic.Client does not implement it.

Actually scope is supported, but unlisted in this spec. If you check the last line of the provided section, you will find:


Additional Client Metadata parameters MAY also be used. Some are defined by other specifications, such as OpenID Connect Session Management 1.0 [OpenID.Session].


Another interesting reading for this topic can be found at OpenID Foundation repository, entitled "Update dynamic client registration spec to reference OAuth2 dynamic client reg". Issue description:

In section 2.0 (Client Metadata) of the Dynamic Client Registration spec there is a statement at the end of the section that says "Additional Client Metadata parameters MAY also be used. ..." I recommend we update this text to also reference the OAuth2 Dynamic Client Registration spec as another place where additional client metadata parameters are defined (specifically, scope and software statements). "Additional Client Metadata parameters MAY also be used. Some are defined by other specifications, such as OpenID Connect Session Management 1.0 [OpenID.Session] and OAuth 2.0 Dynamic Client Registration Protocol [https://tools.ietf.org/html/rfc7591]".

Correct me if I'm wrong, but by specs sending scope is allowed.

schlenk commented 1 year ago

Agreed. The spec allows sending any key and value you want. The default client just does not implement arbitrary additional registration values in its message factory in order to be able to validate the parameters.

So if you want to register a scope value, you must override the message_factory passed into the client:

from oic.oic.message import OIDCMessageFactory, RegistrationRequest, RegistrationResponse, MessageTuple

class MyRegistrationRequest(RegistrationRequest):
    # add the fields you want to send

class MyRegistrationResponse(RegistrationResponse):
    # add the extra fields you expect the OP to send back

class MyMessageFactory(OIDCMessageFactory):
        registration_endpoint = MessageTuple(MyRegistrationRequest, MyRegistrationResponse)

client = Client(client_authn_method=CLIENT_AUTHN_METHOD,
                      message_factory=MyMessageFactory)

See the https://github.com/CZ-NIC/pyoidc/blob/master/src/oic/extension/message.py#L97 for a message type that has the OAuth2 registration fields available. There are a lot of publicly registered fields, see https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#client-metadata for the public list and any OP might invent their own additional ones.

christian-hawk commented 1 year ago

thanks, do you think that updating docs / docstring about allowed fields may be helpful?

tpazderka commented 1 year ago

Yes, you are welcome to submit a PR with updated docs.