jwt
| Kind | ffi-zig |
|---|---|
| Capabilities | ffi |
| Categories | authentication cryptography web |
| Keywords | jwt json-web-token authentication security token |
JSON Web Token (JWT) encoding and decoding for Kit
Files
| File | Description |
|---|---|
.editorconfig | Editor formatting configuration |
.gitignore | Git ignore rules for build artifacts and dependencies |
.tool-versions | asdf tool versions (Zig, Kit) |
LICENSE | MIT license file |
README.md | This file |
examples/basic.kit | Basic HS256 usage example |
kit.toml | Package manifest with metadata, capabilities, and dependencies |
src/jwt.kit | JWT encoding, decoding, verification, and helper API |
zig/jwt_rsa.zig | OpenSSL-backed RSA signing and verification bridge |
zig/kit_ffi.zig | Kit Zig FFI value helpers used by the RSA bridge |
tests/hs256.test.kit | Active HS256 behavior tests |
tests/types.test.kit | Error, type, algorithm, and claim-shape tests |
tests/rs256.kit.disabled | Optional RS256 behavior tests |
tests/rs384.kit.disabled | Optional RS384 behavior tests |
tests/rs512.kit.disabled | Optional RS512 behavior tests |
Dependencies
- Kit package dependency:
crypto - Kit standard module:
Encoding.Base64 - Native library: OpenSSL
libcryptofor RS256, RS384, and RS512 helpers - Capability required:
ffi
The package is declared as ffi-zig because RSA signing and verification use zig/jwt_rsa.zig. HS256 uses crypto.hmac-sha256.
Installation
kit add gitlab.com/kit-lang/packages/kit-jwt.gitUsage
import Kit.Jwt as JWT
main = fn =>
secret = "my-super-secret-key-at-least-32-chars"
claims = "{\"sub\":\"user123\",\"name\":\"John Doe\",\"admin\":true,\"iat\":1700000000}"
match JWT.encode claims secret
| Err e ->
println "Failed to create token:"
println e
| Ok token ->
println "JWT:"
println token
if JWT.verify? token secret then
println "Token is valid"
else
println "Token is invalid"
match JWT.decode token secret
| Ok decoded ->
println "Header:"
println decoded.header
println "Payload:"
println decoded.payload
| Err e ->
println "Decode failed:"
println e
mainSupported signing and verification helpers:
| Algorithm | Helpers |
|---|---|
| HS256 | encode, encode-with-header, decode, verify? |
| RS256 | encode-rs256, decode-rs256, verify-rs256?, get-claims-rs256 |
| RS384 | encode-rs384, decode-rs384, verify-rs384?, get-claims-rs384 |
| RS512 | encode-rs512, decode-rs512, verify-rs512?, get-claims-rs512 |
Unsafe inspection helpers are also available for debugging tokens without verifying signatures:
decode-unsafeget-claims-unsafeget-header-unsafe
Do not use unsafe helpers for authorization decisions.
Development
Running Examples
Run the basic example with the interpreter:
kit run examples/basic.kit --allow=ffiCompile the example to a native binary:
kit build examples/basic.kit --allow=ffi && ./basicRunning Tests
Run the active test suite:
kit test --allow=ffiRun the active test suite with coverage:
kit test --coverage --allow=ffiRun the optional RSA test files directly:
kit test tests/rs256.kit.disabled --allow=ffi
kit test tests/rs384.kit.disabled --allow=ffi
kit test tests/rs512.kit.disabled --allow=ffiRunning kit dev
Run the standard development workflow (format, check, test):
kit dev --no-spinnerThis will:
- Check formatting for Kit source and example files
- Type check source and examples
- Run active tests with coverage
Running Parity
Run interpreter/compiler parity checks for examples:
kit parity --no-spinner --failures-onlyParity checks that examples run through the interpreter, compile successfully, execute successfully, and produce matching output.
Generating Documentation
Generate API documentation from doc comments:
kit doc src/jwt.kitNote: Kit sources with doc comments (##) generate HTML documentation.
Cleaning Build Artifacts
Remove generated files, caches, and build artifacts:
kit task cleanNote: Defined in kit.toml.
Local Installation
To install this package locally for development:
kit installThis installs the package to ~/.kit/packages/@kit/jwt/, making it available for import as Kit.Jwt in other projects.
Security Notes
- Use strong, random HS256 secrets. A minimum of 256 bits is recommended.
- Never commit private keys or production JWT secrets.
- Validate application claims such as
exp,nbf,iat,iss, andaudafter decoding. - Prefer short-lived tokens and rotate keys according to your application's threat model.
- Use HTTPS whenever transmitting JWTs over a network.
License
This package is released under the MIT License - see LICENSE for details.
Exported Functions & Types
JWTError
JWT error type with specific variants for different failure modes.
Variants
JWTEncodeError {message}JWTDecodeError {message}encode
Create a JWT token from claims using HS256 algorithm
Encodes the provided claims as a JWT token signed with HMAC-SHA256. The header is automatically set to {"alg":"HS256","typ":"JWT"}.
Parameters:
- claims-json- String - The claims/payload as a JSON string- secret- String - The secret key used for signing (should be strong and random)
Returns: JwtResult String - Either:- Ok token: The complete JWT token (header.payload.signature)- Err msg: Should not occur for encoding, but included for consistency
String -> NonEmptyString -> Result String JWTError
claims = "{\"sub\":\"user123\",\"name\":\"Alice\",\"exp\":1735689600}"
token-result = encode claims "my-secret-key"
match token-result
| Ok token -> IO.println "JWT: ${token}"
| Err msg -> IO.println "Error: ${msg}"
Security Notes:
- Use a strong secret (minimum 256 bits for HS256)
- Include standard claims like "exp" (expiration) and "iat" (issued at)
- Never expose the secret in client-side code or version controlencode-with-header
Create a JWT token with custom header fields
Similar to encode, but allows specifying custom header fields instead of using the default HS256 header. Useful for adding additional header claims like "kid" (key ID) or other metadata.
Parameters:
- header-json- String - The header as a JSON string (must include "alg" and "typ")- claims-json- String - The claims/payload as a JSON string- secret- String - The secret key used for signing
Returns: JwtResult String - Either:- Ok token: The complete JWT token with custom header- Err msg: Should not occur for encoding, but included for consistency
String -> String -> NonEmptyString -> Result String JWTError
header = "{\"alg\":\"HS256\",\"typ\":\"JWT\",\"kid\":\"key-2025-01\"}"
claims = "{\"sub\":\"user123\",\"role\":\"admin\"}"
token-result = encode-with-header header claims "my-secret-key"
Note:
- The algorithm specified in the header must be HS256 (only supported algorithm)
- Ensure the header is valid JSON and includes required fieldsdecode
Decode and verify a JWT token
Returns:
String -> NonEmptyString -> Result {header: String, payload: String} JWTError
decode-unsafe
Decode a JWT without verifying the signature (UNSAFE - for debugging only)
String -> Result {header: String, payload: String} JWTError
verify?
Verify a JWT token is valid (returns Bool)
String -> NonEmptyString -> Bool
get-claims-unsafe
Get the payload (claims) as JSON string without verification
String -> Option String
get-header-unsafe
Get the header as JSON string without verification
String -> Option String
get-claims
Get claims after verification
String -> NonEmptyString -> Option String
get-header
Get header after verification
String -> NonEmptyString -> Option String
encode-rs256
Create a JWT token from claims using RS256 algorithm (RSA-SHA256)
Encodes the provided claims as a JWT token signed with RSA-SHA256. The header is automatically set to {"alg":"RS256","typ":"JWT"}.
This is the standard algorithm used for service account authentication with Google Cloud Platform and other cloud providers.
Parameters:
- claims-json- String - The claims/payload as a JSON string- private-key-pem- String - RSA private key in PEM format
Returns: Result String JWTError:- Ok token: The complete JWT token (header.payload.signature)- Err JWTError: Error with details about what went wrong
String -> NonEmptyString -> Result String JWTError
claims = "{\"iss\":\"sa@project.iam.gserviceaccount.com\",\"aud\":\"https://oauth2.googleapis.com/token\",\"exp\":1735689600}"
pem-key = File.read "service-account-key.pem"
match encode-rs256 claims pem-key
| Ok token -> print "JWT: ${token}"
| Err e -> print "Error: ${Show.show e}"
Security Notes:
- Never expose the private key in client-side code or version control
- Use appropriate key sizes (2048 bits minimum, 4096 recommended)
- Include standard claims like "exp" (expiration) and "iat" (issued at)encode-rs384
Create a JWT token from claims using RS384 algorithm (RSA-SHA384)
Similar to encode-rs256 but uses SHA-384 for the hash function, providing a higher security margin.
Parameters:
- claims-json- String - The claims/payload as a JSON string- private-key-pem- String - RSA private key in PEM format
Returns: Result String JWTError:- Ok token: The complete JWT token (header.payload.signature)- Err JWTError: Error with details about what went wrong
String -> NonEmptyString -> Result String JWTError
encode-rs512
Create a JWT token from claims using RS512 algorithm (RSA-SHA512)
Similar to encode-rs256 but uses SHA-512 for the hash function, providing the highest security margin among RS* algorithms.
Parameters:
- claims-json- String - The claims/payload as a JSON string- private-key-pem- String - RSA private key in PEM format
Returns: Result String JWTError:- Ok token: The complete JWT token (header.payload.signature)- Err JWTError: Error with details about what went wrong
String -> NonEmptyString -> Result String JWTError
decode-rs256
Decode and verify a JWT token signed with RS256 (RSA-SHA256)
Verifies the signature using the provided RSA public key and returns the decoded header and payload if valid.
Parameters:
- token- String - The JWT token to decode- public-key-pem- String - RSA public key in PEM format
Returns: Result {header: String, payload: String} JWTError:- Ok {header, payload}: Decoded header and payload as JSON strings- Err JWTError: Invalid token format or signature verification failed
String -> NonEmptyString -> Result {header: String, payload: String} JWTError
public-key = File.read "public-key.pem"
match decode-rs256 token public-key
| Ok decoded -> print "Claims: ${decoded.payload}"
| Err e -> print "Error: ${Show.show e}"decode-rs384
Decode and verify a JWT token signed with RS384 (RSA-SHA384)
Parameters:
- token- String - The JWT token to decode- public-key-pem- String - RSA public key in PEM format
Returns: Result {header: String, payload: String} JWTError
String -> NonEmptyString -> Result {header: String, payload: String} JWTError
decode-rs512
Decode and verify a JWT token signed with RS512 (RSA-SHA512)
Parameters:
- token- String - The JWT token to decode- public-key-pem- String - RSA public key in PEM format
Returns: Result {header: String, payload: String} JWTError
String -> NonEmptyString -> Result {header: String, payload: String} JWTError
verify-rs256?
Verify a JWT token signed with RS256 is valid (returns Bool)
Parameters:
- token- String - The JWT token to verify- public-key-pem- String - RSA public key in PEM format
Returns: Bool - true if the token is valid, false otherwise
String -> NonEmptyString -> Bool
verify-rs384?
Verify a JWT token signed with RS384 is valid (returns Bool)
String -> NonEmptyString -> Bool
verify-rs512?
Verify a JWT token signed with RS512 is valid (returns Bool)
String -> NonEmptyString -> Bool
get-claims-rs256
Get claims from an RS256-signed token after verification
Parameters:
- token- String - The JWT token- public-key-pem- String - RSA public key in PEM format
Returns: Option String - Some claims JSON if valid, None otherwise
String -> NonEmptyString -> Option String
get-claims-rs384
Get claims from an RS384-signed token after verification
String -> NonEmptyString -> Option String
get-claims-rs512
Get claims from an RS512-signed token after verification
String -> NonEmptyString -> Option String