A Gleam implementation of JOSE (JSON Object Signing and Encryption) standards:
- JWS (RFC 7515) - JSON Web Signature
- JWE (RFC 7516) - JSON Web Encryption
- JWK (RFC 7517) - JSON Web Key
- JWA (RFC 7518) - JSON Web Algorithms
- JWT (RFC 7519) - JSON Web Token
- Type-Safe by Design - types enforce correct API usage at compile time. Unsigned JWS can't be serialized, unverified JWT claims can't be trusted.
- Algorithm Pinning - JWT/JWS/JWE require explicit algorithm declaration, preventing algorithm confusion attacks common in other JOSE libraries. It trades off verbosity for security.
- Invalid States Are Unconstructable - Keys and tokens are validated at construction time. If you have a
Jwk, it's valid.
gleam add gose- Erlang/OTP 27+
- Node.js 22+
Browser JavaScript is not supported.
All algorithms below apply to both raw JWS/JWE operations and JWT signing and encryption.
| Family | Algorithms |
|---|---|
| HMAC | HS256, HS384, HS512 |
| RSA PKCS#1 v1.5 | RS256, RS384, RS512 |
| RSA-PSS | PS256, PS384, PS512 |
| ECDSA | ES256 (P-256), ES384 (P-384), ES512 (P-521), ES256K (secp256k1) |
| EdDSA | Ed25519, Ed448 |
| Family | Algorithms |
|---|---|
| Direct | dir |
| AES Key Wrap | A128KW, A192KW, A256KW |
| AES-GCM Key Wrap | A128GCMKW, A192GCMKW, A256GCMKW |
| ChaCha20 Key Wrap | C20PKW, XC20PKW |
| RSA | RSA1_5, RSA-OAEP, RSA-OAEP-256 |
| ECDH-ES | ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW, ECDH-ES+C20PKW, ECDH-ES+XC20PKW |
| PBES2 | PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW |
| Family | Algorithms |
|---|---|
| AES-GCM | A128GCM, A192GCM, A256GCM |
| AES-CBC + HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512 |
| ChaCha20 | C20P (ChaCha20-Poly1305), XC20P (XChaCha20-Poly1305) |
import gleam/dynamic/decode
import gleam/option.{None}
import gleam/time/duration
import gleam/time/timestamp
import gose/jwa
import gose/jwk
import gose/jwt
pub fn main() {
// Generate a symmetric key for HS256
let key = jwk.generate_hmac_key(jwa.HmacSha256)
let now = timestamp.system_time()
// Create claims
let claims =
jwt.claims()
|> jwt.with_subject("user123")
|> jwt.with_issuer("my-app")
|> jwt.with_expiration(timestamp.add(now, duration.hours(1)))
// Sign the JWT
let assert Ok(signed) = jwt.sign(jwa.JwsHmac(jwa.HmacSha256), claims, key)
// Serialize to compact format
let token = jwt.serialize(signed)
// Verify and validate
let assert Ok(verifier) =
jwt.verifier(jwa.JwsHmac(jwa.HmacSha256), [key], jwt.default_validation())
let assert Ok(verified) = jwt.verify_and_validate(verifier, token, now)
// Decode verified claims
let decoder = decode.field("sub", decode.string, decode.success)
let assert Ok("user123") = jwt.decode(verified, decoder)
}import gose/jwa
import gose/jwk
import gose/jws
import kryptos/eddsa
pub fn main() {
// Generate an Ed25519 key
let key = jwk.generate_eddsa(eddsa.Ed25519)
let payload = <<"hello world":utf8>>
// Create and sign
let assert Ok(signed) =
jws.new(jwa.JwsEddsa)
|> jws.sign(key, payload)
// Serialize to compact format
let assert Ok(token) = jws.serialize_compact(signed)
// Parse and verify using a Verifier
let assert Ok(parsed) = jws.parse_compact(token)
let assert Ok(verifier) = jws.verifier(jwa.JwsEddsa, [key])
let assert Ok(True) = jws.verify(verifier, parsed)
}import gose/jwa
import gose/jwe
import gose/jwk
pub fn main() {
// Generate a 256-bit key for direct encryption with AES-256-GCM
let key = jwk.generate_enc_key(jwa.AesGcm(jwa.Aes256))
let plaintext = <<"sensitive data":utf8>>
// Encrypt using direct key encryption
let assert Ok(encrypted) =
jwe.new_direct(jwa.AesGcm(jwa.Aes256))
|> jwe.encrypt(key, plaintext)
// Serialize to compact format
let assert Ok(token) = jwe.serialize_compact(encrypted)
// Parse and decrypt with algorithm pinning
let assert Ok(parsed) = jwe.parse_compact(token)
let assert Ok(decryptor) = jwe.key_decryptor(jwa.JweDirect, jwa.AesGcm(jwa.Aes256), [key])
let assert Ok(decrypted) = jwe.decrypt(decryptor, parsed)
// decrypted == <<"sensitive data":utf8>>
}The library uses a two-tier error design:
GoseError — used by JOSE primitives (JWS, JWE, JWK):
| Variant | When It Occurs |
|---|---|
ParseError |
Invalid base64 encoding, malformed JSON, wrong token format |
CryptoError |
Signature verification failure, decryption failure, key derivation error |
InvalidState |
Wrong key type for algorithm, missing required header, incompatible parameters |
JwtError — used by JWT and encrypted JWT modules:
| Variant | When It Occurs |
|---|---|
TokenExpired |
Token's exp claim is in the past |
TokenNotYetValid |
Token's nbf claim is in the future |
IssuerMismatch |
Token's iss doesn't match expected issuer |
AudienceMismatch |
Token's aud doesn't match expected audience |
InvalidSignature |
JWS signature verification failed |
DecryptionFailed |
JWE decryption failed |
JoseError(GoseError) |
Underlying JOSE operation failed (key validation, signing, etc.) |
| ... | See JwtError type for all variants |
- X.509 certificate parameters not supported - JWKs containing X.509 certificate chain parameters (
x5u,x5c,x5t,x5t#S256) are rejected with a parse error. Certificate-based key validation must be performed outside this library.
Full API documentation is available at hexdocs.pm/gose.