ywt
Types
An error returned while decoding and verifying a JWT.
Treat all errors as authentication failures at the boundary. The variants are useful for logging and for flows such as token refresh, but avoid leaking detailed verification failures to clients.
pub type Error {
MalformedToken
InvalidHeaderEncoding
InvalidPayloadEncoding
InvalidSignatureEncoding
InvalidHeaderJson(json.DecodeError)
HeaderDecodingError(List(decode.DecodeError))
UnsupportedCriticalHeader
UnsupportedUnencodedPayload
InvalidPayloadJson(json.DecodeError)
NoMatchingKey
InvalidSignature
TokenExpired(expired_at: timestamp.Timestamp)
TokenNotYetValid(not_before: timestamp.Timestamp)
InvalidIssuer(expected: List(String), actual: String)
InvalidAudience(expected: List(String), actual: List(String))
InvalidType(expected: String, actual: String)
InvalidSubject(expected: List(String), actual: String)
InvalidId(expected: List(String), actual: String)
MissingClaim(claim_name: String)
ClaimDecodingError(
claim_name: String,
error: List(decode.DecodeError),
)
InvalidCustomClaim(claim_name: String)
PayloadDecodingError(List(decode.DecodeError))
}
Constructors
-
MalformedTokenJWT doesn’t contain exactly 3 parts separated by dots
-
InvalidHeaderEncodingBase64 decoding failed for header
-
InvalidPayloadEncodingBase64 decoding failed for payload
-
InvalidSignatureEncodingBase64 decoding failed for signature
-
InvalidHeaderJson(json.DecodeError)JSON parsing failed for header
-
HeaderDecodingError(List(decode.DecodeError))Header structure doesn’t match the JWT header decoder
-
UnsupportedCriticalHeaderJWT uses critical JOSE header extensions that ywt does not support
-
UnsupportedUnencodedPayloadJWT asks for an unencoded JWS payload, which ywt does not support
-
InvalidPayloadJson(json.DecodeError)JSON parsing failed for payload - note that this does not include failures for your payload decoder. See
PayloadDecodingError. -
NoMatchingKeyNo suitable key found to verify the signature
-
InvalidSignatureSignature verification failed (potential tampering)
-
TokenExpired(expired_at: timestamp.Timestamp)Token has expired
-
TokenNotYetValid(not_before: timestamp.Timestamp)Token is not yet valid (nbf claim)
-
InvalidIssuer(expected: List(String), actual: String)Wrong issuer
-
InvalidAudience(expected: List(String), actual: List(String))Wrong audience
-
InvalidType(expected: String, actual: String)Wrong typ header
-
InvalidSubject(expected: List(String), actual: String)Wrong subject
-
InvalidId(expected: List(String), actual: String)Wrong JWT ID
-
MissingClaim(claim_name: String)Required claim is missing
-
ClaimDecodingError( claim_name: String, error: List(decode.DecodeError), )Claim Decoding Error
-
InvalidCustomClaim(claim_name: String)Custom claim did not match
-
PayloadDecodingError(List(decode.DecodeError))Payload structure doesn’t match expected decoder
Values
pub fn decode(
jwt jwt: String,
using decoder: decode.Decoder(payload),
claims claims: List(claim.Claim),
keys keys: List(verify_key.VerifyKey),
) -> promise.Promise(Result(payload, Error))
Verifies a JWT and decodes its payload.
Tokens with exp and nbf are checked by default with zero leeway. Tokens
with aud are rejected by default unless you pass an audience claim.
Audience validation accepts string or array values.
Unknown non-critical JWT header fields are ignored by ywt. Tokens with a
crit header parameter or b64: false are rejected. Unknown payload fields
are accepted or rejected by your payload decoder.
let claims = [
claim.expires_at(max_age: duration.minutes(15), leeway: duration.minutes(1)),
claim.issuer("https://auth.example.com", []),
claim.audience("https://api.example.com", []),
]
let decoder = decode.field("sub", decode.string)
use result <- promise.await(
ywt.decode(jwt, using: decoder, claims:, keys: [verify_key]),
)
pub fn decode_unsafely_without_validation(
jwt: String,
payload_decoder: decode.Decoder(payload),
) -> Result(payload, Error)
Decodes a JWT payload without verifying the signature or claims.
Do not use this for authentication or authorization. The token may be forged,
expired, or intended for another audience. No exp, nbf, or aud checks
are performed.
pub fn encode(
payload payload: List(#(String, json.Json)),
claims claims: List(claim.Claim),
key key: sign_key.SignKey,
) -> promise.Promise(String)
Creates a signed JWT from payload data and claims.
JWTs are signed, not encrypted, so anyone with the token can read the
payload. Claims take precedence when a field appears in both payload and
claims; put security fields such as exp, iss, and aud in claims.
let payload = [
#("sub", json.string("user_123")),
#("role", json.string("admin")),
]
let claims = [
claim.expires_at(max_age: duration.minutes(15), leeway: duration.minutes(1)),
claim.issuer("https://auth.example.com", []),
claim.audience("https://api.example.com", []),
]
use jwt <- promise.await(ywt.encode(payload, claims, signing_key))
pub fn generate_key(
algorithm: algorithm.Algorithm,
) -> promise.Promise(sign_key.SignKey)
Generates a signing key for an algorithm.
Generated keys include a random kid for key rotation. Store signing keys as
secrets; anyone with one can mint trusted tokens.
use signing_key <- promise.await(ywt.generate_key(algorithm.es384))
let verify_key = verify_key.derived(signing_key)
pub fn sign_bits(
message: BitArray,
key: sign_key.SignKey,
) -> promise.Promise(BitArray)
Signs bytes with a signing key.
This is a low-level function used internally by ywt.
This is a low-level primitive. Most applications should use encode so the
JWT header, payload, and claims are constructed consistently. Raw signatures
do not carry claim or audience validation.
pub fn sign_string(
message: String,
key: sign_key.SignKey,
) -> promise.Promise(String)
Signs a UTF-8 string and returns an unpadded base64url signature.
This is a low-level helper for JWT-style signing input. Prefer encode for
complete JWTs.
use signature <- promise.await(ywt.sign_string("header.payload", signing_key))
pub fn verify_bits(
message: BitArray,
signature: BitArray,
key: verify_key.VerifyKey,
) -> promise.Promise(Bool)
Verifies a byte signature with a verification key.
This is a low-level function used internally by ywt.
This only verifies the signature over the exact bytes you pass. It does not parse JWTs or validate claims.
RSA JWKs decoded without alg cannot be used for raw verification and return
False; use decode when the signed JWT header should choose the algorithm.
pub fn verify_string(
message: String,
signature: String,
key: verify_key.VerifyKey,
) -> promise.Promise(Bool)
Verifies an unpadded base64url signature for a UTF-8 string.
Invalid base64url returns False. This only verifies the signature over the
exact string you pass; use decode for complete JWT verification.