Skip to main content

Overview

The verify_grant_token() function verifies a Grantex grant token’s signature and claims locally after retrieving the published JSON Web Key Set (JWKS). It avoids the online verification/revocation endpoint, but the current standalone helper fetches the remote JWKS on every invocation and is not network-free. This is ideal for service-side verification where latency matters and you want to avoid an API round-trip for every request.
Hosted tokens use the canonical issuer https://grantex.dev, even though the stable JWKS URL is served from https://api.grantex.dev. The SDK handles this alias automatically. For self-hosted deployments, it derives the expected issuer from the JWKS URL unless you set issuer or issuer_did explicitly.

Usage

Import

Options

VerifyGrantTokenOptions configures how the token is verified:

Response

verify_grant_token() returns a VerifiedGrant frozen dataclass: Fields are read from the standard claims (scope, client_id, act, urn:grantex:grant) first; see spec/grant-token-0.6.md.

Algorithm

Tokens signed with RS256 or ES256 are accepted (grantex.GRANT_TOKEN_ALGORITHMS). alg: none, HS256 and every other algorithm are rejected, and algorithms can only narrow the list. The key is the JWK Set entry named by the token’s kid whose type matches the algorithm: an RSA key for RS256, an EC key on P-256 for ES256. A key published with a different alg or a use other than sig is never used, and there is no fallback to another key.

JWKS fetch limits

grantex==0.6.1 includes these limits and the DID issuer checks when bounded_jwks_fetch=True is set. The option remains off by default; choose it explicitly when verifying untrusted remote issuers.
The key set comes from whichever endpoint the verifier is pointed at. With bounded_jwks_fetch=True, each fetch is bounded, with the same limits as the TypeScript SDK’s boundedJwksFetch: true:
A refused fetch raises GrantexTokenError naming the endpoint and the reason, and nothing is cached, so the next verification fetches again. The key-count limit leaves room for the grantex-YYYY-MM aliases a self-hosted auth service publishes (JWT_LEGACY_KID_MONTHS). grantex.decisions.verify_decision_grant and verify_decision_grants take the same bounded_jwks_fetch keyword for the keys they read from jwks_uri. Without the option, the key set is read as in earlier releases: any 2xx response of any size, media type and number of keys, with a 10-second timeout for each network operation. Bounded and unbounded key sets for the same URL are cached separately.

DID issuers

With bounded_jwks_fetch=True, issuer_did follows the did:web method specification (§2.3, §2.5.2): the identifier after did:web: is a fully qualified domain name, optionally followed by a port whose colon is percent-encoded (%3A) and by path segments separated by colons. A DID is written in ASCII (§3.5), so an internationalized domain name is given in its IDNA A-label form, beginning xn--. The DID decides whose keys are trusted, so the SDK raises GrantexTokenError, before any request, for an IP address, localhost or a name under .localhost, .local, .home.arpa or .internal, a single-label host, user information (@), a port outside 1–65535, a path segment other than letters, digits, ., - and _ (or . / ..), any character outside ASCII, and any value that is not a did:web identifier. A non-ASCII name is refused rather than converted, because conversion can turn a different character into an ASCII one (the Kelvin sign, U+212A, into k) and so fetch keys from a host the DID does not spell. It is never ignored in favour of jwks_uri. For a local or private issuer, set jwks_uri and issuer instead. issuer_did=None means no DID. The TypeScript SDK applies the same rules with boundedJwksFetch: true, and treats issuerDid: null as None. Without the option, issuer_did is read as in earlier releases: a value that does not start with did:web: is ignored in favour of jwks_uri, and the rest of the identifier is used as written, each : becoming /, with none of the checks above (so a percent-encoded port is not decoded).

Examples

Basic Verification

Require Specific Scopes

With Audience and Clock Tolerance

Verifying Delegated Tokens

Delegated tokens include additional claims for the delegation chain:

Error Handling

verify_grant_token() raises GrantexTokenError in the following cases:
  • The token header uses an algorithm other than RS256 or ES256, or one excluded by algorithms
  • The JWKS endpoint is unreachable or returns invalid data
  • With bounded_jwks_fetch=True, the JWKS response exceeds a fetch limit: not 200, not JSON, encoded, larger than 64 KiB, more than 128 keys, or slower than 5 seconds
  • With bounded_jwks_fetch=True, issuer_did is not a usable did:web issuer
  • No key of the type the algorithm requires is found in the JWKS under the token’s kid
  • The token signature is invalid
  • The token is expired
  • The token issuer does not match the expected issuer
  • The token audience does not match audience, when configured
  • Required claims (jti, sub, agt, dev, scp, iat, exp) are missing
  • Required scopes are not present in the token

Ownership

Grantex is owned by Orchestrum Technologies LLP. Inventor and owner: Sanjeev Kumar. Ownership contact: sanjeev@orchestrum.in or mishra.sanjeev@gmail.com.
Last modified on September 28, 2026