diff --git a/lib/jwt.rb b/lib/jwt.rb index 950c4dec..3a0034ed 100644 --- a/lib/jwt.rb +++ b/lib/jwt.rb @@ -2,6 +2,7 @@ require 'base64' require 'openssl' require 'jwt/decode' +require 'jwt/encode' require 'jwt/error' require 'jwt/json' @@ -76,38 +77,6 @@ def sign_hmac(algorithm, msg, key) OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg) end - def base64url_encode(str) - Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '') - end - - def encoded_header(algorithm = 'HS256', header_fields = {}) - header = { 'alg' => algorithm }.merge(header_fields) - base64url_encode(encode_json(header)) - end - - def encoded_payload(payload) - raise InvalidPayload, 'exp claim must be an integer' if payload['exp'] && payload['exp'].is_a?(Time) - base64url_encode(encode_json(payload)) - end - - def encoded_signature(signing_input, key, algorithm) - if algorithm == 'none' - '' - else - signature = sign(algorithm, signing_input, key) - base64url_encode(signature) - end - end - - 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 - def decoded_segments(jwt, key = nil, verify = true, custom_options = {}, &keyfinder) raise(JWT::DecodeError, 'Nil JSON web token') unless jwt @@ -117,6 +86,11 @@ def decoded_segments(jwt, key = nil, verify = true, custom_options = {}, &keyfin decoder.decode_segments end + def encode(payload, key, algorithm = 'HS256', header_fields = {}) + encoder = Encode.new payload, key, algorithm, header_fields + encoder.segments + end + def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder) raise(JWT::DecodeError, 'Nil JSON web token') unless jwt @@ -197,4 +171,8 @@ def asn1_to_raw(signature, public_key) def base64url_decode(str) Decode.base64url_decode(str) end + + def base64url_encode(str) + Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '') + end end diff --git a/lib/jwt/encode.rb b/lib/jwt/encode.rb new file mode 100644 index 00000000..d8c1995c --- /dev/null +++ b/lib/jwt/encode.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +require 'jwt/json' + +# JWT::Encode module +module JWT + extend JWT::Json + + # Encoding logic for JWT + class Encode + attr_reader :payload, :key, :algorithm, :header_fields, :segments + + def initialize(payload, key, algorithm, header_fields) + @payload = payload + @key = key + @algorithm = algorithm + @header_fields = header_fields + @segments = encode_segments + end + + private + + def encoded_header(algorithm, header_fields) + header = { 'alg' => algorithm }.merge(header_fields) + JWT.base64url_encode(JWT.encode_json(header)) + end + + def encoded_payload(payload) + raise InvalidPayload, 'exp claim must be an integer' if payload['exp'] && payload['exp'].is_a?(Time) + JWT.base64url_encode(JWT.encode_json(payload)) + end + + def encoded_signature(signing_input, key, algorithm) + if algorithm == 'none' + '' + else + signature = JWT.sign(algorithm, signing_input, key) + JWT.base64url_encode(signature) + end + end + + def encode_segments + segments = [] + segments << encoded_header(@algorithm, @header_fields) + segments << encoded_payload(@payload) + segments << encoded_signature(segments.join('.'), @key, @algorithm) + segments.join('.') + end + end +end