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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
# frozen_string_literal: true
require 'json'
require 'active_support/testing/time_helpers'
RSpec.describe JSONWebToken::HMACToken do
include ActiveSupport::Testing::TimeHelpers
let(:secret) { 'shh secret squirrel' }
shared_examples 'a valid, non-expired token' do
it 'is an Array with two elements' do
expect(decoded_token).to be_a(Array)
expect(decoded_token.count).to eq(2)
end
it 'contains the following keys in the first Array element Hash - jti, iat, nbf, exp' do
expect(decoded_token[0].keys).to include('jti', 'iat', 'nbf', 'exp')
end
it 'contains the following keys in the second Array element Hash - typ and alg' do
expect(decoded_token[1]['typ']).to eql('JWT')
expect(decoded_token[1]['alg']).to eql('HS256')
end
end
describe '.decode' do
let(:leeway) { described_class::IAT_LEEWAY }
let(:decoded_token) { described_class.decode(encoded_token, secret, leeway: leeway) }
context 'with an invalid token' do
context 'that is junk' do
let(:encoded_token) { 'junk' }
it "raises exception saying 'Not enough or too many segments'" do
expect { decoded_token }.to raise_error(JWT::DecodeError, 'Not enough or too many segments')
end
end
context 'that has been fiddled with' do
let(:encoded_token) do
described_class.new(secret).encoded.tap { |token| token[0] = 'E' }
end
it "raises exception saying 'Invalid segment encoding'" do
expect { decoded_token }.to raise_error(JWT::DecodeError, 'Invalid segment encoding')
end
end
context 'that was generated using a different secret' do
let(:encoded_token) { described_class.new('some other secret').encoded }
it "raises exception saying 'Signature verification raised" do
expect { decoded_token }.to raise_error(JWT::VerificationError, 'Signature verification raised')
end
end
context 'that is expired' do
# Needs the ! so freeze_time() is effective
let!(:encoded_token) { described_class.new(secret).encoded }
it "raises exception saying 'Signature has expired'" do
# Needs to be 120 seconds, because the default expiry is 60 seconds
# with an additional 60 second leeway.
travel_to(Time.now + 120) do
expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
end
end
end
end
context 'with a valid token' do
let(:encoded_token) do
hmac_token = described_class.new(secret)
hmac_token.expire_time = Time.now + expire_time
hmac_token.encoded
end
context 'that has expired' do
let(:expire_time) { 0 }
around do |example|
travel_to(Time.now + 1) { example.run }
end
context 'with the default leeway' do
it_behaves_like 'a valid, non-expired token'
end
context 'with a leeway of 0 seconds' do
let(:leeway) { 0 }
it "raises exception saying 'Signature has expired'" do
expect { decoded_token }.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
end
end
end
context 'that has not expired' do
let(:expire_time) { described_class::DEFAULT_EXPIRE_TIME }
it_behaves_like 'a valid, non-expired token'
end
end
end
describe '#encoded' do
let(:decoded_token) { described_class.decode(encoded_token, secret) }
context 'without data' do
let(:encoded_token) { described_class.new(secret).encoded }
it_behaves_like 'a valid, non-expired token'
end
context 'with data' do
let(:data) { { secret_key: 'secret value' }.to_json }
let(:encoded_token) do
ec = described_class.new(secret)
ec[:data] = data
ec.encoded
end
it_behaves_like 'a valid, non-expired token'
it "contains the 'data' key in the first Array element Hash" do
expect(decoded_token[0]).to have_key('data')
end
it 'can re-read back the data' do
expect(decoded_token[0]['data']).to eql(data)
end
end
end
end
|