1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
require 'openssl'
require 'url_safe_base64'
require 'multi_json'
require 'active_support'
require 'active_support/core_ext'
require 'json/jose'
module JSON
class JWT < ActiveSupport::HashWithIndifferentAccess
attr_accessor :signature
class Exception < StandardError; end
class InvalidFormat < Exception; end
class VerificationFailed < Exception; end
class UnexpectedAlgorithm < VerificationFailed; end
include JOSE
def initialize(claims = {})
@content_type = 'application/jwt'
self.typ = :JWT
self.alg = :none
[:exp, :nbf, :iat].each do |key|
claims[key] = claims[key].to_i if claims[key]
end
update claims
end
def sign(private_key_or_secret, algorithm = :autodetect)
if algorithm == :autodetect
# NOTE:
# I'd like to make :RS256 default.
# However, by histrical reasons, :HS256 was default.
# This code is needed to keep legacy behavior.
algorithm = private_key_or_secret.is_a?(String) ? :HS256 : :RS256
end
jws = JWS.new self.dup
jws.kid ||= private_key_or_secret[:kid] if private_key_or_secret.is_a? JSON::JWK
jws.alg = algorithm
jws.sign! private_key_or_secret
end
def encrypt(public_key_or_secret, algorithm = :RSA1_5, encryption_method = :'A128CBC-HS256')
jwe = JWE.new self
jwe.kid ||= public_key_or_secret[:kid] if public_key_or_secret.is_a? JSON::JWK
jwe.alg = algorithm
jwe.enc = encryption_method
jwe.encrypt! public_key_or_secret
end
def to_s
[
header.to_json,
self.to_json,
signature
].collect do |segment|
UrlSafeBase64.encode64 segment.to_s
end.join('.')
end
def as_json(options = {})
case options[:syntax]
when :general
{
payload: UrlSafeBase64.encode64(self.to_json),
signatures: [{
protected: UrlSafeBase64.encode64(header.to_json),
signature: UrlSafeBase64.encode64(signature.to_s)
}]
}
when :flattened
{
protected: UrlSafeBase64.encode64(header.to_json),
payload: UrlSafeBase64.encode64(self.to_json),
signature: UrlSafeBase64.encode64(signature.to_s)
}
else
super
end
end
class << self
def decode_compact_serialized(jwt_string, key_or_secret)
case jwt_string.count('.') + 1
when JWS::NUM_OF_SEGMENTS
JWS.decode_compact_serialized jwt_string, key_or_secret
when JWE::NUM_OF_SEGMENTS
JWE.decode_compact_serialized jwt_string, key_or_secret
else
raise InvalidFormat.new("Invalid JWT Format. JWT should include #{JWS::NUM_OF_SEGMENTS} or #{JWE::NUM_OF_SEGMENTS} segments.")
end
end
def decode_json_serialized(input, key_or_secret)
input = input.with_indifferent_access
if (input[:signatures] || input[:signature]).present?
JWS.decode_json_serialized input, key_or_secret
elsif input[:ciphertext].present?
JWE.decode_json_serialized input, key_or_secret
else
raise InvalidFormat.new("Unexpected JOSE JSON Serialization Format.")
end
end
end
end
end
require 'json/jws'
require 'json/jwe'
require 'json/jwk'
require 'json/jwk/jwkizable'
require 'json/jwk/set'
|