Skip to content

docs: Clarify return values of introspect_token()#943

Open
SyntaxColoring wants to merge 2 commits intooauthlib:masterfrom
SyntaxColoring:master
Open

docs: Clarify return values of introspect_token()#943
SyntaxColoring wants to merge 2 commits intooauthlib:masterfrom
SyntaxColoring:master

Conversation

@SyntaxColoring
Copy link
Copy Markdown

I was trying to implement a token introspection endpoint with the following flavor:

  • For debuggability, I wanted to return metadata about tokens that are expired now but were once valid. I acknowledge that this is discouraged (SHOULD NOT) in https://datatracker.ietf.org/doc/html/rfc7662.
  • I wanted to return metadata about refresh tokens, but with "active": false to prevent downstream resource servers from misinterpreting them as valid access tokens. (The RFC leaves it unclear how the token introspection endpoint ought to distinguish refresh tokens from access tokens. This was just my guess. Maybe there's a better way.)

Here was my implementation attempt, which has a bug:

class RequestValidator(oauthlib.oauth2.RequestValidator):
    ...
    
    def introspect_token(
        self,
        token: str,
        token_type_hint: str | None,
        request: oauthlib.common.Request,
    ) -> dict[str, str] | None:
        """Return information about an access or refresh token (if it exists)."""
        found_token_info = ...   # Implementation omitted.

        if found_token is None:
            return None
        else:
            active = (
                found_token_info.type == "access"  # not "refresh"
                and found_token.expires_at > datetime.now()
            )
            return {
                "active": active  # <-- Bug!
                "scope": found_token_info.scope,
                "username": found_token_info.username,
            }

The bug in that code is that returning active: False does not actually do anything. oauthlib always overrides active to True. The only way to return "active": false over HTTP is for the Python code to return None.

if claims is None:
return resp_headers, json.dumps({'active': False}), 200
if "active" in claims:
claims.pop("active")
return resp_headers, json.dumps(dict(active=True, **claims)), 200

That wasn't clear in the oauthlib documentation, so this updates it.

(Documentation aside, I think this behavior is unfortunate because it prevents us from deliberately returning extra information about inactive tokens, but maybe that's just a bad thing to want to do.)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Clarifies the RequestValidator.introspect_token() contract so implementers understand how to produce an “inactive” introspection response in oauthlib’s RFC6749 introspection endpoint.

Changes:

  • Updates the introspect_token() docstring to indicate that only active tokens should return a claims dict.
  • Documents that expired/unknown (inactive) tokens should return None (resulting in "active": false in the HTTP response).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread oauthlib/oauth2/rfc6749/request_validator.py Outdated
@auvipy auvipy self-requested a review March 26, 2026 06:01
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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

Successfully merging this pull request may close these issues.

3 participants