require "jwt" # gem install jwt -v 3.3.0require "openssl" private_key = OpenSSL::PKey.read(File.read("key.pem")) # an OpenSSL::PKey::EC, not the PEM string now = Time.now.to_ipayload = { iss: "https://issuer.example.com", sub: "user-42", aud: "api://orders", iat: now, exp: now + 15 * 60}puts JWT.encode(payload, private_key, "ES256", { kid: "2026-10", typ: "JWT" })How it works
OpenSSL::PKey.readloads the PEM (PKCS#8 orEC PRIVATE KEY) into anOpenSSL::PKey::EC.JWT.encode(payload, key, "ES256", header_fields)signs; the fourth argument addskidandtypto the header.Time.now.to_igives whole seconds foriatandexp.- The return value is the compact token string.
Gotchas
- The key must be a key object: passing the PEM string raises
JWT::EncodeError: The given key is a String. It has to be an OpenSSL::PKey::EC instance. - The gem does not add
typunless asked — the header is{"kid":…,"alg":"ES256"}without it. Passtyp: "JWT"if consumers check it. - The curve is checked: a P-384 key raises
JWT::EncodeError: payload algorithm is ES256 but ES384 signing key was provided. - Use symbol or string keys consistently in the payload:
{ exp: 1, "exp" => 2 }is two hash keys, and the token carries{"exp":1,"exp":2}— JSON with a duplicate member, which verifiers resolve differently.