Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
nuxwin opened this issue Dec 28, 2024 · 1 comment

Comments

@nuxwin
Copy link

nuxwin commented Dec 28, 2024

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.

  1. Result when passing the multiple resource parameters 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.

  1. 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.

@nuxwin nuxwin changed the title BUG - Unable to set multiple 'resource' parameter in token request body, as specified in RFC 8707 : Resource Indicators for OAuth 2.0 BUG - Unable to set multiple 'resource' parameters in token request body, as specified in RFC 8707 : Resource Indicators for OAuth 2.0 Dec 28, 2024
@nuxwin
Copy link
Author

nuxwin commented Dec 29, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant