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

Introspection endpoint doesn't require "introspection" scope when used in client-credentials flow #1451

Open
1 of 2 tasks
makulatur opened this issue Aug 8, 2024 · 3 comments
Assignees
Labels
Milestone

Comments

@makulatur
Copy link

Hi. I am not entirely sure if this is a bug or I am misunderstanding something about OAuth2, I'd like to apologize in advance if it's the latter.

Describe the bug

I followed the tutorial to create an access token and tried using the IntrospectTokenView with that token. This view is a ClientProtectedScopedResourceView that includes required_scopes = ["introspection"]. My understanding of the documentation around these made me believe that if I use a bearer token without the appropriate scope, my request will fail. To my surprise, the call succeeds regardless of the scopes in the token I use.

To Reproduce

  • Set up a basic OAuth2 provider as suggested by the tutorial.
  • Use these steps to create an access token.
  • Try accessing the introspect endpoint like so: curl -X POST -H "Authorization: Basic ${CREDENTIAL}" -H "Cache-Control: no-cache" -H "Content-Type: application/x-www-form-urlencoded" "http://127.0.0.1:8000/o/introspect/" -d "token=..."

Expected behavior
Even when using the client-credentials flow, I expect a token that doesn't have the introspection scope to be rejected when calling an endpoint declaring required_scopes = ["introspection"].

Version
2.4.0

  • I have tested with the latest published release and it's still a problem.
  • I have tested with the master branch and it's still a problem.

Additional context
I googled around and found this Auth0 thread for a similar question. There it is suggested that scopes in a token are not considered at all if using client-credentials. However, in that example the client itself has associated scopes. This isn't the case for the default setup of DOT, as far as I can see.

As it stands, when using client-credentials, any token can be inspected by any client. If this is the desired behavior that's fine with me. If not, however, you might consider a small change to IntrospectTokenView.get_token_response that uses cls.required_scopes:

@classmethod
def get_token_response(cls, token_value=None):
    try:
        ...
    except ObjectDoesNotExist:
        return JsonResponse({"active": False}, status=200)
    else:
        if token.is_valid(cls.required_scopes):
            ...
@makulatur makulatur added the bug label Aug 8, 2024
@n2ygk
Copy link
Member

n2ygk commented Aug 8, 2024

I think you are right that the introspection client (what DOT calls an application) should be somehow constrained in whether it is authorized to introspect. However I don't think using token.is_valid(cls.required_scopes) is correct as that is checking the token that is being introspected rather than the token of the introspector. Also only a Authorization: Bearer <token> would have an access token with a scope. When Authorization: Basic <base64(user:pass)> is used, there is no place for a scope as there is no Access Token and technically this is not a client credentials flow as implemented, so it seems any application with client_credentials grant_type can be used for introspection. Hopefully most actual users have authorization_code grant_type.

Introspection Basic auth was added pretty recently (2019 in #725:-) so I suspect the earlier Bearer-only version of introspection worked correctly [1]. When support for Basic was added it broke this checking. Prior to that PR, introspection could only be done with a Bearer access token.

I expect a PR to fix this would somehow have to constrain applications with the client_credentials as discussed in #709

BTW, the part of the discussion in #709 about constraining which applications a particular introspection application can introspect might be a little too much. In my experience with a commercial product, an introspector can introspect any access token. And, with OIDC, anyone with a given Access Token can essentially introspect it via the Userinfo endpoint.

However, in the commercial product, the introspector is specially identified with a "fake" grant type of ACCESS_TOKEN_VALIDATION. In other words it is flagged as somehow special and allowed to invoke the introspection endpoint rather than just allowing anyone with client_credentials to do so. I believe this historically predates the creation of a standardized introspection endpoint which came out 3 years after OAuth2

So to summarize, using Authorization: Basic is technically not an OAuth2 client credentials flow at all and conflating that was probably a mistake. If it were a real client credentials flow, the introspector client would have made anAuthorization: Basic client credentials request to the AS's token endpoint, been given an Access Token, and then presented that Access Token in an Authorization: Bearer header.

[1] Further, given that any client can request any scope, I think that means that any client's access token (Bearer) can be an introspector!

@n2ygk n2ygk self-assigned this Aug 8, 2024
@n2ygk
Copy link
Member

n2ygk commented Aug 8, 2024

Confirmed in testing that anyone can request introspection scope (e.g. using authorization code flow) and then use the Bearer token to introspect any access token.

Not a huge concern since the access token needs to be protected no matter what as possession means access to whatever resources allow it.

@makulatur
Copy link
Author

makulatur commented Aug 9, 2024

Hey, thanks for the very quick response time.

However I don't think using token.is_valid(cls.required_scopes) is correct as that is checking the token that is being introspected rather than the token of the introspector.

facepalm - of course.

When Authorization: Basic <base64(user:pass)> is used, there is no place for a scope as there is no Access Token and technically this is not a client credentials flow as implemented, so it seems any application with client_credentials grant_type can be used for introspection. Hopefully most actual users have authorization_code grant_type.

Sorry, I am still new to this thing. According to this article on the OAuth website the "client needs to authenticate themselves for this request. Typically the service will allow either additional request parameters client_id and client_secret, or accept the client ID and secret in the HTTP Basic auth header." This reads to me as if it's acceptable to use auth basic in the client credentials flow. Maybe I'm missing something.

I expect a PR to fix this would somehow have to constrain applications with the client_credentials as discussed in #709

I dug around a bit and it seems to me like the OAuth2Validator already has most of the tooling to retrieve the client from a request with basic auth, but the functions are not yet refactored in a way that they are easy to use inside the IntrospectTokenView.

So to summarize, using Authorization: Basic is technically not an OAuth2 client credentials flow at all and conflating that was probably a mistake. If it were a real client credentials flow, the introspector client would have made anAuthorization: Basic client credentials request to the AS's token endpoint, been given an Access Token, and then presented that Access Token in an Authorization: Bearer header.

Thanks for the summary, this seems logical to me (although I still wonder about the article above).

@n2ygk n2ygk added this to the 3.0.0 milestone Aug 13, 2024
@n2ygk n2ygk modified the milestones: 3.0.0, Future Sep 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants