NaviServer Programmable Web Server

ns_jwt(n)

NaviServer Built-in Commands – 1.0


[ Main Table Of Contents | Table Of Contents | Keyword Index ]

Name

ns_jwt - JSON Web Token support

Table Of Contents

Synopsis

Description

The command ns_jwt provides a Tcl-level interface for creating, decoding, and verifying JSON Web Tokens (JWTs) in compact serialization format.

The implementation is built on top of NaviServer's JSON and cryptographic facilities. It is intended as a convenience interface for common JWT workflows and hides most low-level details of key handling and signature verification.

The commands documented here operate on compact JWTs of the form header.payload.signature.

This command requires a NaviServer version built with OpenSSL support and NSF installed.

COMMANDS

ns_jwt encode -alg value ?-key value? ?-jwk value? ?-secret value? ?-kid value? ?-typ value? ?-cty value? ?-extraheader value? ?-iss value? ?-sub value? ?-aud value? ?-exp value? ?-nbf value? ?-iat value? ?-jti value? ?extrapayload?

Create a JSON Web Token (JWT) in compact serialization format.

The command builds a protected header and payload, encodes both as base64url, and signs the resulting input using the specified algorithm. The payload is constructed from standard JWT claims provided as named parameters and optional additional claims in triple form.

The option -alg specifies the signature algorithm (for example HS256, HS384, HS512, EdDSA, ES256, ES256K, ES384, ES512, RS256, RS384, or RS512). The set of available algorithms depends on the underlying OpenSSL version and provider configuration; the values listed above correspond to typical configurations based on OpenSSL 3.x.

The algorithms HS256, HS384, and HS512 use symmetric keys (shared secrets) and require the option -secret. All other algorithms use asymmetric keys and require -key. Support for signing via -jwk is reserved for future use and is not implemented yet.

When none is specified, no signature is added and the JWT ends with an empty signature component.

The option -key specifies the private key in PEM form. The value may be a PEM string or a PEM file name.

The option -jwk is reserved for future support of signing with private keys in JWK form and is currently not implemented.

The option -secret specifies the shared secret used for HMAC-based algorithms (HS256, HS384, HS512). This option is only valid for these algorithms.

The option -kid adds a key identifier to the protected header. The options -typ and -cty set the corresponding JWT header fields. The default for -typ is JWT.

The option -extraheader specifies additional protected header fields in JSON triples form (see ns_json), using the representation name type value ....

The options -iss, -sub, -aud, -exp, -nbf, -iat, and -jti set the corresponding registered JWT claims. The audience claim -aud may be either a single value or a Tcl list of values. A single value is encoded as a JSON string; multiple values are encoded as a JSON array.

The final argument extrapayload, when provided, contains additional payload fields in JSON triples form (see ns_json), using the representation name type value ....

The command returns the compact JWT string header.payload.signature.

 # Generate a PEM key containing the private key for demonstration purposes.
 set privpem [ns_crypto::key generate -name Ed25519]
 
 set token [ns_jwt encode \
                -alg EdDSA \
                -key $privpem \
                -kid ed1 \
                -iss naviserver \
                -sub alice \
                -aud myservice \
                -exp [expr {[clock seconds] + 300}] \
                {role string admin}]
ns_jwt decode token

Decode a JSON Web Token without performing signature verification.

The command splits the token into its three compact components, decodes the protected header and payload from base64url, and parses them as JSON.

The result is a Tcl dictionary containing the keys header, payload, and signature. The values of header and payload are parsed Tcl dictionaries. The value of signature is the raw binary signature. For unsigned tokens (alg=none), the signature value is empty.

 set decoded [ns_jwt decode $token]
 set header  [dict get $decoded header]
 set payload [dict get $decoded payload]
 
 dict get $header alg
 # returns the "alg" component of the JWT header
 
 dict get $payload sub
 # returns the "sub" component of the JWT payload
ns_jwt verify ?-alg value? ?-key value? ?-jwk value? ?-jwks value? ?-secret value? ?-kid value? ?-requirekid? ?-verifyclaims? ?-aud value? ?-iss value? ?-sub value? ?-clockskew value? ?-now value? token

Verify a JSON Web Token and optionally validate claims.

The command decodes the token, resolves a verification key, and verifies the signature according to the algorithm specified in the JWT header.

Verification keys may be provided in one of the following forms, depending on the algorithm:

  • -secret - shared secret for HMAC-based algorithms (HS256, HS384, HS512)

  • -key - public key in PEM form for asymmetric algorithms

  • -jwk - a single JWK for asymmetric algorithms

  • -jwks - a JWK set from which a key is selected

When -alg is specified, its value must match the alg field in the JWT header. When -kid is specified, its value must match the kid field in the JWT header. When -requirekid is used, the JWT header must contain a kid field.

Unsigned JWTs using alg=none are rejected by this command.

When -verifyclaims is specified, the command additionally validates registered claims. The options -aud, -iss, and -sub specify expected values for these claims. The option -clockskew specifies an allowed time skew in seconds for validating time-based claims such as exp, nbf, and iat. The option -now specifies the reference time as seconds since the epoch; when omitted, the current time is used.

On success, the command returns a Tcl dictionary containing:

  • valid - boolean value 1

  • header - parsed JWT header

  • payload - parsed JWT payload

  • kid - resolved key identifier, possibly empty

  • alg - algorithm used for verification

On failure, the command raises a Tcl error.

 # Derive the public key in PEM format from the private key and verify
 # the token.
 set pubpem [ns_crypto::key pub -pem $privpem -format pem]
 
 set verified [ns_jwt verify \
                   -key $pubpem \
                   -verifyclaims \
                   -iss naviserver \
                   -aud myservice \
                   $token]
 
 set payload [dict get $verified payload]
 dict get $payload sub
 # returns the "sub" component of the JWT payload

NOTES

The options -extraheader and extrapayload use the JSON triples representation as used by ns_json. This representation preserves JSON types precisely and avoids ambiguity between strings, numbers, booleans, null values, arrays, and objects.

Standard claims passed via dedicated options are encoded with their natural JSON types. In particular, -exp, -nbf, and -iat are encoded as numbers.

The set of supported algorithms depends on the cryptographic algorithms available through the underlying ns_crypto implementation and the OpenSSL version in use.

HMAC-based algorithms use symmetric keys. This means that the same secret is used for both signing and verification. All parties that verify such tokens must know the shared secret. For applications that require key separation or public-key distribution, asymmetric algorithms (e.g., RS256, ES256, EdDSA) should be preferred.

When using HMAC-based algorithms, the security of the token depends entirely on the secrecy of the shared key provided via -secret. While kid may still be present in the JWT header, key distribution and shared-secret management remain the responsibility of the application.

EXAMPLES

 # Create and verify an Ed25519-signed JWT
 
 set privpem [ns_crypto::key generate -name Ed25519]
 set pubpem  [ns_crypto::key pub -pem $privpem -format pem]
 
 set token [ns_jwt encode \
                -alg EdDSA \
                -key $privpem \
                -iss naviserver \
                -sub alice \
                {role string admin}]
 
 set result [ns_jwt verify -key $pubpem $token]
 dict get [dict get $result payload] role
 # Verify a token against a JWK
 
 set privpem [ns_crypto::key generate -name EC -group prime256v1]
 set info    [ns_crypto::key info -pem $privpem -encoding base64url]
 
 set jwk [dict create \
              kty EC \
              crv P-256 \
              x [dict get $info x] \
              y [dict get $info y]]
 
 set token [ns_jwt encode \
                -alg ES256 \
                -key $privpem \
                -sub alice]
 
 ns_jwt verify -jwk $jwk $token

See Also

ns_crypto, ns_json

Keywords

JSON, JWK, JWKS, JWT, Key, Token, Web, signature, verification