Skip to main content

Overview

The verify_grant_token() function verifies a Grantex grant token locally using the published JSON Web Key Set (JWKS). This approach has zero runtime dependency on Grantex infrastructure — the only network call is to fetch the JWKS from the issuer. This is ideal for service-side verification where latency matters and you want to avoid an API round-trip for every request.

Usage

from grantex import verify_grant_token, VerifyGrantTokenOptions

grant = verify_grant_token(
    "eyJhbGciOiJSUzI1NiIs...",
    VerifyGrantTokenOptions(
        jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
    ),
)

print(f"Principal: {grant.principal_id}")
print(f"Agent DID: {grant.agent_did}")
print(f"Scopes: {grant.scopes}")
print(f"Grant ID: {grant.grant_id}")

Import

from grantex import verify_grant_token, VerifyGrantTokenOptions, GrantexTokenError

Options

VerifyGrantTokenOptions configures how the token is verified:
FieldTypeRequiredDefaultDescription
jwks_uristrYesURL of the JWKS endpoint (e.g. https://api.grantex.dev/.well-known/jwks.json).
required_scopeslist[str] | NoneNoNoneIf set, verification fails if the token is missing any of these scopes.
clock_toleranceintNo0Seconds of leeway for exp and iat clock skew.
audiencestr | NoneNoNoneExpected aud claim. If None, audience is not validated.

Response

verify_grant_token() returns a VerifiedGrant frozen dataclass:
FieldTypeDescription
token_idstrThe JWT jti claim (unique token identifier).
grant_idstrThe grant identifier (grnt claim, falls back to jti).
principal_idstrThe user/principal who authorized the grant (sub).
agent_didstrThe agent’s DID (agt claim).
developer_idstrThe developer who owns the agent (dev claim).
scopestuple[str, ...]The granted permission scopes.
issued_atintUnix timestamp when the token was issued.
expires_atintUnix timestamp when the token expires.
parent_agent_didstr | NoneParent agent DID (delegation chains only).
parent_grant_idstr | NoneParent grant ID (delegation chains only).
delegation_depthint | NoneDelegation depth (0 = root grant).

Algorithm

The algorithm is fixed to RS256 per SPEC section 11. Tokens signed with any other algorithm are rejected. This cannot be overridden.

Examples

Basic Verification

from grantex import verify_grant_token, VerifyGrantTokenOptions

grant = verify_grant_token(
    token,
    VerifyGrantTokenOptions(
        jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
    ),
)

print(f"Token ID: {grant.token_id}")
print(f"Grant ID: {grant.grant_id}")
print(f"Authorized by: {grant.principal_id}")
print(f"Agent: {grant.agent_did}")
print(f"Scopes: {grant.scopes}")

Require Specific Scopes

from grantex import verify_grant_token, VerifyGrantTokenOptions, GrantexTokenError

try:
    grant = verify_grant_token(
        token,
        VerifyGrantTokenOptions(
            jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
            required_scopes=["files:read", "files:write"],
        ),
    )
except GrantexTokenError as e:
    print(f"Verification failed: {e}")
    # e.g. "Grant token is missing required scopes: files:write"

With Audience and Clock Tolerance

from grantex import verify_grant_token, VerifyGrantTokenOptions

grant = verify_grant_token(
    token,
    VerifyGrantTokenOptions(
        jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
        audience="https://api.myservice.com",
        clock_tolerance=30,  # allow 30 seconds of clock skew
    ),
)

Verifying Delegated Tokens

Delegated tokens include additional claims for the delegation chain:
from grantex import verify_grant_token, VerifyGrantTokenOptions

grant = verify_grant_token(
    delegated_token,
    VerifyGrantTokenOptions(
        jwks_uri="https://api.grantex.dev/.well-known/jwks.json",
    ),
)

if grant.delegation_depth is not None:
    print(f"This is a delegated token (depth: {grant.delegation_depth})")
    print(f"Parent agent: {grant.parent_agent_did}")
    print(f"Parent grant: {grant.parent_grant_id}")
else:
    print("This is a root grant token")

Error Handling

verify_grant_token() raises GrantexTokenError in the following cases:
  • The token header uses an algorithm other than RS256
  • The JWKS endpoint is unreachable or returns invalid data
  • No matching RSA key is found in the JWKS
  • The token signature is invalid
  • The token is expired
  • Required claims (jti, sub, agt, dev, scp, iat, exp) are missing
  • Required scopes are not present in the token
from grantex import verify_grant_token, VerifyGrantTokenOptions, GrantexTokenError

try:
    grant = verify_grant_token(token, options)
except GrantexTokenError as e:
    print(f"Token verification failed: {e}")