Require explicit scope claim in validate_token
Why
Our bearer tokens carry roles for coarse-grained access control but have never enforced fine-grained OAuth-style scopes. Two recent audit findings make scope enforcement a hard requirement before the Q3 compliance deadline:
Tokens issued to the webhook relay were accepted by the billing API even when the relay's token carried no billing-specific grant. The notifications dispatcher was accepting any valid token regardless of whether it was issued for notification operations. What changes validate_token(token) now requires a second positional argument:
Before
claims = validate_token(token)After
claims = validate_token(token, required_scope="billing:read")Every caller must declare the minimum scope it needs. issue_token gains a matching scopes parameter so callers and tests can mint tokens with the correct grants.
TokenClaims grows a scopes: list[str] field. Tokens issued without a scopes array in the payload treat it as [], so tokens pre-dating this change will be rejected by any scope-enforcing caller until re-issued.