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

Allow string hash keys in validation configurations #130

Merged
merged 6 commits into from
Feb 24, 2016
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
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ If you have further questions releated to development or usage, join us: [ruby-j

## Installing

### Using Rubygems:
```bash
sudo gem install jwt
```

### Using Bundler:
Add the following to your Gemfile
```
gem 'jwt'
```
And run `bundle install`

## Algorithms and Usage

The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/).
Expand Down Expand Up @@ -328,16 +336,16 @@ end

# Development and Tests

We depend on [Echoe](http://rubygems.org/gems/echoe) for defining gemspec and performing releases to rubygems.org, which can be done with
We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with

```bash
rake release
```

The tests are written with rspec. Given you have rake and rspec, you can run tests with
The tests are written with rspec. Given you have installed the dependencies via bundler, you can run tests with

```bash
rake test
bundle exec rspec
```

**If you want a release cut with your PR, please include a version bump according to [Semantic Versioning](http://semver.org/)**
Expand Down
11 changes: 1 addition & 10 deletions lib/jwt.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
require 'base64'
require 'openssl'
require 'jwt/decode'
require 'jwt/error'
require 'jwt/json'

# JSON Web Token implementation
#
# Should be up to date with the latest spec:
# https://tools.ietf.org/html/rfc7519#section-4.1.5
module JWT
class DecodeError < StandardError; end
class VerificationError < DecodeError; end
class ExpiredSignature < DecodeError; end
class IncorrectAlgorithm < DecodeError; end
class ImmatureSignature < DecodeError; end
class InvalidIssuerError < DecodeError; end
class InvalidIatError < DecodeError; end
class InvalidAudError < DecodeError; end
class InvalidSubError < DecodeError; end
class InvalidJtiError < DecodeError; end
extend JWT::Json

NAMED_CURVES = {
Expand Down
12 changes: 12 additions & 0 deletions lib/jwt/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module JWT
class DecodeError < StandardError; end
class VerificationError < DecodeError; end
class ExpiredSignature < DecodeError; end
class IncorrectAlgorithm < DecodeError; end
class ImmatureSignature < DecodeError; end
class InvalidIssuerError < DecodeError; end
class InvalidIatError < DecodeError; end
class InvalidAudError < DecodeError; end
class InvalidSubError < DecodeError; end
class InvalidJtiError < DecodeError; end
end
107 changes: 66 additions & 41 deletions lib/jwt/verify.rb
Original file line number Diff line number Diff line change
@@ -1,73 +1,98 @@
require 'jwt/error'

module JWT
# JWT verify methods
module Verify
def self.verify_expiration(payload, options)
return unless payload.include?('exp')

if payload['exp'].to_i < (Time.now.to_i - options[:leeway])
fail(JWT::ExpiredSignature, 'Signature has expired')
class Verify
class << self
%i[verify_aud verify_expiration verify_iat verify_iss verify_jti verify_not_before verify_sub].each do |method_name|
define_method method_name do |payload, options|
new(payload, options).send(method_name)
end
end
end

def self.verify_not_before(payload, options)
return unless payload.include?('nbf')

if payload['nbf'].to_i > (Time.now.to_i + options[:leeway])
fail(JWT::ImmatureSignature, 'Signature nbf has not been reached')
end
def initialize(payload, options)
@payload = payload
@options = options
end

def self.verify_iss(payload, options)
return unless options[:iss]
def verify_aud
return unless options_aud = extract_option(:aud)

if payload['iss'].to_s != options[:iss].to_s
if @payload['aud'].is_a?(Array)
fail(
JWT::InvalidIssuerError,
"Invalid issuer. Expected #{options[:iss]}, received #{payload['iss'] || '<none>'}"
)
JWT::InvalidAudError,
'Invalid audience'
) unless @payload['aud'].include?(options_aud.to_s)
else
fail(
JWT::InvalidAudError,
"Invalid audience. Expected #{options_aud}, received #{@payload['aud'] || '<none>'}"
) unless @payload['aud'].to_s == options_aud.to_s
end
end

def self.verify_iat(payload, options)
return unless payload.include?('iat')
def verify_expiration
return unless @payload.include?('exp')

if !(payload['iat'].is_a?(Integer)) || payload['iat'].to_i > (Time.now.to_i + options[:leeway])
fail(JWT::InvalidIatError, 'Invalid iat')
if @payload['exp'].to_i < (Time.now.to_i - leeway)
fail(JWT::ExpiredSignature, 'Signature has expired')
end
end

def self.verify_jti(payload, _options)
if _options[:verify_jti].class == Proc
fail(JWT::InvalidJtiError, 'Invalid jti') unless _options[:verify_jti].call(payload['jti'])
else
fail(JWT::InvalidJtiError, 'Missing jti') if payload['jti'].to_s == ''
def verify_iat
return unless @payload.include?('iat')

if !(@payload['iat'].is_a?(Integer)) || @payload['iat'].to_i > (Time.now.to_i + leeway)
fail(JWT::InvalidIatError, 'Invalid iat')
end
end

def self.verify_aud(payload, options)
return unless options[:aud]
def verify_iss
return unless options_iss = extract_option(:iss)

if payload[:aud].is_a?(Array)
if @payload['iss'].to_s != options_iss.to_s
fail(
JWT::InvalidAudError,
'Invalid audience'
) unless payload['aud'].include?(options[:aud].to_s)
JWT::InvalidIssuerError,
"Invalid issuer. Expected #{options_iss}, received #{@payload['iss'] || '<none>'}"
)
end
end

def verify_jti
options_verify_jti = extract_option(:verify_jti)
if options_verify_jti.respond_to?(:call)
fail(JWT::InvalidJtiError, 'Invalid jti') unless options_verify_jti.call(@payload['jti'])
else
fail(
JWT::InvalidAudError,
"Invalid audience. Expected #{options[:aud]}, received #{payload['aud'] || '<none>'}"
) unless payload['aud'].to_s == options[:aud].to_s
fail(JWT::InvalidJtiError, 'Missing jti') if @payload['jti'].to_s.strip.empty?
end
end

def self.verify_sub(payload, options)
return unless options[:sub]
def verify_not_before
return unless @payload.include?('nbf')

if @payload['nbf'].to_i > (Time.now.to_i + leeway)
fail(JWT::ImmatureSignature, 'Signature nbf has not been reached')
end
end

def verify_sub
return unless options_sub = extract_option(:sub)

fail(
JWT::InvalidSubError,
"Invalid subject. Expected #{options[:sub]}, received #{payload['sub'] || '<none>'}"
) unless payload['sub'].to_s == options[:sub].to_s
"Invalid subject. Expected #{options_sub}, received #{@payload['sub'] || '<none>'}"
) unless @payload['sub'].to_s == options_sub.to_s
end

private

def extract_option(key)
@options.values_at(key.to_sym, key.to_s).compact.first
end

def leeway
extract_option :leeway
end
end
end
2 changes: 1 addition & 1 deletion lib/jwt/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module VERSION
# tiny version
TINY = 3
# alpha, beta, etc. tag
PRE = 'dev'
PRE = nil

# Build version string
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
Expand Down
1 change: 1 addition & 0 deletions ruby-jwt.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'simplecov'
spec.add_development_dependency 'simplecov-json'
spec.add_development_dependency 'codeclimate-test-reporter'
spec.add_development_dependency 'byebug'
end
Loading