Class: JOSE::JWT

Inherits:
Struct
  • Object
show all
Defined in:
lib/jose/jwt.rb

Overview

JWT stands for JSON Web Token which is defined in RFC 7519.

Encryption Examples

All of the example keys generated below can be found here: https://gist.github.com/potatosalad/dd140560b2bdbdab886d

See JOSE::JWE for more Encryption examples.

A128GCM

jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
jwt        = { "test" => true }

# A128GCM
encrypted_a128gcm = JOSE::JWT.encrypt(jwk_oct128, { "alg" => "dir", "enc" => "A128GCM" }, jwt).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwidHlwIjoiSldUIn0..yKs3KxBPBsp60bVv.lYrRQrT8GQQMG2OFCA.Z6GQkHT6K6VWxkOJBLFt3g"
JOSE::JWT.decrypt(jwk_oct128, encrypted_a128gcm)
# => [#<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
#  #<struct JOSE::JWE
#   alg=#<JOSE::JWE::ALG_dir:0x007fd81c1023d0>,
#   enc=#<struct JOSE::JWE::ENC_AES_GCM cipher_name="aes-128-gcm", bits=128, cek_len=16, iv_len=12>,
#   zip=nil,
#   fields=JOSE::Map["typ" => "JWT"]>]

Signature Examples

All of the example keys generated below can be found here: https://gist.github.com/potatosalad/925a8b74d85835e285b9

See JOSE::JWS for more Signature examples. For security purposes, JOSE::JWT.verify_strict is recommended over JOSE::JWT.verify.

HS256

# let's generate the key we'll use below and define our jwt
jwk_hs256 = JOSE::JWK.generate_key([:oct, 16])
jwt       = { "test" => true }

# HS256
signed_hs256 = JOSE::JWT.sign(jwk_hs256, { "alg" => "HS256" }, jwt).compact
# => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlfQ.XYsFJDhfBZCAKnEZjR0WWd1l1ZPDD4bYpZYMHizexfQ"

# verify_strict is recommended over verify
JOSE::JWT.verify_strict(jwk_hs256, ["HS256"], signed_hs256)
# => [true,
#  #<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
#  #<struct JOSE::JWS
#   alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
#   b64=nil,
#   fields=JOSE::Map["typ" => "JWT"]>]

# verify returns the same thing without "alg" whitelisting
JOSE::JWT.verify(jwk_hs256, signed_hs256)
# => [true,
#  #<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
#  #<struct JOSE::JWS
#   alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
#   b64=nil,
#   fields=JOSE::Map["typ" => "JWT"]>]

# the default signing algorithm is also "HS256" based on the type of jwk used
signed_hs256 == JOSE::JWT.sign(jwk_hs256, jwt).compact
# => true

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#fieldsObject

Returns the value of attribute fields

Returns:

  • (Object)

    the current value of fields



65
66
67
# File 'lib/jose/jwt.rb', line 65

def fields
  @fields
end

Class Method Details

.decrypt(jwk, encrypted) ⇒ [JOSE::JWT, JOSE::JWE]

Decrypts an encrypted JOSE::JWT using the jwk.

Parameters:

Returns:

See Also:



191
192
193
194
# File 'lib/jose/jwt.rb', line 191

def self.decrypt(jwk, encrypted)
  decrypted, jwe = JOSE::JWK.block_decrypt(jwk, encrypted)
  return from_binary(decrypted), jwe
end

.encrypt(jwk, jwe, jwt = nil) ⇒ JOSE::EncryptedMap

Encrypts a JOSE::JWT using the jwk and the default block encryptor algorithm jwe for the key type.

Parameters:

Returns:

See Also:



202
203
204
205
206
207
208
# File 'lib/jose/jwt.rb', line 202

def self.encrypt(jwk, jwe, jwt = nil)
  if jwt.nil?
    jwt = jwe
    jwe = nil
  end
  return from(jwt).encrypt(jwk, jwe)
end

.from(object, modules = {}) ⇒ JOSE::JWT+

Converts a binary or map into a JOSE::JWT.

JOSE::JWT.from({ "test" => true })
# => #<struct JOSE::JWT fields=JOSE::Map["test" => true]>
JOSE::JWT.from("{\"test\":true}")
# => #<struct JOSE::JWT fields=JOSE::Map["test" => true]>

Parameters:

Returns:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/jose/jwt.rb', line 80

def self.from(object, modules = {})
  case object
  when JOSE::Map, Hash
    return from_map(object, modules)
  when String
    return from_binary(object, modules)
  when JOSE::JWT
    return object
  when Array
    return object.map { |obj| from(obj, modules) }
  else
    raise ArgumentError, "'object' must be a Hash, String, JOSE::JWT, or Array"
  end
end

.from_binary(object, modules = {}) ⇒ JOSE::JWT+

Converts a binary into a JOSE::JWT.

Parameters:

  • object (String, Array<String>)
  • modules (Hash) (defaults to: {})

Returns:



99
100
101
102
103
104
105
106
107
108
# File 'lib/jose/jwt.rb', line 99

def self.from_binary(object, modules = {})
  case object
  when String
    return from_map(JOSE.decode(object), modules)
  when Array
    return object.map { |obj| from_binary(obj, modules) }
  else
    raise ArgumentError, "'object' must be a String or Array"
  end
end

.from_file(file, modules = {}) ⇒ JOSE::JWT

Reads file and calls from_binary to convert into a JOSE::JWT.

Parameters:

  • file (String)
  • modules (Hash) (defaults to: {})

Returns:



114
115
116
# File 'lib/jose/jwt.rb', line 114

def self.from_file(file, modules = {})
  return from_binary(File.binread(file), modules)
end

.from_map(object, modules = {}) ⇒ JOSE::JWT+

Converts a map into a JOSE::JWT.

Parameters:

Returns:



122
123
124
125
126
127
128
129
130
131
# File 'lib/jose/jwt.rb', line 122

def self.from_map(object, modules = {})
  case object
  when JOSE::Map, Hash
    return from_fields(JOSE::JWT.new(JOSE::Map.new(object)), modules)
  when Array
    return object.map { |obj| from_map(obj, modules) }
  else
    raise ArgumentError, "'object' must be a Hash or Array"
  end
end

.merge(left, right) ⇒ JOSE::JWT

Merges map on right into map on left.

Parameters:

Returns:



236
237
238
# File 'lib/jose/jwt.rb', line 236

def self.merge(left, right)
  return from(left).merge(right)
end

.peek_payload(signed) ⇒ JOSE::Map

Returns the decoded payload portion of a signed binary or map without verifying the signature.

JOSE::JWT.peek_payload("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlfQ.XYsFJDhfBZCAKnEZjR0WWd1l1ZPDD4bYpZYMHizexfQ")
# => JOSE::Map["test" => true]

Parameters:

Returns:

See Also:



266
267
268
# File 'lib/jose/jwt.rb', line 266

def self.peek_payload(signed)
  return JOSE::Map.new(JOSE.decode(JOSE::JWS.peek_payload(signed)))
end

.peek_protected(signed) ⇒ String

Returns the decoded protected portion of a signed binary or map without verifying the signature.

Parameters:

Returns:

  • (String)

See Also:



274
275
276
# File 'lib/jose/jwt.rb', line 274

def self.peek_protected(signed)
  return JOSE::JWS.peek_protected(signed)
end

.peek_signature(signed) ⇒ String

Returns the decoded signature portion of a signed binary or map without verifying the signature.

Parameters:

Returns:

  • (String)

See Also:



282
283
284
# File 'lib/jose/jwt.rb', line 282

def self.peek_signature(signed)
  return JOSE::JWS.peek_signature(signed)
end

.sign(jwk, jws, jwt = nil, header = nil) ⇒ JOSE::SignedMap

Signs a JOSE::JWT using the jwk and the default signer algorithm jws for the key type.

Parameters:

Returns:

See Also:



294
295
296
297
298
299
300
# File 'lib/jose/jwt.rb', line 294

def self.sign(jwk, jws, jwt = nil, header = nil)
  if jwt.nil?
    jwt = jws
    jws = nil
  end
  return from(jwt).sign(jwk, jws, header)
end

.to_binary(jwt) ⇒ String+

Converts a JOSE::JWT into a binary.

Parameters:

Returns:

  • (String, Array<String>)


138
139
140
141
142
143
144
# File 'lib/jose/jwt.rb', line 138

def self.to_binary(jwt)
  if jwt.is_a?(Array)
    return from(jwt).map { |obj| obj.to_binary }
  else
    return from(jwt).to_binary
  end
end

.to_file(jwt, file) ⇒ Fixnum

Calls to_binary on a JOSE::JWT and then writes the binary to file.

Parameters:

Returns:

  • (Fixnum)

    bytes written



156
157
158
# File 'lib/jose/jwt.rb', line 156

def self.to_file(jwt, file)
  return from(jwt).to_file(file)
end

.to_map(jwt) ⇒ JOSE::Map+

Converts a JOSE::JWT into a map.

Parameters:

Returns:



170
171
172
173
174
175
176
# File 'lib/jose/jwt.rb', line 170

def self.to_map(jwt)
  if jwt.is_a?(Array)
    return from(jwt).map { |obj| obj.to_map }
  else
    return from(jwt).to_map
  end
end

.verify(jwk, signed) ⇒ [Boolean, JOSE::JWT, JOSE::JWS]

Verifies the signed using the jwk and calls JOST::JWT.from on the payload.

Parameters:

Returns:

See Also:



327
328
329
330
331
# File 'lib/jose/jwt.rb', line 327

def self.verify(jwk, signed)
  verified, payload, jws = JOSE::JWK.verify(signed, jwk)
  jwt = from_binary(payload)
  return verified, jwt, jws
end

.verify_strict(jwk, allow, signed) ⇒ [Boolean, (JOSE::JWT, String), (JOSE::JWS, JOSE::Map)]

Verifies the signed using the jwk, whitelists the "alg" using allow, and calls JOST::JWT.from on the payload.

Parameters:

Returns:

See Also:



339
340
341
342
343
344
345
346
# File 'lib/jose/jwt.rb', line 339

def self.verify_strict(jwk, allow, signed)
  verified, payload, jws = JOSE::JWK.verify_strict(signed, allow, jwk)
  jwt = payload
  if verified
    jwt = from_binary(payload)
  end
  return verified, jwt, jws
end

Instance Method Details

#encrypt(jwk, jwe = nil) ⇒ JOSE::EncryptedMap

Encrypts a JOSE::JWT using the jwk and the jwe algorithm.

If "typ" is not specified in the jwe, { "typ" => "JWT" } will be added.

Parameters:

Returns:

See Also:



217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/jose/jwt.rb', line 217

def encrypt(jwk, jwe = nil)
  plain_text = to_binary
  if jwe.nil?
    jwk = JOSE::JWK.from(jwk)
    jwe = (jwk.is_a?(Array) ? jwk.last : jwk).block_encryptor
  end
  if jwe.is_a?(Hash)
    jwe = JOSE::Map.new(jwe)
  end
  if jwe.is_a?(JOSE::Map) and not jwe.has_key?('typ')
    jwe = jwe.put('typ', 'JWT')
  end
  return JOSE::JWK.block_encrypt(jwk, plain_text, jwe)
end

#merge(object) ⇒ JOSE::JWT

Merges object into current map.

Parameters:

Returns:



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/jose/jwt.rb', line 243

def merge(object)
  object = case object
  when JOSE::Map, Hash
    object
  when String
    JOSE.decode(object)
  when JOSE::JWT
    object.to_map
  else
    raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWT"
  end
  return JOSE::JWT.from_map(self.to_map.merge(object))
end

#sign(jwk, jws = nil, header = nil) ⇒ JOSE::SignedMap

Signs a JOSE::JWT using the jwk and the jws algorithm.

Parameters:

Returns:

See Also:



309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/jose/jwt.rb', line 309

def sign(jwk, jws = nil, header = nil)
  plain_text = to_binary
  if jws.nil?
    jwk = JOSE::JWK.from(jwk)
    jws = jwk.signer
  end
  jws = JOSE::JWS.from(jws).to_map
  if not jws.has_key?('typ')
    jws = jws.put('typ', 'JWT')
  end
  return JOSE::JWK.sign(plain_text, jws, jwk, header)
end

#to_binaryString

Converts a JOSE::JWT into a binary.

Returns:

  • (String)


148
149
150
# File 'lib/jose/jwt.rb', line 148

def to_binary
  return JOSE.encode(to_map)
end

#to_file(file) ⇒ Fixnum

Calls #to_binary on a JOSE::JWT and then writes the binary to file.

Parameters:

  • file (String)

Returns:

  • (Fixnum)

    bytes written



163
164
165
# File 'lib/jose/jwt.rb', line 163

def to_file(file)
  return File.binwrite(file, to_binary)
end

#to_mapJOSE::Map

Converts a JOSE::JWT into a map.

Returns:



180
181
182
# File 'lib/jose/jwt.rb', line 180

def to_map
  return fields
end