requests-oauthlib
requests-oauthlib copied to clipboard
BUG - Unable to set multiple 'resource' parameters in token request body, as specified in RFC 8707 : Resource Indicators for OAuth 2.0
Hi,
I'm using latest version of the requests-authlibpackage.
For our authorization server, we added support for audience claim, according RFC 8707. 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.
- Result when passing the multiple
resourceparameters through kwargs, as a list:
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 resourceparameters is encoded as single string which is wrong.
- Result when passing the multiple
resourceparameters through body:
Request kwargs: {'data': {'resource': 'https://resourceserver2.example.com', 'grant_type': 'client_credentials', 'scope': 'read write'}}
As you can see, only one resourceparameter is kept which is wrong too.
For the record, I tried with the following code:
By passing multiple resource parameters in body:
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:
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.
Something that seem to work but... really ugly:
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.