Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options for claim-specific leeway #187

Merged
merged 1 commit into from
Feb 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ token = JWT.encode exp_payload, hmac_secret, 'HS256'

begin
# add leeway to ensure the token is still accepted
decoded_token = JWT.decode token, hmac_secret, true, { :leeway => leeway, :algorithm => 'HS256' }
decoded_token = JWT.decode token, hmac_secret, true, { :exp_leeway => leeway, :algorithm => 'HS256' }
rescue JWT::ExpiredSignature
# Handle expired token, e.g. logout user or deny access
end
Expand Down Expand Up @@ -258,7 +258,7 @@ token = JWT.encode nbf_payload, hmac_secret, 'HS256'

begin
# add leeway to ensure the token is valid
decoded_token = JWT.decode token, hmac_secret, true, { :leeway => leeway, :algorithm => 'HS256' }
decoded_token = JWT.decode token, hmac_secret, true, { :nbf_leeway => leeway, :algorithm => 'HS256' }
rescue JWT::ImmatureSignature
# Handle invalid token, e.g. logout user or deny access
end
Expand Down Expand Up @@ -337,6 +337,8 @@ From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.or

> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a ***NumericDate*** value. Use of this claim is OPTIONAL.

**Handle Issued At Claim**

```ruby
iat = Time.now.to_i
iat_payload = { :data => 'data', :iat => iat }
Expand All @@ -351,6 +353,25 @@ rescue JWT::InvalidIatError
end
```

**Adding Leeway**

```ruby
iat = Time.now.to_i + 10
leeway = 30 # seconds

iat_payload = { :data => 'data', :iat => iat }

# build token issued in the future
token = JWT.encode iat_payload, hmac_secret, 'HS256'

begin
# add leeway to ensure the token is accepted
decoded_token = JWT.decode token, hmac_secret, true, { :iat_leeway => leeway, :verify_iat => true, :algorithm => 'HS256' }
rescue JWT::InvalidIatError
# Handle invalid token, e.g. logout user or deny access
end
```

### Subject Claim

From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2):
Expand Down
20 changes: 16 additions & 4 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ def verify_aud
def verify_expiration
return unless @payload.include?('exp')

if @payload['exp'].to_i <= (Time.now.to_i - leeway)
if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
raise(JWT::ExpiredSignature, 'Signature has expired')
end
end

def verify_iat
return unless @payload.include?('iat')

if !@payload['iat'].is_a?(Numeric) || @payload['iat'].to_f > (Time.now.to_f + leeway)
if !@payload['iat'].is_a?(Numeric) || @payload['iat'].to_f > (Time.now.to_f + iat_leeway)
raise(JWT::InvalidIatError, 'Invalid iat')
end
end
Expand Down Expand Up @@ -67,7 +67,7 @@ def verify_jti
def verify_not_before
return unless @payload.include?('nbf')

if @payload['nbf'].to_i > (Time.now.to_i + leeway)
if @payload['nbf'].to_i > (Time.now.to_i + nbf_leeway)
raise(JWT::ImmatureSignature, 'Signature nbf has not been reached')
end
end
Expand All @@ -87,8 +87,20 @@ def extract_option(key)
@options.values_at(key.to_sym, key.to_s).compact.first
end

def leeway
def global_leeway
extract_option :leeway
end

def exp_leeway
extract_option(:exp_leeway) || global_leeway
end

def iat_leeway
extract_option(:iat_leeway) || global_leeway
end

def nbf_leeway
extract_option(:nbf_leeway) || global_leeway
end
end
end
20 changes: 16 additions & 4 deletions spec/jwt/verify_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ module JWT

it 'should allow strings or symbols in options array' do
options['aud'] = [
'ruby-jwt-aud',
'ruby-jwt-aud',
'test-aud',
'ruby-ruby-ruby',
:test
]

array_payload['aud'].push('test')

Verify.verify_aud(array_payload, options)
end
end
Expand All @@ -87,10 +87,14 @@ module JWT
end.to raise_error JWT::ExpiredSignature
end

it 'must allow some leeway in the expiration when configured' do
it 'must allow some leeway in the expiration when global leeway is configured' do
Verify.verify_expiration(payload, options.merge(leeway: 10))
end

it 'must allow some leeway in the expiration when exp_leeway is configured' do
Verify.verify_expiration(payload, options.merge(exp_leeway: 10))
end

it 'must be expired if the exp claim equals the current time' do
payload['exp'] = Time.now.to_i

Expand All @@ -112,6 +116,10 @@ module JWT
Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(leeway: 70))
end

it 'must allow configured iat_leeway' do
Verify.verify_iat(payload.merge('iat' => (iat + 60)), options.merge(iat_leeway: 70))
end

it 'must properly handle integer times' do
Verify.verify_iat(payload.merge('iat' => Time.now.to_i), options)
end
Expand Down Expand Up @@ -191,9 +199,13 @@ module JWT
end.to raise_error JWT::ImmatureSignature
end

it 'must allow some leeway in the token age when configured' do
it 'must allow some leeway in the token age when global leeway is configured' do
Verify.verify_not_before(payload, options.merge(leeway: 10))
end

it 'must allow some leeway in the token age when nbf_leeway is configured' do
Verify.verify_not_before(payload, options.merge(nbf_leeway: 10))
end
end

context '.verify_sub(payload, options)' do
Expand Down