Module: JWT
- Extended by:
- Json
- Defined in:
- lib/jwt.rb,
lib/jwt/json.rb
Defined Under Namespace
Modules: Json Classes: DecodeError
Class Method Summary collapse
- .base64url_decode(str) ⇒ Object
- .base64url_encode(str) ⇒ Object
- .decode(jwt, key = nil, verify = true, &keyfinder) ⇒ Object
- .decode_header_and_payload(header_segment, payload_segment) ⇒ Object
- .decoded_segments(jwt, verify = true) ⇒ Object
- .encode(payload, key, algorithm = "HS256", header_fields = {}) ⇒ Object
- .encoded_header(algorithm = "HS256", header_fields = {}) ⇒ Object
- .encoded_payload(payload) ⇒ Object
- .encoded_signature(signing_input, key, algorithm) ⇒ Object
- .raw_segments(jwt, verify = true) ⇒ Object
-
.secure_compare(a, b) ⇒ Object
From devise constant-time comparison algorithm to prevent timing attacks.
- .sign(algorithm, msg, key) ⇒ Object
- .sign_hmac(algorithm, msg, key) ⇒ Object
- .sign_rsa(algorithm, msg, private_key) ⇒ Object
- .signature_algorithm_and_key(header, key, &keyfinder) ⇒ Object
- .verify_rsa(algorithm, public_key, signing_input, signature) ⇒ Object
- .verify_signature(algo, key, signing_input, signature) ⇒ Object
Methods included from Json
Class Method Details
.base64url_decode(str) ⇒ Object
39 40 41 42 |
# File 'lib/jwt.rb', line 39 def base64url_decode(str) str += "=" * (4 - str.length.modulo(4)) Base64.decode64(str.tr("-_", "+/")) end |
.base64url_encode(str) ⇒ Object
44 45 46 |
# File 'lib/jwt.rb', line 44 def base64url_encode(str) Base64.encode64(str).tr("+/", "-_").gsub(/[\n=]/, "") end |
.decode(jwt, key = nil, verify = true, &keyfinder) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/jwt.rb', line 96 def decode(jwt, key=nil, verify=true, &keyfinder) raise JWT::DecodeError.new("Nil JSON web token") unless jwt header, payload, signature, signing_input = decoded_segments(jwt, verify) raise JWT::DecodeError.new("Not enough or too many segments") unless header && payload if verify algo, key = signature_algorithm_and_key(header, key, &keyfinder) verify_signature(algo, key, signing_input, signature) end return payload,header end |
.decode_header_and_payload(header_segment, payload_segment) ⇒ Object
82 83 84 85 86 |
# File 'lib/jwt.rb', line 82 def decode_header_and_payload(header_segment, payload_segment) header = decode_json(base64url_decode(header_segment)) payload = decode_json(base64url_decode(payload_segment)) [header, payload] end |
.decoded_segments(jwt, verify = true) ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/jwt.rb', line 88 def decoded_segments(jwt, verify=true) header_segment, payload_segment, crypto_segment = raw_segments(jwt, verify) header, payload = decode_header_and_payload(header_segment, payload_segment) signature = base64url_decode(crypto_segment.to_s) if verify signing_input = [header_segment, payload_segment].join(".") [header, payload, signature, signing_input] end |
.encode(payload, key, algorithm = "HS256", header_fields = {}) ⇒ Object
66 67 68 69 70 71 72 73 |
# File 'lib/jwt.rb', line 66 def encode(payload, key, algorithm="HS256", header_fields={}) algorithm ||= "none" segments = [] segments << encoded_header(algorithm, header_fields) segments << encoded_payload(payload) segments << encoded_signature(segments.join("."), key, algorithm) segments.join(".") end |
.encoded_header(algorithm = "HS256", header_fields = {}) ⇒ Object
48 49 50 51 |
# File 'lib/jwt.rb', line 48 def encoded_header(algorithm="HS256", header_fields={}) header = {"typ" => "JWT", "alg" => algorithm}.merge(header_fields) base64url_encode(encode_json(header)) end |
.encoded_payload(payload) ⇒ Object
53 54 55 |
# File 'lib/jwt.rb', line 53 def encoded_payload(payload) base64url_encode(encode_json(payload)) end |
.encoded_signature(signing_input, key, algorithm) ⇒ Object
57 58 59 60 61 62 63 64 |
# File 'lib/jwt.rb', line 57 def encoded_signature(signing_input, key, algorithm) if algorithm == "none" "" else signature = sign(algorithm, signing_input, key) base64url_encode(signature) end end |
.raw_segments(jwt, verify = true) ⇒ Object
75 76 77 78 79 80 |
# File 'lib/jwt.rb', line 75 def raw_segments(jwt, verify=true) segments = jwt.split(".") required_num_segments = verify ? [3] : [2,3] raise JWT::DecodeError.new("Not enough or too many segments") unless required_num_segments.include? segments.length segments end |
.secure_compare(a, b) ⇒ Object
From devise constant-time comparison algorithm to prevent timing attacks
134 135 136 137 138 139 140 141 |
# File 'lib/jwt.rb', line 134 def secure_compare(a, b) return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end |
.sign(algorithm, msg, key) ⇒ Object
17 18 19 20 21 22 23 24 25 |
# File 'lib/jwt.rb', line 17 def sign(algorithm, msg, key) if ["HS256", "HS384", "HS512"].include?(algorithm) sign_hmac(algorithm, msg, key) elsif ["RS256", "RS384", "RS512"].include?(algorithm) sign_rsa(algorithm, msg, key) else raise NotImplementedError.new("Unsupported signing method") end end |
.sign_hmac(algorithm, msg, key) ⇒ Object
35 36 37 |
# File 'lib/jwt.rb', line 35 def sign_hmac(algorithm, msg, key) OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub("HS", "sha")), key, msg) end |
.sign_rsa(algorithm, msg, private_key) ⇒ Object
27 28 29 |
# File 'lib/jwt.rb', line 27 def sign_rsa(algorithm, msg, private_key) private_key.sign(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), msg) end |
.signature_algorithm_and_key(header, key, &keyfinder) ⇒ Object
109 110 111 112 113 114 |
# File 'lib/jwt.rb', line 109 def signature_algorithm_and_key(header, key, &keyfinder) if keyfinder key = keyfinder.call(header) end [header['alg'], key] end |
.verify_rsa(algorithm, public_key, signing_input, signature) ⇒ Object
31 32 33 |
# File 'lib/jwt.rb', line 31 def verify_rsa(algorithm, public_key, signing_input, signature) public_key.verify(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), signature, signing_input) end |
.verify_signature(algo, key, signing_input, signature) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/jwt.rb', line 116 def verify_signature(algo, key, signing_input, signature) begin if ["HS256", "HS384", "HS512"].include?(algo) raise JWT::DecodeError.new("Signature verification failed") unless secure_compare(signature, sign_hmac(algo, signing_input, key)) elsif ["RS256", "RS384", "RS512"].include?(algo) raise JWT::DecodeError.new("Signature verification failed") unless verify_rsa(algo, key, signing_input, signature) else raise JWT::DecodeError.new("Algorithm not supported") end rescue OpenSSL::PKey::PKeyError raise JWT::DecodeError.new("Signature verification failed") ensure OpenSSL.errors.clear end end |