Signing (9 functions)
xjwtSign config-style
Full-control signing: injects iss/aud/sub/jti/exp/iat and the header kid (iat always). The header is built via JSON serialization (kid auto-escaped, no fixed-length limit); signature output capacity is checked (covers RSA-8192). Note: modifies the claims object in place.
xjwtconfig cfg;
xjwtConfigInit(&cfg);
cfg.Alg = XJWT_ALG_RS256;
cfg.KeyPem = sPrivateKeyPem; /* loaded at startup */
cfg.KeyId = "web-2026-09";
cfg.Issuer = "https://myapp.example.com";
cfg.Audience = "myapp-web";
cfg.ExpireSeconds = 900;
xvalue* claims = xrtValueObject();
xrtValueObjectSetNew(claims, xrtStrView("scope"),
xrtValueString(xrtStrView("profile orders")));
char* sToken = xjwtSign(&cfg, claims); /* free with xrtFree */
xrtValueRelease(claims);
xjwtHs256 / Hs384 / Hs512 / Rs256 / Rs384 / Rs512 / Es256 one-shot
Seven one-shot signers (claims + key + lifetime in seconds). This family injects no iss/aud — when the verifier's xjwtcheck checks Issuer/Audience, use xjwtSign instead. HS takes a key as a C string (embedded '\0' truncates; no binary keys); RS takes a PKCS#8/PKCS#1 private-key PEM; ES256 takes a SEC1/PKCS#8 private-key PEM.
xjwtConfigInit init
Zeroes the config and defaults to HS256. KeyId ≥ 256 characters is rejected at signing (matching the verifier-side cap).
Verification (4 functions)
xjwtVerify PEM path
One-step verify: header alg decode → claims checks (non-integer exp/nbf rejected; iss/aud constant-time; aud accepts strings and arrays) → signature. HS takes a key string, RS/ES a public-key PEM; key type is cross-checked against the algorithm family (anti-confusion). Returns claims on success (release with xrtValueRelease), NULL with an error code on failure.
xjwtcheck check;
xjwtCheckInit(&check);
check.Issuer = "https://myapp.example.com";
check.Audience = "myapp-web";
check.ClockLeeway = 30;
xvalue* claims = xjwtVerify(sBearerToken, sPublicKeyPem, &check);
if ( claims == NULL ) {
if ( xjwtLastError() == XJWT_ERROR_EXPIRED )
return 401; /* expired */
return 401; /* tampered / confused / bad signature */
}
char aUser[64];
xjwtClaimString(claims, "sub", aUser, sizeof(aUser));
xrtValueRelease(claims);
xjwtKeyParse / xjwtKeyFree / xjwtVerifyKey key cache
High-frequency verification: parse the public key once (RSA SPKI/PKCS#1 or EC P-256 SPKI; private keys and certificates rejected); the cache object is read-only and shareable across threads. VerifyKey rejects the HS family and cross-checks algorithm family against key type. Heap-pointer pattern — the natural fit for the script layer.
JWKS (3 functions)
xjwtJwksParse / xjwtJwksFree key set
Parse a JWKS (RFC 7517) JSON document: RSA n/e and EC x/y (P-256 only; other curves skipped; coordinates must be exactly 32 bytes). At most 16 keys — more rejects the whole set; malformed entries (e.g. n without e) are freed immediately, nothing lingers.
xjwtVerifyJwks keyed verify
Selects a key by strict kid match against the token header (no kid → first key; kid ≥ 256 rejected everywhere). Explicitly rejects the HS family; RS* ↔ RSA and ES256 ↔ EC type cross-checks. Rotation retry: on xjwtLastError() == XJWT_ERROR_KEY_NOT_FOUND, refetch the JWKS and verify once more.
Claims & errors (3 functions)
xjwtClaimsValid manual flow
Standalone claims check (exp/nbf/iss/aud): must be a JSON object; exp/nbf present-but-non-integer is always rejected; aud accepts strings and arrays (any match wins).
xjwtClaimString extraction
Safe string-claim extraction: truncates to cap-1 and NUL-terminates; returns false when the claim is missing or not a string.
xjwtLastError error read
Returns the xjwt-domain error code of the current execution context (XJWT_ERROR_*), 0 when the current error belongs to another domain. Read immediately after a failure.
Decoding & utilities (5 functions)
xjwtDecode / xjwtDecodeHeader decode
Verification-free decoding (trusted sources / debugging): the former returns claims and can report the header alg; the latter returns the header object and can report alg + kid (kid is a heap copy, free with xrtFree). Claims/header must be JSON objects; with pKid, a kid ≥ 256 rejects the whole call.
xjwtCheckInit / xjwtAlgName / xjwtAlgParse utilities
Zeroes the check struct (NowOverride injects a test clock, ClockLeeway is the tolerance in seconds); converts between the algorithm enum and its name.