class JOSE::JWT

Overview

JWT (JSON Web Token) compact representation of claims (RFC 7519).

Tokens are backed by a Hash(String, JSON::Any) and can be signed (JWS) or encrypted (JWE). The seven RFC 7519 registered claims (#iss, #sub, #aud, #exp, #nbf, #iat, #jti) are available as typed accessors. Use the claim macro to define additional typed claims in a subclass:

require "jose"

class MyJWT < JOSE::JWT
  claim role : String?
end

jwk = JOSE::JWK.generate_key_oct

tok = MyJWT.new
tok.sub = "alice"
tok.role = "admin"
tok.exp = Time.utc + 1.hour

signed = JOSE::JWT.sign(jwk, tok)
valid, decoded, _header = JOSE::JWT.verify_strict(jwk, ["HS256"], signed)
my_jwt = MyJWT.from_map(decoded.to_map)
my_jwt.sub  # => "alice"
my_jwt.role # => "admin"

Defined in:

jose/jwt.cr

Constructors

Class Method Summary

Macro Summary

Instance Method Summary

Constructor Detail

def self.decrypt(jwk : JWK, encrypted : String | EncryptedBinary) : JWT #

Decrypts a compact JWE and returns the contained JWT.


[View source]
def self.from_binary(json : String) : self #

Parses a JSON string and returns a JWT.


[View source]
def self.from_map(map : Hash(String, JSON::Any)) : self #

Wraps an existing claims map in a JWT.


[View source]
def self.new(fields : Hash(String, JSON::Any)) #

[View source]
def self.new #

[View source]
def self.peek(compact : String) : JWT #

Alias for .peek_payload.


[View source]
def self.peek_payload(compact : String) : JWT #

Decodes the payload of compact as a JWT without verifying the signature.


[View source]

Class Method Detail

def self.encrypt(jwk : JWK, jwt : JWT, header_overrides : Hash(String, JSON::Any) | Nil = nil) : EncryptedBinary #

Encrypts jwt for jwk and returns a compact EncryptedBinary.

The "typ" header is set to "JWT" unless header_overrides overrides it. The key-wrap and content-encryption algorithms are inferred from the key type when not present in header_overrides.


[View source]
def self.peek_protected(compact : String) : Hash(String, JSON::Any) #

Decodes the protected header of compact without verifying.


[View source]
def self.sign(jwk : JWK, jwt : JWT, header_overrides : Hash(String, JSON::Any) | Nil = nil) : SignedBinary #

Signs jwt with jwk and returns a compact SignedBinary.

The "typ" header is set to "JWT" unless header_overrides provides a different value. The signing algorithm is inferred from the key type when not present in header_overrides.


[View source]
def self.verify(jwk : JWK, signed : String | SignedBinary) : Tuple(Bool, JWT, Hash(String, JSON::Any)) #

Verifies a compact JWS carrying JWT claims.

Returns {valid, jwt, protected_header}. valid is true when the signature is correct. The JWT is decoded regardless of validity.


[View source]
def self.verify_strict(jwk : JWK, allowed_algs : Array(String), signed : String | SignedBinary, *, iss : String | Nil = nil, aud : String | Array(String) | Nil = nil, typ : String | Nil = nil, validate_claims : Bool = true) : Tuple(Bool, JWT, Hash(String, JSON::Any)) #

Like .verify but enforces RFC 8725 best-current-practices on top of the cryptographic signature check.

Always checked:

  • "alg" header must appear in allowed_algs (algorithm-confusion guard).

Keyword parameters (all optional, off by default except validate_claims):

  • iss — expected issuer; token #iss claim must match exactly (§3.8).
  • aud — expected audience; token must include at least one of the given values (§3.9). Accepts a single string or an array.
  • typ — expected "typ" header value, e.g. "JWT" or "at+JWT" (§3.11).
  • validate_claims — when true (default) the token must not be expired and its #nbf must have been reached (§3.3). Pass false to skip.

Returns {false, jwt, header} whenever any check fails, even if the cryptographic signature itself is valid.


[View source]

Macro Detail

macro claim(decl) #

[View source]

Instance Method Detail

def [](key : String) : JSON::Any #

Returns the claim for key, raising KeyError when absent.


[View source]
def []?(key : String) : JSON::Any | Nil #

Returns the claim for key, or nil when absent.


[View source]
def aud : String | Array(String) | Nil #

[View source]
def aud=(value : String | Array(String) | Nil) #

[View source]
def exp : Time | Nil #

[View source]
def exp=(value : Time | Nil) #

[View source]
def expired?(now : Time = Time.utc) : Bool #

Returns true if #exp is set and is in the past relative to now. Does NOT verify the signature — call .verify_strict for that.


[View source]
def fields : Hash(String, JSON::Any) #

The claims map backing this token.


[View source]
def iat : Time | Nil #

[View source]
def iat=(value : Time | Nil) #

[View source]
def iss : String | Nil #

String (default fallthrough)


[View source]
def iss=(value : String | Nil) #

── RFC 7519 Registered Claims ───────────────────────────────────────────


[View source]
def jti : String | Nil #

String (default fallthrough)


[View source]
def jti=(value : String | Nil) #

[View source]
def nbf : Time | Nil #

[View source]
def nbf=(value : Time | Nil) #

[View source]
def not_yet_valid?(now : Time = Time.utc) : Bool #

Returns true if #nbf is set and has not yet been reached relative to now.


[View source]
def sub : String | Nil #

String (default fallthrough)


[View source]
def sub=(value : String | Nil) #

[View source]
def to_binary : String #

Serializes the claims to a compact JSON string.


[View source]
def to_map : Hash(String, JSON::Any) #

Returns the underlying claims map.


[View source]
def valid_at?(now : Time = Time.utc) : Bool #

Returns true if the token is currently valid: not expired and nbf has passed. Does NOT verify the signature — call .verify_strict for that.


[View source]