Overview
verifyGrantToken() is a standalone function that verifies signatures and claims
locally after retrieving the published JWKS (JSON Web Key Set). It validates the
RS256 or ES256 signature, issuer, expiry, and optional scopes and audience. The current
standalone helper resolves the remote JWKS on each invocation, so it is not a
network-free hot path.
Use it when signature-and-claim validation without a revocation lookup is the
right trade-off. It avoids POST /v1/tokens/verify, but the JWKS endpoint must be
reachable for each standalone helper call.
The standalone helper may contact the JWKS endpoint when it resolves keys.
Keep that endpoint reachable from the verifying service; token validation
itself is performed locally and does not call
POST /v1/tokens/verify.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 issuerDid explicitly.Import
Grantex client instance.
Parameters
string
required
The grant token JWT string to verify.
VerifyGrantTokenOptions
required
Verification options.
VerifyGrantTokenOptions
string
required
The JWKS endpoint URL. For the hosted service, use
https://api.grantex.dev/.well-known/jwks.json.string[]
If provided, the function throws
GrantexTokenError when the token is missing any of these scopes.string
Expected
aud claim. If provided, verification fails when the token’s audience does not match.string
Expected
iss claim. The hosted JWKS alias automatically expects
https://grantex.dev; custom JWKS URLs derive the issuer from their URL when
this option is omitted.string | null
A
did:web issuer identifier. When provided, the JWKS is read from
https://<host>[:<port>][/<path>]/.well-known/jwks.json instead of jwksUri,
and the expected issuer is that location without the suffix. An explicit
issuer still takes precedence. With boundedJwksFetch: true it must name a
public domain; see DID issuers. null, like leaving it out,
means no DID, as issuer_did=None does in the Python SDK.boolean
Apply the JWKS fetch limits and the
DID issuer checks. Defaults to
false in these releases; a
later major release may make true the default, with false as the opt-out.
Available in @grantex/sdk@0.7.1.number
Optional clock-skew tolerance, in seconds, for time-based JWT claims.
string
Thumbprint of the key the caller proved possession of (for example with a verified DPoP
proof). The token’s
cnf.jkt must match it. The verifier does not check DPoP proofs.boolean
Fail closed unless
proofJkt is given and matches cnf.jkt. Without it, cnf is returned but not enforced.boolean
Read legacy claim aliases (
agt, dev, grnt, scp, parentAgt, parentGrnt,
delegationDepth) when the standard claim is absent, emitting a DeprecationWarning
(code GRANTEX_LEGACY_CLAIM) once per alias. Defaults to true in 0.6 and false in 0.7.
With false, only standard claims are read and typ must be at+jwt. A token whose
standard claim and alias disagree is always rejected.Array<'RS256' | 'ES256'>
Narrow the accepted signature algorithms. The default is
GRANT_TOKEN_ALGORITHMS
(['RS256', 'ES256']); any other value throws GrantexTokenError. The key is the
JWK Set entry named by kid whose type matches the algorithm (RSA for RS256, EC
P-256 for ES256); alg: none and HS256 are always rejected.JWKS fetch limits
@grantex/sdk@0.7.1 includes these limits and the DID issuer
checks when boundedJwksFetch: true is set. The option remains off by
default; choose it explicitly when verifying untrusted remote issuers.boundedJwksFetch: true, each fetch is bounded, with the same limits as the
Python SDK’s bounded_jwks_fetch=True:
A refused fetch throws
GrantexTokenError naming the endpoint and the reason,
and nothing is cached, so the next verification fetches again. JOSE’s cache
and refresh behaviour is unchanged. The key-count limit leaves room for the
grantex-YYYY-MM aliases a self-hosted auth service publishes
(JWT_LEGACY_KID_MONTHS). Decision-grant verification (verifyDecisionGrant,
verifyDecisionGrants) with jwksUri and boundedJwksFetch: true reads keys
within the same limits.
Without the option, the key set is read as in earlier releases, with JOSE’s
createRemoteJWKSet defaults: an HTTP 200 of any size, media type and number
of keys, within JOSE’s 5-second timeout. Bounded and unbounded key sets for the
same URL are cached separately.
DID issuers
WithboundedJwksFetch: true, issuerDid 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 throws
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 jwksUri. For a local or private issuer, set
jwksUri and issuer instead. The Python SDK applies the same rules with
bounded_jwks_fetch=True.
Without the option, issuerDid is read as in earlier releases: a value that
does not start with did:web: is ignored in favour of jwksUri, 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).
Response: VerifiedGrant
string
Unique token ID (the
jti JWT claim).string
The grant record ID (from the
grnt claim, falls back to jti).string
The end-user who authorized the agent (the
sub claim).string
The agent’s decentralized identifier (the
agt claim).string
The developer organization that owns the agent (the
dev claim).string[]
The scopes granted to the agent (the
scp claim).number
Token issued-at timestamp in seconds since the Unix epoch.
number
Token expiry timestamp in seconds since the Unix epoch.
WebAuthnGrantEvidence | undefined
Signed issuer reference to the original passkey assertion, when portable
evidence was captured. This does not replace online revocation checks or
independent verification of the opt-in VC’s assertion.
string
The parent agent’s DID, present only for delegated grants.
string
The parent grant ID, present only for delegated grants.
number
The delegation depth (
0 = root grant, 1 = first-level delegation, etc.). Present only for delegated grants.Error handling
verifyGrantToken() throws GrantexTokenError in the following cases:
- The JWT signature is invalid
- The token uses an algorithm other than RS256 or ES256, or names a key of the wrong type
- The token has expired
- The token issuer does not match the expected issuer
- Required claims (
jti,sub,agt,dev,scp,iat,exp) are missing - The token is missing one or more
requiredScopes - The
audiencedoes not match - With
boundedJwksFetch: true, the JWKS response exceeds a fetch limit: not200, not JSON, larger than 64 KiB, more than 128 keys, or slower than 5000 ms - With
boundedJwksFetch: true,issuerDidis not a usable did:web issuer
JWT claims mapping
The following table shows how JWT claims map toVerifiedGrant fields: