moonbit_jwt

    A MoonBit library for JSON Web Token (JWT) creation and verification. Pure MoonBit SHA256 + HMAC implementation, no FFI.

    jwt
    json-web-token
    auth
    hmac
    sha256
    Download zip
    Version
    0.1.0
    License
    MIT
    Last updated
    last month
    Downloads
    6

    #MoonBit JWT

    CI MoonBit License

    A pure MoonBit library for JSON Web Tokens (JWT).

    Create and verify JWT tokens with HMAC-SHA256/384/512. Includes from-scratch SHA-256, SHA-384, SHA-512, and HMAC implementations — no FFI, no external dependencies, runs on all MoonBit targets.

    #Quick Start

    import {
    "123123213weqw/moonbit_jwt" @jwt,
    }

    fn main {
    // Build claims
    let claims = @jwt.ClaimsBuilder::new()
    |> @jwt.ClaimsBuilder::issuer("my-app")
    |> @jwt.ClaimsBuilder::subject("user123")
    |> @jwt.ClaimsBuilder::expires_at(2000000000)
    |> @jwt.ClaimsBuilder::to_json_string()

    // Sign with HS256
    let token = @jwt.sign_hs256(@jwt.JwtHeader::hs256(), claims, "secret".to_bytes())

    // Validate with full options
    let opts = @jwt.ValidationOptions::new(1000000000)
    |> @jwt.ValidationOptions::with_issuer("my-app")
    |> @jwt.ValidationOptions::with_leeway(30)
    match @jwt.validate(token, "secret".to_bytes(), opts) {
    Ok(_) => println("Valid!")
    Err(_) => println("Invalid!")
    }
    }

    #Modules

    ModuleLinesDescription
    sha256.mbt170SHA-256 (FIPS 180-4), streaming + one-shot
    sha512.mbt241SHA-512 + SHA-384 (truncated SHA-512)
    hmac.mbt30HMAC-SHA256 (RFC 2104)
    hmac_full.mbt61HMAC-SHA384 + HMAC-SHA512
    base64url.mbt128Base64 URL-safe encode/decode (RFC 4648)
    jwt.mbt143JWT types, sign HS256, verify, decode_claims
    jwt_full.mbt118HS384/HS512 sign + verify_with_alg
    claims.mbt187ClaimsBuilder for structured claim construction
    validator.mbt191Full validation (exp/nbf/iss/aud/sub + leeway)
    decoder.mbt144Full token decode with header/claims extraction
    Total~1900

    #Algorithms

    AlgorithmHashStatus
    HS256HMAC-SHA256
    HS384HMAC-SHA384
    HS512HMAC-SHA512

    #Test Vectors Verified

    • SHA256("") = e3b0c442...
    • SHA256("abc") = ba7816bf...
    • SHA512("abc") = ddaf35a1...
    • SHA384("abc") = cb00753f...
    • HMAC-SHA256("key", "The quick brown fox...") = f7bc83f4...
    • JWT sign → verify roundtrip (HS256/384/512) ✅
    • Token expiration / not-before / issuer / audience validation ✅
    • Tampered token rejection ✅

    36 tests, all passing on wasm/wasm-gc/js/native.

    #Development

    moon fmt --check moon check --deny-warn moon test --deny-warn moon test --target all moon info

    #License

    MIT

    Algorithm

    pub(all) enum Algorithm {
    HS256
    HS384
    HS512
    } derive(Eq,
    Debug
    )

    JWT signing algorithm.

    Algorithm::to_str

    fn Algorithm::to_str(self : Algorithm) -> String

    Convert algorithm to its JOSE name string.

    ClaimsBuilder

    pub(all) struct ClaimsBuilder {
    claims : Map[String, Json]
    }

    A builder for constructing JWT claims as a JSON object.

    Standard claims (iss, sub, aud, exp, nbf, iat, jti) have dedicated methods; custom claims can be added with [ClaimsBuilder::set].

    ClaimsBuilder::audience

    fn ClaimsBuilder::audience(self : ClaimsBuilder, aud : String) -> ClaimsBuilder

    Set the audience claim (intended recipient).

    ClaimsBuilder::contains

    fn ClaimsBuilder::contains(self : ClaimsBuilder, key : String) -> Bool

    Check if a claim exists.

    ClaimsBuilder::expires_at

    fn ClaimsBuilder::expires_at(self : ClaimsBuilder, exp : Int) -> ClaimsBuilder

    Set the expiration time claim (NumericDate: seconds since epoch).

    ClaimsBuilder::get

    fn ClaimsBuilder::get(self : ClaimsBuilder, key : String) -> Json?

    Get a claim value.

    ClaimsBuilder::issued_at

    fn ClaimsBuilder::issued_at(self : ClaimsBuilder, iat : Int) -> ClaimsBuilder

    Set the issued-at claim (when the token was issued).

    ClaimsBuilder::issuer

    fn ClaimsBuilder::issuer(self : ClaimsBuilder, iss : String) -> ClaimsBuilder

    Set the issuer claim (who issued the token).

    ClaimsBuilder::jwt_id

    fn ClaimsBuilder::jwt_id(self : ClaimsBuilder, jti : String) -> ClaimsBuilder

    Set the JWT ID claim (unique identifier for the token).

    ClaimsBuilder::new

    Create a new empty claims builder.

    ClaimsBuilder::not_before

    fn ClaimsBuilder::not_before(self : ClaimsBuilder, nbf : Int) -> ClaimsBuilder

    Set the not-before claim (token not valid before this time).

    ClaimsBuilder::remove

    fn ClaimsBuilder::remove(self : ClaimsBuilder, key : String) -> ClaimsBuilder

    Remove a claim by key.

    ClaimsBuilder::set

    fn ClaimsBuilder::set(self : ClaimsBuilder, key : String, value : Json) -> ClaimsBuilder

    Set a custom claim from a raw JSON value.

    ClaimsBuilder::set_bool

    fn ClaimsBuilder::set_bool(self : ClaimsBuilder, key : String, value : Bool) -> ClaimsBuilder

    Set a custom boolean claim.

    ClaimsBuilder::set_int

    fn ClaimsBuilder::set_int(self : ClaimsBuilder, key : String, value : Int) -> ClaimsBuilder

    Set a custom integer claim.

    ClaimsBuilder::set_string

    fn ClaimsBuilder::set_string(self : ClaimsBuilder, key : String, value : String) -> ClaimsBuilder

    Set a custom string claim.

    ClaimsBuilder::subject

    fn ClaimsBuilder::subject(self : ClaimsBuilder, sub : String) -> ClaimsBuilder

    Set the subject claim (who the token is about).

    ClaimsBuilder::to_json

    fn ClaimsBuilder::to_json(self : ClaimsBuilder) -> Json

    Build the claims as a JSON value.

    ClaimsBuilder::to_json_string

    fn ClaimsBuilder::to_json_string(self : ClaimsBuilder) -> String

    Build the claims as a JSON string.

    DecodedToken

    pub(all) struct DecodedToken {
    header : Json
    claims : Json
    signature : Bytes
    raw_header : String
    raw_claims : String
    raw_signature : String
    }

    Decoded JWT token parts.

    DecodedToken::algorithm

    fn DecodedToken::algorithm(self : DecodedToken) -> Result[Algorithm, JwtError]

    Extract the algorithm from a decoded token's header.

    DecodedToken::get_numeric_claim

    fn DecodedToken::get_numeric_claim(self : DecodedToken, key : String) -> Int?

    Get a numeric claim (as Int) from the decoded token.

    DecodedToken::get_string_claim

    fn DecodedToken::get_string_claim(self : DecodedToken, key : String) -> String?

    Get a string claim from the decoded token.

    DecodedToken::key_id

    fn DecodedToken::key_id(self : DecodedToken) -> String?

    Extract the key ID (kid) from a decoded token's header.

    DecodedToken::token_type

    fn DecodedToken::token_type(self : DecodedToken) -> String?

    Extract the token type (typ) from a decoded token's header.

    JwtError

    pub(all) enum JwtError {
    InvalidToken(String)
    InvalidSignature
    InvalidAlgorithm(String)
    InvalidHeader(String)
    InvalidClaims(String)
    TokenExpired
    } derive(Eq,
    Debug
    )

    JWT error type.

    JwtHeader

    pub(all) struct JwtHeader {
    alg : Algorithm
    typ : String
    kid : String?
    } derive(Eq,
    Debug
    )

    JWT header structure.

    JwtHeader::hs256

    fn JwtHeader::hs256() -> JwtHeader

    Default header for HS256.

    JwtHeader::hs384

    fn JwtHeader::hs384() -> JwtHeader

    Create a JwtHeader for HS384.

    JwtHeader::hs512

    fn JwtHeader::hs512() -> JwtHeader

    Create a JwtHeader for HS512.

    Sha256

    pub(all) struct Sha256 {
    h : Array[UInt]
    buffer : Array[Byte]
    total_len : UInt64
    }

    SHA-256 state.

    Sha256::finalize

    fn Sha256::finalize(self : Sha256) -> Bytes

    Finalize and return the 32-byte digest.

    Sha256::new

    fn Sha256::new() -> Sha256

    Create a new SHA-256 hasher.

    Sha256::write

    fn Sha256::write(self : Sha256, data : Bytes) -> Unit

    Feed data into the hasher.

    Sha384

    pub(all) struct Sha384 {
    inner : Sha512
    }

    SHA-384 state (uses SHA-512 internally with different IV).

    Sha384::finalize

    fn Sha384::finalize(self : Sha384) -> Bytes

    Sha384::new

    fn Sha384::new() -> Sha384

    Sha384::write

    fn Sha384::write(self : Sha384, data : Bytes) -> Unit

    Sha512

    pub(all) struct Sha512 {
    h : Array[UInt64]
    buffer : Array[Byte]
    total_len : UInt64
    }

    SHA-512 state.

    Sha512::finalize

    fn Sha512::finalize(self : Sha512) -> Bytes

    Finalize and return the 64-byte digest.

    Sha512::new

    fn Sha512::new() -> Sha512

    Create a new SHA-512 hasher.

    Sha512::write

    fn Sha512::write(self : Sha512, data : Bytes) -> Unit

    Feed data into the hasher.

    ValidationOptions

    pub(all) struct ValidationOptions {
    expected_issuer : String?
    expected_audience : String?
    expected_subject : String?
    now_time : Int
    leeway : Int
    }

    Validation options for JWT verification.

    ValidationOptions::new

    fn ValidationOptions::new(now : Int) -> ValidationOptions

    Default validation options: no issuer/audience check, leeway=0.

    ValidationOptions::with_audience

    fn ValidationOptions::with_audience(self : ValidationOptions, aud : String) -> ValidationOptions

    Set expected audience.

    ValidationOptions::with_issuer

    fn ValidationOptions::with_issuer(self : ValidationOptions, iss : String) -> ValidationOptions

    Set expected issuer.

    ValidationOptions::with_leeway

    fn ValidationOptions::with_leeway(self : ValidationOptions, seconds : Int) -> ValidationOptions

    Set clock skew leeway in seconds.

    base64url_decode

    fn base64url_decode(s : String) -> Bytes?

    Decode a Base64URL string to bytes.

    base64url_encode

    fn base64url_encode(data : Bytes) -> String

    Encode bytes to a Base64URL string (no padding).

    base64url_encode_str

    fn base64url_encode_str(s : String) -> String

    Encode a string to Base64URL.

    claim_aud

    let claim_aud : String

    claim_exp

    let claim_exp : String

    claim_iat

    let claim_iat : String

    claim_iss

    let claim_iss : String

    Standard JWT claim keys (RFC 7519 §4).

    claim_jti

    let claim_jti : String

    claim_nbf

    let claim_nbf : String

    claim_sub

    let claim_sub : String

    decode

    fn decode(token : String) -> Result[DecodedToken, JwtError]

    Fully decode a JWT token into its parts (without verifying signature).

    Returns the parsed header JSON, claims JSON, and raw signature bytes.

    decode_claims

    fn decode_claims(token : String) -> Result[Json, JwtError]

    Decode the claims payload from a JWT token (without verifying signature).

    hmac_sha256

    fn hmac_sha256(key : Bytes, message : Bytes) -> Bytes

    Compute HMAC-SHA256 (RFC 2104) and return raw 32-byte digest.

    hmac_sha384

    fn hmac_sha384(key : Bytes, message : Bytes) -> Bytes

    Compute HMAC-SHA384 (RFC 2104) and return raw 48-byte digest.

    hmac_sha512

    fn hmac_sha512(key : Bytes, message : Bytes) -> Bytes

    Compute HMAC-SHA512 (RFC 2104) and return raw 64-byte digest.

    is_expired

    fn is_expired(token : String, now : Int, leeway : Int) -> Bool

    Check if a token is expired without verifying signature.

    sha256

    fn sha256(data : Bytes) -> Bytes

    One-shot SHA-256 hash of a byte string.

    sha256_hex

    fn sha256_hex(data : String) -> String

    One-shot SHA-256 hash of a MoonBit String, returned as hex.

    sha384

    fn sha384(data : Bytes) -> Bytes

    sha384_hex

    fn sha384_hex(data : String) -> String

    SHA-384 hex string output.

    sha512

    fn sha512(data : Bytes) -> Bytes

    One-shot SHA-512 hash.

    sha512_hex

    fn sha512_hex(data : String) -> String

    SHA-512 hex string output.

    sign

    fn sign(_alg : Algorithm, header_json : String, claims_json : String, secret : Bytes) -> String

    Sign a JWT token with HS256 (or HS384/HS512).

    header_json and claims_json should be pre-built JSON strings.

    sign_hs256

    fn sign_hs256(header : JwtHeader, claims_json : String, secret : Bytes) -> String

    Convenience: sign HS256 with a string secret and pre-made header + claims JSON.

    sign_hs384

    fn sign_hs384(header : JwtHeader, claims_json : String, secret : Bytes) -> String

    Sign HS384 with a JwtHeader.

    sign_hs512

    fn sign_hs512(header : JwtHeader, claims_json : String, secret : Bytes) -> String

    Sign HS512 with a JwtHeader.

    sign_with_alg

    fn sign_with_alg(alg : Algorithm, header_json : String, claims_json : String, secret : Bytes) -> String

    Sign a JWT token with the specified HMAC algorithm.

    Automatically selects the correct HMAC function based on the algorithm.

    validate

    fn validate(token : String, secret : Bytes, options : ValidationOptions) -> Result[Json, JwtError]

    Validate a token's signature AND claims according to [ValidationOptions].

    This is the high-level entry point: it verifies the HMAC signature, then checks exp, nbf, iss, aud, sub claims as configured.

    validate_with_expiry

    fn validate_with_expiry(token : String, secret : Bytes, now : Int) -> Result[Json, JwtError]

    Convenience: validate with only signature + expiration check.

    verify

    fn verify(token : String, secret : Bytes) -> Result[Unit, JwtError]

    Verify a JWT token's signature. Returns Ok(()) if valid, Err otherwise.

    verify_with_alg

    fn verify_with_alg(token : String, secret : Bytes) -> Result[Algorithm, JwtError]

    Verify a token and return the algorithm used.

    This decodes the header to determine which HMAC variant was used, then verifies with the corresponding function.