Skip to content

x.crypto.jwt #

x.crypto.jwt

x.crypto.jwt provides JSON Web Token (JWT) signing, verification, and decoding using the compact signed-token format from RFC 7519 and RFC 7515 (JWS), with algorithms from RFC 7518 (JWA) and RFC 8037 (EdDSA).

Supported Algorithms

  • HS256: HMAC using SHA-256 (symmetric)
  • HS384: HMAC using SHA-384 (symmetric)
  • HS512: HMAC using SHA-512 (symmetric)
  • EdDSA: Ed25519 signature algorithm (asymmetric)

In accordance with security best practices, none and arbitrary unapproved algorithms are rejected by default.

Every verification call requires VerifyOptions.algorithm. Choose this algorithm from trusted application configuration for the verification key. Do not derive it from the token header. For Ed25519 public keys, always select .eddsa; selecting an HMAC algorithm would allow anyone who knows the public key to forge tokens.

Registered timestamp claims (exp, nbf, iat) use signed 64-bit integer Unix seconds. Verification rejects malformed JSON and claims that cannot be decoded as StandardClaims, even when validate_exp or validate_nbf is disabled. Timestamp strings, fractional timestamps, null registered claims, and duplicate top-level members are rejected. Audience claims support a single string; audience arrays are not supported. Headers require an explicit alg. Unsupported crit and b64 extensions, padded or noncanonical Base64url segments, and negative leeway are rejected. Leeway must be a nonnegative number of seconds.

Usage

Signing and Verifying with StandardClaims

import time
import x.crypto.jwt

fn main() {
    secret := 'my-secret-key'

    claims := jwt.StandardClaims{
        sub: 'user-12345'
        iss: 'auth.example.com'
        aud: 'api.example.com'
        exp: time.now().unix() + 3600
    }

    // Sign token
    token := jwt.sign_with_secret(claims, secret, jwt.SignOptions{
        algorithm: .hs256
    })!
    println('Token: ${token}')

    // Verify token
    tok := jwt.verify_with_secret[jwt.StandardClaims](token, secret, jwt.VerifyOptions{
        algorithm:         .hs256
        expected_issuer:   'auth.example.com'
        expected_audience: 'api.example.com'
    })!
    println('Subject: ${tok.claims.sub or { '' }}')
}

Custom Claims Struct

You can embed StandardClaims into your own custom struct:

import time
import x.crypto.jwt

struct MyClaims {
    jwt.StandardClaims
pub mut:
    role    string
    user_id int
}

fn main() {
    secret := 'my-secret-key'

    claims := MyClaims{
        StandardClaims: jwt.StandardClaims{
            sub: 'user-1'
            exp: time.now().unix() + 1800
        }
        role:           'admin'
        user_id:        101
    }

    token := jwt.sign_with_secret(claims, secret, jwt.SignOptions{})!
    tok := jwt.verify_with_secret[MyClaims](token, secret, jwt.VerifyOptions{
        algorithm: .hs256
    })!

    println('User ID: ${tok.claims.user_id}, Role: ${tok.claims.role}')
}

Asymmetric Signatures (EdDSA / Ed25519)

import crypto.ed25519
import x.crypto.jwt

fn main() {
    pubkey, privkey := ed25519.generate_key()!

    claims := jwt.StandardClaims{
        sub: 'ed25519-user'
    }

    // Sign using Ed25519 private key
    token := jwt.sign(claims, privkey, jwt.SignOptions{
        algorithm: .eddsa
        kid:       'key-2026'
    })!

    // Verify using Ed25519 public key
    tok := jwt.verify[jwt.StandardClaims](token, pubkey, jwt.VerifyOptions{
        algorithm: .eddsa
    })!
    println('Verified subject: ${tok.claims.sub or { '' }}')
}

Inspecting Header or Claims Without Verifying

import x.crypto.jwt

fn main() {
    token := '...'
    header := jwt.decode_header(token)!
    println('Algorithm: ${header.alg}, Key ID: ${header.kid or { 'none' }}')

    tok := jwt.decode[jwt.StandardClaims](token)!
    println('Claimed subject: ${tok.claims.sub or { '' }}')
}

fn decode #

fn decode[T](token string) !Token[T]

decode decodes a token into Token[T] without verifying its signature.

fn decode_header #

fn decode_header(token string) !Header

decode_header parses the Header from a token string without verifying its signature.

fn decode_raw #

fn decode_raw(token string) !(Header, string)

decode_raw parses and base64url-decodes the header and payload JSON strings without verifying the signature.

fn parse_algorithm #

fn parse_algorithm(name string) !Algorithm

parse_algorithm parses a string representation of an algorithm into an Algorithm enum.

fn sign #

fn sign[T](claims T, key []u8, options SignOptions) !string

sign serializes claims to JSON, attaches a JOSE header, signs it using key, and returns the compact encoded JWT string (<header>.<payload>.<signature>).

fn sign_raw #

fn sign_raw(header Header, payload_json string, key []u8) !string

sign_raw signs an arbitrary raw JSON payload string using header and key.

fn sign_with_secret #

fn sign_with_secret[T](claims T, secret string, options SignOptions) !string

sign_with_secret is a convenience helper for signing with a string secret key.

fn verify #

fn verify[T](token string, key []u8, options VerifyOptions) !Token[T]

verify validates token signature, header, standard claims, and decodes claims into T. options.algorithm must specify the trusted algorithm associated with key.

fn verify_raw #

fn verify_raw(token string, key []u8, options VerifyOptions) !string

verify_raw verifies token structure, algorithm, signature, and standard claims, returning the decoded payload JSON string. options.algorithm must specify the trusted algorithm associated with key.

fn verify_with_secret #

fn verify_with_secret[T](token string, secret string, options VerifyOptions) !Token[T]

verify_with_secret is a convenience helper for verifying a token with a string secret key. options.algorithm must specify the trusted algorithm associated with secret.

fn Algorithm.from #

fn Algorithm.from[W](input W) !Algorithm

from returns the Algorithm value of an integer or of a field name, or an error if there is none.

enum Algorithm #

enum Algorithm {
	hs256
	hs384
	hs512
	eddsa
}

Algorithm represents the cryptographic signing algorithm used in the JWS/JWT.

fn (Algorithm) str #

fn (a Algorithm) str() string

str returns the standard string identifier (e.g. 'HS256', 'EdDSA') for the algorithm.

struct SignOptions #

struct SignOptions {
pub:
	algorithm Algorithm = .hs256
	kid       ?string
}

SignOptions provides configuration options for signing tokens.

struct StandardClaims #

struct StandardClaims {
pub mut:
	iss ?string // Issuer
	sub ?string // Subject
	aud ?string // Audience
	exp ?i64    // Expiration Time (Unix timestamp in seconds)
	nbf ?i64    // Not Before (Unix timestamp in seconds)
	iat ?i64    // Issued At (Unix timestamp in seconds)
	jti ?string // JWT ID
}

StandardClaims contains the standard registered claims defined in RFC 7519 section 4.1.

fn (StandardClaims) is_expired #

fn (c StandardClaims) is_expired(now i64) bool

is_expired returns true if exp is defined and now is at or past exp.

fn (StandardClaims) is_valid_at #

fn (c StandardClaims) is_valid_at(now i64) bool

is_valid_at checks if the claims are currently valid at timestamp now according to exp and nbf (with 0 leeway).

struct Token #

struct Token[T] {
pub:
	header    Header
	claims    T
	signature []u8
	raw       string
}

Token represents a parsed or verified JSON Web Token.

struct VerifyOptions #

struct VerifyOptions {
pub:
	algorithm         Algorithm @[required]
	validate_exp      bool = true
	validate_nbf      bool = true
	leeway            i64
	current_time      ?i64
	expected_issuer   ?string
	expected_audience ?string
	expected_subject  ?string
}

VerifyOptions provides configuration options for validating and verifying tokens. algorithm must be explicitly set to the trusted algorithm associated with the verification key.