v0.151.1
 1"""Resource-server side: validate access tokens issued by this server.
 2
 3Kept separate from the authorization-server views so a resource server (e.g. a
 4`plain.mcp` endpoint) can validate tokens without importing the view layer.
 5"""
 6
 7from __future__ import annotations
 8
 9from typing import TYPE_CHECKING
10
11if TYPE_CHECKING:
12    from .models import AccessToken
13
14
15def validate_access_token(
16    token: str, *, resource: str | None = None
17) -> AccessToken | None:
18    """Return the live `AccessToken` for a bearer value, or `None`.
19
20    `None` covers unknown, expired, and revoked tokens. When `resource` is
21    given and the token is audience-bound (RFC 8707), the bound resource must
22    match — a token minted for a different endpoint is rejected. Omitting
23    `resource` (or a token with no bound resource) skips the audience check, so
24    pass `resource=` whenever the caller is a specific endpoint.
25    """
26    from .models import AccessToken, _hash_token
27
28    try:
29        access_token = AccessToken.query.select_related("user").get(
30            token_hash=_hash_token(token)
31        )
32    except AccessToken.DoesNotExist:
33        return None
34
35    if not access_token.is_valid():
36        return None
37    if resource and access_token.resource and access_token.resource != resource:
38        return None
39    return access_token