/
decode.rb
111 lines (92 loc) · 3 KB
/
decode.rb
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
# frozen_string_literal: true
require 'json'
require 'jwt/signature'
require 'jwt/verify'
# JWT::Decode module
module JWT
# Decoding logic for JWT
class Decode
def initialize(jwt, key, verify, options, &keyfinder)
raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
@jwt = jwt
@key = key
@options = options
@segments = jwt.split('.')
@verify = verify
@signature = ''
@keyfinder = keyfinder
end
def decode_segments
validate_segment_count!
if @verify
decode_crypto
verify_signature
verify_claims
end
raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
[payload, header]
end
private
def verify_signature
raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') if allowed_algorithms.empty?
raise(JWT::IncorrectAlgorithm, 'Token is missing alg header') unless header['alg']
raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless options_includes_algo_in_header?
@key = find_key(&@keyfinder) if @keyfinder
@key = ::JWT::JWK::KeyFinder.new(jwks: @options[:jwks]).key_for(header['kid']) if @options[:jwks]
Signature.verify(header['alg'], @key, signing_input, @signature)
end
def options_includes_algo_in_header?
allowed_algorithms.any? { |alg| alg.casecmp(header['alg']).zero? }
end
def allowed_algorithms
# Order is very important - first check for string keys, next for symbols
algos = if @options.key?('algorithm')
@options['algorithm']
elsif @options.key?(:algorithm)
@options[:algorithm]
elsif @options.key?('algorithms')
@options['algorithms']
elsif @options.key?(:algorithms)
@options[:algorithms]
else
[]
end
Array(algos)
end
def find_key(&keyfinder)
key = (keyfinder.arity == 2 ? yield(header, payload) : yield(header))
raise JWT::DecodeError, 'No verification key available' unless key
key
end
def verify_claims
Verify.verify_claims(payload, @options)
Verify.verify_required_claims(payload, @options)
end
def validate_segment_count!
return if segment_length == 3
return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
return if segment_length == 2 && header['alg'] == 'none'
raise(JWT::DecodeError, 'Not enough or too many segments')
end
def segment_length
@segments.count
end
def decode_crypto
@signature = JWT::Base64.url_decode(@segments[2] || '')
end
def header
@header ||= parse_and_decode @segments[0]
end
def payload
@payload ||= parse_and_decode @segments[1]
end
def signing_input
@segments.first(2).join('.')
end
def parse_and_decode(segment)
JWT::JSON.parse(JWT::Base64.url_decode(segment))
rescue ::JSON::ParserError
raise JWT::DecodeError, 'Invalid segment encoding'
end
end
end