BUG - Unable to set multiple 'resource' parameters in token request body, as specified in RFC 8707 : Resource Indicators for OAuth 2.0

#558 · open · 1 comments

View on GitHub ↗

nuxwin

Hi, I'm using latest version of the `requests-authlib`package. For our authorization server, we added support for audience claim, according [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707#name-resource-parameter). So, basically put, one client can request access to one or many resources by adding the `resource` parameter in the token request **body**. When requesting access to many resources, many 'resource' parameters need to be added into the token request rather than a single 'resource' parameter with space-separated resource values (resource URI), as it is done with scope claim. For instance, for a token request using the **client credentials grant** (POST request): ``` POST /token HTTP/1.1 Host: server.example.com Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&resource=https//resourceserver1.example.com&resource=https//resourceserver2.example.com&scope=read+write ``` The problem is that with your current implementation, resulting **Request kwargs** are wrong. 1. Result when passing the multiple `resource` parameters through **kwargs**, as a list: ```python Request kwargs: {'data': {'grant_type': 'client_credentials', 'scope': 'read write', 'resource': "['https://resourceserver1.example.com', 'https://resourceserver2.example.com']"}} ``` As you can see the list of `resource`parameters is encoded as single string which is wrong. 2. Result when passing the multiple `resource`parameters through **body**: ```python Request kwargs: {'data': {'resource': 'https://resourceserver2.example.com', 'grant_type': 'client_credentials', 'scope': 'read write'}} ``` As you can see, only one `resource`parameter is kept which is wrong too. For the record, I tried with the following code: By passing multiple `resource` parameters in **body**: ```python token = OAuth2Session(client=BackendApplicationClient(client_id=client_id)).fetch_token( token_url='https://my-oauthorization-server.tld/o/token', client_id=client_id, client_secret=client_secret, body="resource=https://resourceserver1.example.com&resource=https://resourceserver2.example.com", scope=["read", "write"] ) ``` By passing multiple `resource` parameters as **kwargs**: ```python token = OAuth2Session(client=BackendApplicationClient(client_id=client_id)).fetch_token( token_url='https://my-oauthorization-server.tld/o/token', client_id=client_id, client_secret=client_secret, resource=["https://resourceserver1.example.com", "https://resourceserver2.example.com"], scope=["read", "write"] ) ``` Thank you.

Comments

nuxwin

Something that seem to work but... really ugly: ```python import ast from oauthlib.oauth2 import BackendApplicationClient from requests_oauthlib import OAuth2Session # Register a hook to add multiple resource parameters to the token request as per RFC 8707. def _oauth2_resource_indicators_injector(token_url, headers, request_kwargs): # Modify the resource parameter to be a list if "data" in request_kwargs and isinstance(request_kwargs["data"], dict) and "resource" in request_kwargs["data"]: # Convert the string that looks like a list into an actual list using ast.literal_eval try: resource_list = ast.literal_eval(request_kwargs["data"]["resource"]) # Ensure it's a list. if isinstance(resource_list, list): # Overwrite the resource parameter with the list. request_kwargs["data"]["resource"] = resource_list except (ValueError, SyntaxError): # If conversion fails, leave it as is. pass return token_url, headers, request_kwargs oauth_session = OAuth2Session(client=BackendApplicationClient(client_id='abcdefg')) oauth_session.register_compliance_hook("access_token_request", _oauth2_resource_indicators_injector) token = oauth_session.fetch_token( token_url="https://my-oauthorization-server.tld/o/token", client_id="abcdefg", client_secret="abcdefgabcdefgabcdefgabcdefg", resource=[ "https://api1.example.com", "https://api2.example.com" ], scope=["read", "write"] ) ``` Result (prepared request body): ``` grant_type=client_credentials&scope=read+write&resource=https%3A%2F%2Fapi1.example.com&resource=https%3A%2F%2Fapi2.example.com ``` but hooks are intented to be used for non compliant providers. From my point of view, here, the non compliant part is **OAuth2Session**.