rack-prx_auth 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7cde70cde8a0092e5b9b103dca645ead406af8b
4
- data.tar.gz: 36a0c5162508458ec15e9db7572d1cc1244113fd
3
+ metadata.gz: 65365d276d9668d2b774150b51cae69cc94e7d4a
4
+ data.tar.gz: 2f9218bdcfe4b22ac97f45f6040b575e0fa9c665
5
5
  SHA512:
6
- metadata.gz: 805280d7e1fda15de81c390a9c73d89613cc42843452eeff25481abfec36b6882bc8e8fa58312aba0660d7cf8abecea377127fb8912a359ae74214adc5131b7d
7
- data.tar.gz: 5b518e3aecb8e4c9c557c1fb7369a092e0b48f902b6e87a5e56fbd407c68e048ea627dc54572a3700d66cdc636f06b21f20451671f88e5fd1cea3604f7cc4619
6
+ metadata.gz: 253b726a570baa8db347649f171b89f7d955ebec32c65d860aa426a1dda1df0c1fe333d5518b28530f7a9047bc29febf82d5978d8aca71035f3463017ba63625
7
+ data.tar.gz: bdd2a4f396fb85aefd68677becf5fedaf16a8b46482ff8bd6735455afa0de71cb367d07a13743405ec5f8911ea83523d9b69900d55593295c10dc61b2ce8fb62
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /vendor/bundle/
10
11
  *.bundle
11
12
  *.so
12
13
  *.o
data/.travis.yml CHANGED
@@ -1,3 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.0.0-p598
3
4
  - 2.1.1
5
+ - 2.1.2
6
+ - 2.1.3
7
+ - 2.1.4
8
+ - 2.1.5
data/Gemfile CHANGED
@@ -2,3 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rack-prx_auth.gemspec
4
4
  gemspec
5
+
6
+ gem 'guard', '~> 2.6.1'
7
+ gem 'guard-minitest', '~> 2.3.2'
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  guard :minitest, all_after_pass: true do
2
2
  watch(%r{^test/(.*)\/?test_(.*)\.rb})
3
- watch(%r{^lib/rack/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
4
- watch(%r{^lib/rack/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
3
+ watch(%r{^lib/(.*/)?([^/]+)\.rb}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
4
+ watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
5
5
  watch(%r{^lib/(.+)\.rb}) { |m| "test/#{m[1]}_test.rb" }
6
6
  watch(%r{^test/.+_test\.rb})
7
7
  watch(%r{^test/test_helper\.rb}) { 'test' }
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Rack::PrxAuth
2
2
 
3
- TODO: Write a gem description
3
+ [![Gem Version](https://badge.fury.io/rb/rack-prx_auth.svg)](http://badge.fury.io/rb/rack-prx_auth)
4
+ [![Dependency Status](https://gemnasium.com/PRX/rack-prx_auth.svg)](https://gemnasium.com/PRX/rack-prx_auth)
5
+ [![Build Status](https://travis-ci.org/PRX/rack-prx_auth.svg?branch=master)](https://travis-ci.org/PRX/rack-prx_auth)
6
+ [![Code Climate](https://codeclimate.com/github/PRX/rack-prx_auth/badges/gpa.svg)](https://codeclimate.com/github/PRX/rack-prx_auth)
7
+ [![Coverage Status](https://coveralls.io/repos/PRX/rack-prx_auth/badge.svg)](https://coveralls.io/r/PRX/rack-prx_auth)
8
+
9
+ This gem adds middleware to a Rack application that decodes and verified a JSON Web Token (JWT) issued by PRX.org. If the JWT is invalid, the middleware will respond with a 401 Unauthorized. If the JWT was not issued by PRX (or the specified issuer), the request will continue through the middleware stack.
4
10
 
5
11
  ## Installation
6
12
 
@@ -18,9 +24,38 @@ Or install it yourself as:
18
24
 
19
25
  $ gem install rack-prx_auth
20
26
 
27
+ In a non-Rails app, add the following to the application's config.ru file:
28
+
29
+ ```ruby
30
+ use Rack::PrxAuth, cert_location: [CERT LOCATION], issuer: [ISSUER]
31
+ ```
32
+ The `cert_location` and `issuer` parameters are optional. See below.
33
+
21
34
  ## Usage
22
35
 
23
- TODO: Write usage instructions here
36
+ ### The Request
37
+
38
+ Rack-prx_auth looks for a token in the request's HTTP_AUTHORIZATION header. It expects that the header's content will take the form of 'Bearer <your token>'. If no HTTP_AUTHORIZATION header is present, rack-prx_auth passes the request to the next middleware.
39
+
40
+ We have another application that's in charge of making the token. It's called id.prx.org. Its job is to show a form for a user to enter credentials, validate those credentials, and then generate a JWT using PRX's private key. See http://openid.net/specs/openid-connect-implicit-1_0.html to find out what information is encoded in a JWT. Basically it's a hash containing, among other things, the user's ID, the issuer of the token, and when the token expires.
41
+
42
+ ### Configuration
43
+
44
+ Rack-prx_auth takes two optional parameters, `issuer` and `cert_location`. See Installation for how to specify them.
45
+
46
+ By default, rack-prx_auth will assume that you want to make sure the JWT was issued by PRX. After decoding the JWT, rack-prx_auth checks the `issuer` field to make sure it's id.prx.org. If you want it to check for a different issuer, pass `issuer: <your issuer>` as a parameter.
47
+
48
+ Since the JWT was created using PRX's private key, rack-prx_auth needs to fetch PRX's public key to decode it. It does this by accessing the `cert_location` (default is https://id.prx.org/api/v1/certs), generating an OpenSSL::X509::Certificate based on its contents, and determining the public key from the certificate object. Should you wish to get your public key from a different certificate, you may specify a different endpoint by passing `cert_location: <your cert location>` as a parameter. Keep in mind that unless the certificate matches the private key used to make the JWT, rack-prx_auth will return 401.
49
+
50
+ ### The Response
51
+
52
+ If the token isn't valid, meaning it's expired or it wasn't created using our private key, rack-prx_auth will return 401 Unauthorized.
53
+
54
+ If there's nothing in the HTTP_AUTHORIZATION heading, there's something but JSON::JWT can't decode it, or the issuer field doesn't specify the correct issuer, rack-prx_auth just punts to the next piece of middleware.
55
+
56
+ If all goes well, rack-prx_auth takes the decoded JWT and makes a TokenData object. Then it adds this object to the `env` with the key 'prx.auth'.
57
+
58
+ If you are using rack-prx_auth in a Rails app, you'll have a few handy controller methods available to you. Calling `prx_auth_token` within a controller returns the TokenData object, and `prx_authenticated?` tells you whether a TokenData object is available. Also, if you call `user_id` on the TokenData object, you get the user's ID so you can ask id.prx.org for information about them.
24
59
 
25
60
  ## Contributing
26
61
 
data/Rakefile CHANGED
@@ -3,7 +3,8 @@ require 'rake'
3
3
  require 'rake/testtask'
4
4
 
5
5
  Rake::TestTask.new do |t|
6
- t.pattern = 'test/*test.rb'
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*test.rb'
7
8
  end
8
9
 
9
10
  task default: :test
@@ -0,0 +1,55 @@
1
+ require 'json/jwt'
2
+ require 'net/http'
3
+
4
+ module Rack
5
+ class PrxAuth
6
+ class Certificate
7
+ EXPIRES_IN = 43200
8
+ DEFAULT_CERT_LOC = URI('https://id.prx.org/api/v1/certs')
9
+
10
+ attr_reader :cert_location
11
+
12
+ def initialize(cert_uri = nil)
13
+ @cert_location = cert_uri.nil? ? DEFAULT_CERT_LOC : URI(cert_uri)
14
+ end
15
+
16
+ def valid?(token)
17
+ begin
18
+ JSON::JWT.decode(token, public_key)
19
+ rescue JSON::JWT::VerificationFailed
20
+ false
21
+ else
22
+ true
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def public_key
29
+ certificate.public_key
30
+ end
31
+
32
+ def certificate
33
+ if @certificate.nil? || needs_refresh?
34
+ @certificate = fetch
35
+ end
36
+ @certificate
37
+ end
38
+
39
+ def fetch
40
+ certs = JSON.parse(Net::HTTP.get(cert_location))
41
+ cert_string = certs['certificates'].values[0]
42
+ @refresh_at = Time.now.to_i + EXPIRES_IN
43
+ OpenSSL::X509::Certificate.new(cert_string)
44
+ end
45
+
46
+ def needs_refresh?
47
+ expired? || @refresh_at <= Time.now.to_i
48
+ end
49
+
50
+ def expired?
51
+ @certificate.not_after < Time.now
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class PrxAuth
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
data/lib/rack/prx_auth.rb CHANGED
@@ -1,34 +1,47 @@
1
1
  require 'json/jwt'
2
2
  require 'rack/prx_auth/version'
3
- require 'rack/prx_auth/public_key'
3
+ require 'rack/prx_auth/certificate'
4
4
  require 'rack/prx_auth/token_data'
5
- require 'rack/prx_auth/railtie' if defined?(Rails)
6
5
 
7
6
  module Rack
8
7
  class PrxAuth
9
- attr_reader :public_key
8
+ INVALID_TOKEN = [
9
+ 401, {'Content-Type' => 'application/json'},
10
+ [{status: 401, error: 'Invalid JSON Web Token'}.to_json]
11
+ ]
12
+
13
+ DEFAULT_ISS = 'id.prx.org'
14
+
15
+ attr_reader :issuer
10
16
 
11
17
  def initialize(app, options = {})
12
18
  @app = app
13
- @public_key = PublicKey.new(options[:cert_location])
19
+ @certificate = Certificate.new(options[:cert_location])
20
+ @issuer = options[:issuer] || DEFAULT_ISS
14
21
  end
15
22
 
16
23
  def call(env)
17
- token = (env['HTTP_AUTHORIZATION'] || 'no token').split[1]
24
+ return @app.call(env) unless env['HTTP_AUTHORIZATION']
25
+
26
+ token = env['HTTP_AUTHORIZATION'].split[1]
18
27
  claims = decode_token(token)
19
28
 
20
- if claims['iss'] == 'auth.prx.org'
21
- if verified?(token) && !token_expired?(claims) && !cert_expired?(@public_key.certificate)
22
- env['prx.auth'] = TokenData.new(claims)
23
- @app.call(env)
24
- else
25
- [401, {'Content-Type' => 'application/json'}, [{status: 401, error: 'Invalid JSON Web Token'}.to_json]]
26
- end
27
- else
29
+ return @app.call(env) unless should_validate_token?(claims)
30
+
31
+ if valid?(claims, token)
32
+ env['prx.auth'] = TokenData.new(claims)
28
33
  @app.call(env)
34
+ else
35
+ INVALID_TOKEN
29
36
  end
30
37
  end
31
38
 
39
+ private
40
+
41
+ def valid?(claims, token)
42
+ !expired?(claims) && @certificate.valid?(token)
43
+ end
44
+
32
45
  def decode_token(token)
33
46
  begin
34
47
  JSON::JWT.decode(token, :skip_verification)
@@ -37,22 +50,12 @@ module Rack
37
50
  end
38
51
  end
39
52
 
40
- def verified?(token)
41
- begin
42
- JSON::JWT.decode(token, @public_key.key)
43
- rescue JSON::JWT::VerificationFailed
44
- false
45
- else
46
- true
47
- end
48
- end
49
-
50
- def cert_expired?(certificate)
51
- certificate.not_after < Time.now
53
+ def expired?(claims)
54
+ Time.now.to_i > (claims['iat'] + claims['exp'])
52
55
  end
53
56
 
54
- def token_expired?(claims)
55
- Time.now.to_i > (claims['iat'] + claims['exp'])
57
+ def should_validate_token?(claims)
58
+ claims['iss'] == @issuer
56
59
  end
57
60
  end
58
61
  end
@@ -6,23 +6,21 @@ require 'rack/prx_auth/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "rack-prx_auth"
8
8
  spec.version = Rack::PrxAuth::VERSION
9
- spec.authors = ["Eve Asher"]
10
- spec.email = ["eve@prx.org"]
9
+ spec.authors = ["Eve Asher", "Chris Rhoden"]
10
+ spec.email = ["eve@prx.org", "carhoden@gmail.com"]
11
11
  spec.summary = %q{Rack middleware that verifies and decodes a JWT token and attaches the token's claims to env.}
12
12
  spec.description = %q{Specific to PRX. Will ignore tokens that were not issued by PRX.}
13
- spec.homepage = ""
13
+ spec.homepage = "https://github.com/PRX/rack-prx_auth"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.test_files = spec.files.grep(%r{^test/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency 'bundler', '~> 1.7'
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
22
  spec.add_development_dependency 'rake', '~> 10.0'
23
- spec.add_development_dependency 'guard', '~> 2.6', '>= 2.6.1'
24
- spec.add_development_dependency 'guard-minitest', '~> 2.3', '>= 2.3.2'
25
- spec.add_development_dependency 'minitest-stub_any_instance'
23
+ spec.add_development_dependency 'coveralls', '~> 0'
26
24
 
27
25
  spec.add_dependency 'rack', '~> 1.5', '>= 1.5.2'
28
26
  spec.add_dependency 'json', '~> 1.8', '>= 1.8.1'
@@ -0,0 +1,130 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth::Certificate do
4
+ let(:subject) { Rack::PrxAuth::Certificate.new }
5
+ let(:certificate) { subject }
6
+
7
+ describe '#initialize' do
8
+ it 'allows setting the location of the certificates' do
9
+ cert = Rack::PrxAuth::Certificate.new('http://example.com')
10
+ cert.cert_location.must_equal URI('http://example.com')
11
+ end
12
+
13
+ it 'defaults to DEFAULT_CERT_LOC' do
14
+ certificate.cert_location.must_equal Rack::PrxAuth::Certificate::DEFAULT_CERT_LOC
15
+ end
16
+ end
17
+
18
+ describe '#valid?' do
19
+ it 'validates the token with the public key' do
20
+ token, key = nil, nil
21
+ certificate.stub(:public_key, :public_key) do
22
+ JSON::JWT.stub(:decode, Proc.new {|t, k| token, key = t, k }) do
23
+ certificate.valid?(:token)
24
+ end
25
+ end
26
+
27
+ token.must_equal :token
28
+ key.must_equal :public_key
29
+ end
30
+
31
+ it 'returns false if verification fails' do
32
+ JSON::JWT.stub(:decode, Proc.new do |t, k|
33
+ raise JSON::JWT::VerificationFailed
34
+ end) do
35
+ certificate.stub(:public_key, :foo) do
36
+ certificate.wont_be :valid?, :token
37
+ end
38
+ end
39
+ end
40
+
41
+ it 'returns true if verification passes' do
42
+ JSON::JWT.stub(:decode, {}) do
43
+ certificate.stub(:public_key, :foo) do
44
+ certificate.must_be :valid?, :token
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#certificate' do
51
+ it 'calls fetch if unprimed' do
52
+ def certificate.fetch
53
+ :sigil
54
+ end
55
+
56
+ certificate.send(:certificate).must_equal :sigil
57
+ end
58
+ end
59
+
60
+ describe '#public_key' do
61
+ it 'pulls from the certificate' do
62
+ certificate.stub(:certificate, Struct.new(:public_key).new(:key)) do
63
+ certificate.send(:public_key).must_equal :key
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#fetch' do
69
+ it 'pulls from `#cert_location`' do
70
+ Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
71
+ OpenSSL::X509::Certificate.stub(:new, ->(x) { x }) do
72
+ certificate.stub(:cert_location, "a://fake.url/here") do
73
+ certificate.send(:fetch).must_equal "a://fake.url/here"
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ it 'sets the expiration value' do
80
+ Net::HTTP.stub(:get, ->(x) { "{\"certificates\":{\"asdf\":\"#{x}\"}}" }) do
81
+ OpenSSL::X509::Certificate.stub(:new, ->(_) { Struct.new(:not_after).new(Time.now + 10000) }) do
82
+ certificate.send :certificate
83
+ certificate.wont_be :needs_refresh?
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#expired?' do
90
+ let(:stub_cert) { Struct.new(:not_after).new(Time.now + 10000) }
91
+ before(:each) do
92
+ certificate.instance_variable_set :'@certificate', stub_cert
93
+ end
94
+
95
+ it 'is false when the certificate is not expired' do
96
+ certificate.wont_be :expired?
97
+ end
98
+
99
+ it 'is true when the certificate is expired' do
100
+ stub_cert.not_after = Time.now - 500
101
+ certificate.must_be :expired?
102
+ end
103
+ end
104
+
105
+ describe '#needs_refresh?' do
106
+ def refresh_at=(time)
107
+ certificate.instance_variable_set :'@refresh_at', time
108
+ end
109
+
110
+ it 'is true if certificate is expired' do
111
+ certificate.stub(:expired?, true) do
112
+ certificate.must_be :needs_refresh?
113
+ end
114
+ end
115
+
116
+ it 'is true if we are past refresh value' do
117
+ self.refresh_at = Time.now.to_i - 1000
118
+ certificate.stub(:expired?, false) do
119
+ certificate.must_be :needs_refresh?
120
+ end
121
+ end
122
+
123
+ it 'is false if certificate is not expired and refresh is in the future' do
124
+ self.refresh_at = Time.now.to_i + 10000
125
+ certificate.stub(:expired?, false) do
126
+ certificate.wont_be :needs_refresh?
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+ describe Rack::PrxAuth do
4
+ let(:app) { Proc.new {|env| env } }
5
+ let(:prxauth) { Rack::PrxAuth.new(app) }
6
+ let(:fake_token) { 'afawefawefawefawegstgnsrtiohnlijblublwjnvrtoign'}
7
+ let(:env) { {'HTTP_AUTHORIZATION' => 'Bearer ' + fake_token } }
8
+ let(:claims) { {'sub'=>3, 'exp'=>3600, 'iat'=>Time.now.to_i, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'id.prx.org'} }
9
+
10
+ describe '#call' do
11
+ it 'does nothing if there is no authorization header' do
12
+ env = {}
13
+
14
+ prxauth.call(env.clone).must_equal env
15
+ end
16
+
17
+ it 'does nothing if the token is from another issuer' do
18
+ claims['iss'] = 'auth.elsewhere.org'
19
+
20
+ JSON::JWT.stub(:decode, claims) do
21
+ prxauth.call(env.clone).must_equal env
22
+ end
23
+ end
24
+
25
+ it 'does nothing if token is invalid' do
26
+ prxauth.call(env.clone).must_equal env
27
+ end
28
+
29
+ it 'returns 401 if verification fails' do
30
+ JSON::JWT.stub(:decode, claims) do
31
+ prxauth.stub(:valid?, false) do
32
+ prxauth.call(env).must_equal Rack::PrxAuth::INVALID_TOKEN
33
+ end
34
+ end
35
+ end
36
+
37
+ it 'returns 401 if access token has expired' do
38
+ JSON::JWT.stub(:decode, claims) do
39
+ prxauth.stub(:expired?, true) do
40
+ prxauth.call(env).must_equal Rack::PrxAuth::INVALID_TOKEN
41
+ end
42
+ end
43
+ end
44
+
45
+ it 'attaches claims to request params if verification passes' do
46
+ prxauth.stub(:decode_token, claims) do
47
+ prxauth.stub(:valid?, true) do
48
+ prxauth.call(env)['prx.auth'].tap do |token|
49
+ token.must_be_instance_of Rack::PrxAuth::TokenData
50
+ token.attributes.must_equal claims
51
+ token.user_id.must_equal claims['sub']
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ describe '#token_expired?' do
59
+ it 'returns true if token is expired' do
60
+ claims['iat'] = Time.now.to_i - 4000
61
+
62
+ prxauth.send(:expired?, claims).must_equal true
63
+ end
64
+
65
+ it 'returns false if it is valid' do
66
+ prxauth.send(:expired?, claims).must_equal false
67
+ end
68
+ end
69
+
70
+ describe 'initialize' do
71
+ it 'takes a certificate location as an option' do
72
+ loc = nil
73
+ Rack::PrxAuth::Certificate.stub(:new, Proc.new{|l| loc = l}) do
74
+ Rack::PrxAuth.new(app, cert_location: :location)
75
+ loc.must_equal :location
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,9 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
+ require 'rack/prx_auth'
6
+
7
+ require 'minitest/autorun'
8
+ require 'minitest/spec'
9
+ require 'minitest/pride'
metadata CHANGED
@@ -1,181 +1,142 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-prx_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eve Asher
8
+ - Chris Rhoden
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-11-20 00:00:00.000000000 Z
12
+ date: 2015-04-22 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - "~>"
18
+ - - ~>
18
19
  - !ruby/object:Gem::Version
19
- version: '1.7'
20
+ version: '1.3'
20
21
  type: :development
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - "~>"
25
+ - - ~>
25
26
  - !ruby/object:Gem::Version
26
- version: '1.7'
27
+ version: '1.3'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: rake
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - "~>"
32
+ - - ~>
32
33
  - !ruby/object:Gem::Version
33
34
  version: '10.0'
34
35
  type: :development
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - "~>"
39
+ - - ~>
39
40
  - !ruby/object:Gem::Version
40
41
  version: '10.0'
41
42
  - !ruby/object:Gem::Dependency
42
- name: guard
43
+ name: coveralls
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.6'
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: 2.6.1
51
- type: :development
52
- prerelease: false
53
- version_requirements: !ruby/object:Gem::Requirement
54
- requirements:
55
- - - "~>"
56
- - !ruby/object:Gem::Version
57
- version: '2.6'
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 2.6.1
61
- - !ruby/object:Gem::Dependency
62
- name: guard-minitest
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '2.3'
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 2.3.2
71
- type: :development
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - "~>"
76
- - !ruby/object:Gem::Version
77
- version: '2.3'
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 2.3.2
81
- - !ruby/object:Gem::Dependency
82
- name: minitest-stub_any_instance
83
- requirement: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - ">="
46
+ - - ~>
86
47
  - !ruby/object:Gem::Version
87
48
  version: '0'
88
49
  type: :development
89
50
  prerelease: false
90
51
  version_requirements: !ruby/object:Gem::Requirement
91
52
  requirements:
92
- - - ">="
53
+ - - ~>
93
54
  - !ruby/object:Gem::Version
94
55
  version: '0'
95
56
  - !ruby/object:Gem::Dependency
96
57
  name: rack
97
58
  requirement: !ruby/object:Gem::Requirement
98
59
  requirements:
99
- - - "~>"
60
+ - - ~>
100
61
  - !ruby/object:Gem::Version
101
62
  version: '1.5'
102
- - - ">="
63
+ - - '>='
103
64
  - !ruby/object:Gem::Version
104
65
  version: 1.5.2
105
66
  type: :runtime
106
67
  prerelease: false
107
68
  version_requirements: !ruby/object:Gem::Requirement
108
69
  requirements:
109
- - - "~>"
70
+ - - ~>
110
71
  - !ruby/object:Gem::Version
111
72
  version: '1.5'
112
- - - ">="
73
+ - - '>='
113
74
  - !ruby/object:Gem::Version
114
75
  version: 1.5.2
115
76
  - !ruby/object:Gem::Dependency
116
77
  name: json
117
78
  requirement: !ruby/object:Gem::Requirement
118
79
  requirements:
119
- - - "~>"
80
+ - - ~>
120
81
  - !ruby/object:Gem::Version
121
82
  version: '1.8'
122
- - - ">="
83
+ - - '>='
123
84
  - !ruby/object:Gem::Version
124
85
  version: 1.8.1
125
86
  type: :runtime
126
87
  prerelease: false
127
88
  version_requirements: !ruby/object:Gem::Requirement
128
89
  requirements:
129
- - - "~>"
90
+ - - ~>
130
91
  - !ruby/object:Gem::Version
131
92
  version: '1.8'
132
- - - ">="
93
+ - - '>='
133
94
  - !ruby/object:Gem::Version
134
95
  version: 1.8.1
135
96
  - !ruby/object:Gem::Dependency
136
97
  name: json-jwt
137
98
  requirement: !ruby/object:Gem::Requirement
138
99
  requirements:
139
- - - "~>"
100
+ - - ~>
140
101
  - !ruby/object:Gem::Version
141
102
  version: '0.7'
142
- - - ">="
103
+ - - '>='
143
104
  - !ruby/object:Gem::Version
144
105
  version: 0.7.0
145
106
  type: :runtime
146
107
  prerelease: false
147
108
  version_requirements: !ruby/object:Gem::Requirement
148
109
  requirements:
149
- - - "~>"
110
+ - - ~>
150
111
  - !ruby/object:Gem::Version
151
112
  version: '0.7'
152
- - - ">="
113
+ - - '>='
153
114
  - !ruby/object:Gem::Version
154
115
  version: 0.7.0
155
116
  description: Specific to PRX. Will ignore tokens that were not issued by PRX.
156
117
  email:
157
118
  - eve@prx.org
119
+ - carhoden@gmail.com
158
120
  executables: []
159
121
  extensions: []
160
122
  extra_rdoc_files: []
161
123
  files:
162
- - ".gitignore"
163
- - ".travis.yml"
124
+ - .gitignore
125
+ - .travis.yml
164
126
  - Gemfile
165
127
  - Guardfile
166
128
  - LICENSE
167
129
  - README.md
168
130
  - Rakefile
169
131
  - lib/rack/prx_auth.rb
170
- - lib/rack/prx_auth/controller_methods.rb
171
- - lib/rack/prx_auth/public_key.rb
172
- - lib/rack/prx_auth/railtie.rb
132
+ - lib/rack/prx_auth/certificate.rb
173
133
  - lib/rack/prx_auth/token_data.rb
174
134
  - lib/rack/prx_auth/version.rb
175
135
  - rack-prx_auth.gemspec
176
- - test/controller_methods_test.rb
177
- - test/rack-prx_auth_test.rb
178
- homepage: ''
136
+ - test/rack/prx_auth/certificate_test.rb
137
+ - test/rack/prx_auth_test.rb
138
+ - test/test_helper.rb
139
+ homepage: https://github.com/PRX/rack-prx_auth
179
140
  licenses:
180
141
  - MIT
181
142
  metadata: {}
@@ -185,21 +146,22 @@ require_paths:
185
146
  - lib
186
147
  required_ruby_version: !ruby/object:Gem::Requirement
187
148
  requirements:
188
- - - ">="
149
+ - - '>='
189
150
  - !ruby/object:Gem::Version
190
151
  version: '0'
191
152
  required_rubygems_version: !ruby/object:Gem::Requirement
192
153
  requirements:
193
- - - ">="
154
+ - - '>='
194
155
  - !ruby/object:Gem::Version
195
156
  version: '0'
196
157
  requirements: []
197
158
  rubyforge_project:
198
- rubygems_version: 2.2.2
159
+ rubygems_version: 2.1.11
199
160
  signing_key:
200
161
  specification_version: 4
201
162
  summary: Rack middleware that verifies and decodes a JWT token and attaches the token's
202
163
  claims to env.
203
164
  test_files:
204
- - test/controller_methods_test.rb
205
- - test/rack-prx_auth_test.rb
165
+ - test/rack/prx_auth/certificate_test.rb
166
+ - test/rack/prx_auth_test.rb
167
+ - test/test_helper.rb
@@ -1,11 +0,0 @@
1
- class Rack::PrxAuth
2
- module ControllerMethods
3
- def prx_auth_token
4
- request.env['prx.auth']
5
- end
6
-
7
- def prx_authenticated?
8
- !!prx_auth_token
9
- end
10
- end
11
- end
@@ -1,37 +0,0 @@
1
- module Rack
2
- class PrxAuth
3
- class PublicKey
4
- EXPIRES_IN = 43200
5
-
6
- attr_reader :certificate, :cert_location
7
-
8
- def initialize(cert_location = nil)
9
- @created_at = Time.now
10
- @cert_location = URI(cert_location || 'https://auth.prx.org/api/v1/certs')
11
- get_key
12
- end
13
-
14
- def refresh_key
15
- if Time.now > @created_at + EXPIRES_IN
16
- get_key
17
- end
18
- end
19
-
20
- def get_key
21
- @certificate = get_certificate
22
- @key = @certificate.public_key
23
- end
24
-
25
- def get_certificate
26
- certs = JSON.parse(Net::HTTP.get(@cert_location))
27
- cert_string = certs['certificates'].values[0]
28
- OpenSSL::X509::Certificate.new(cert_string)
29
- end
30
-
31
- def key
32
- refresh_key
33
- @key
34
- end
35
- end
36
- end
37
- end
@@ -1,15 +0,0 @@
1
- require 'rails/railtie'
2
- require 'rack/prx_auth/controller_methods'
3
- require 'rack/prx_auth'
4
-
5
- class Rack::PrxAuth
6
- class Railtie < Rails::Railtie
7
- config.to_prepare do
8
- ApplicationController.send(:include, Rack::PrxAuth::ControllerMethods)
9
- end
10
-
11
- initializer 'rack-prx_auth.insert_middleware' do |app|
12
- app.config.middleware.insert_before ActionDispatch::ParamsParser, 'Rack::PrxAuth'
13
- end
14
- end
15
- end
@@ -1,44 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/spec'
3
- require 'minitest/pride'
4
- require 'rack/prx_auth'
5
- require 'rack/prx_auth/controller_methods'
6
- require 'rack/prx_auth/token_data'
7
-
8
- class FakeController
9
- include Rack::PrxAuth::ControllerMethods
10
- attr_accessor :request
11
-
12
- Request = Struct.new('Request', :env)
13
-
14
- def initialize(env)
15
- self.request = Request.new(env)
16
- end
17
- end
18
-
19
- describe Rack::PrxAuth::ControllerMethods do
20
- let(:claims) { {'sub'=>nil, 'exp'=>3600, 'iat'=>Time.now.to_i, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'auth.prx.org'} }
21
- let(:token_data) { Rack::PrxAuth::TokenData.new(claims) }
22
- let(:env) { {'prx.auth' => token_data } }
23
- let(:empty_env) { Hash.new }
24
-
25
- describe '#prx_auth_token' do
26
- it 'returns the token data object' do
27
- FakeController.new(env).prx_auth_token.must_equal token_data
28
- end
29
-
30
- it 'returns nil if there is no prx.auth field' do
31
- FakeController.new(empty_env).prx_auth_token.must_be_nil
32
- end
33
- end
34
-
35
- describe '#prx_authenticated?' do
36
- it 'returns false if there is no token data' do
37
- FakeController.new(empty_env).prx_authenticated?.must_equal false
38
- end
39
-
40
- it 'must be true if there is token data' do
41
- FakeController.new(env).prx_authenticated?.must_equal true
42
- end
43
- end
44
- end
@@ -1,129 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'minitest/spec'
3
- require 'minitest/pride'
4
- require 'minitest/stub_any_instance'
5
- require 'rack/prx_auth'
6
-
7
- describe Rack::PrxAuth do
8
- let(:app) { Proc.new {|env| env } }
9
- let(:prxauth) { Rack::PrxAuth.new(app) }
10
- let(:fake_token) { 'afawefawefawefawegstgnsrtiohnlijblublwjnvrtoign'}
11
- let(:env) { {'HTTP_AUTHORIZATION' => 'Bearer ' + fake_token } }
12
- let(:claims) { {'sub'=>3, 'exp'=>3600, 'iat'=>Time.now.to_i, 'token_type'=>'bearer', 'scope'=>nil, 'iss'=>'auth.prx.org'} }
13
-
14
- describe '#call' do
15
- it 'does nothing if there is no authorization header' do
16
- env = {}
17
-
18
- prxauth.call(env).must_equal env
19
- end
20
-
21
- it 'does nothing if the token is from another issuer' do
22
- claims['iss'] = 'auth.elsewhere.org'
23
-
24
- JSON::JWT.stub(:decode, claims) do
25
- prxauth.call(env).must_equal env
26
- end
27
- end
28
-
29
- it 'does nothing if token is invalid' do
30
- prxauth.call(env).must_equal env
31
- end
32
-
33
- it 'returns 401 if verification fails' do
34
- JSON::JWT.stub(:decode, claims) do
35
- prxauth.stub(:verified?, false) do
36
- prxauth.call(env).must_equal [401, {'Content-Type' => 'application/json'}, [{status: 401, error: 'Invalid JSON Web Token'}.to_json]]
37
- end
38
- end
39
- end
40
-
41
- it 'returns 401 if access token has expired' do
42
- JSON::JWT.stub(:decode, claims) do
43
- prxauth.stub(:token_expired?, true) do
44
- prxauth.call(env).must_equal [401, {'Content-Type' => 'application/json'}, [{status: 401, error: 'Invalid JSON Web Token'}.to_json]]
45
- end
46
- end
47
- end
48
-
49
- it 'returns 401 if certificate has expired' do
50
- JSON::JWT.stub(:decode, claims) do
51
- prxauth.stub(:cert_expired?, true) do
52
- prxauth.call(env).must_equal [401, {'Content-Type' => 'application/json'}, [{status: 401, error: 'Invalid JSON Web Token'}.to_json]]
53
- end
54
- end
55
- end
56
-
57
- it 'attaches claims to request params if verification passes' do
58
- JSON::JWT.stub(:decode, claims) do
59
- prxauth.call(env)['prx.auth'].must_be_instance_of Rack::PrxAuth::TokenData
60
- prxauth.call(env)['prx.auth'].attributes.must_equal claims
61
- prxauth.call(env)['prx.auth'].user_id.must_equal claims['sub']
62
- end
63
- end
64
- end
65
-
66
- describe '#token_expired?' do
67
- it 'returns true if token is expired' do
68
- claims['iat'] = Time.now.to_i - 4000
69
-
70
- prxauth.token_expired?(claims).must_equal true
71
- end
72
-
73
- it 'returns false if it is valid' do
74
- prxauth.token_expired?(claims).must_equal false
75
- end
76
- end
77
-
78
- describe '#cert_expired?' do
79
- let(:cert) { prxauth.public_key.certificate }
80
-
81
- it 'returns true if cert is expired' do
82
- cert.stub(:not_after, Time.now - 100000) do
83
- prxauth.cert_expired?(cert).must_equal true
84
- end
85
- end
86
-
87
- it 'returns false if it is valid' do
88
- cert.stub(:not_after, Time.now + 100000) do
89
- prxauth.cert_expired?(cert).must_equal false
90
- end
91
- end
92
- end
93
-
94
- describe '#verified?' do
95
- it 'returns false if error is raised' do
96
- raise_error = Proc.new { raise JSON::JWT::VerificationFailed }
97
-
98
- JSON::JWT.stub(:decode, raise_error) do
99
- prxauth.verified?(fake_token).must_equal false
100
- end
101
- end
102
-
103
- it 'returns true if no error is raised' do
104
- JSON::JWT.stub(:decode, claims) do
105
- prxauth.verified?(fake_token).must_equal true
106
- end
107
- end
108
- end
109
-
110
- describe 'initialize' do
111
- it 'takes a certificate location as an option' do
112
- Rack::PrxAuth::PublicKey.stub_any_instance(:get_key, nil) do
113
- prxauth = Rack::PrxAuth.new(app, cert_location: 'http://www.prx-auth.org/api/v1/certs')
114
- key = prxauth.public_key
115
- key.cert_location.host.must_equal 'www.prx-auth.org'
116
- key.cert_location.path.must_equal '/api/v1/certs'
117
- end
118
- end
119
-
120
- it 'uses auth.prx.org if no uri is given' do
121
- Rack::PrxAuth::PublicKey.stub_any_instance(:get_key, nil) do
122
- prxauth = Rack::PrxAuth.new(app)
123
- key = prxauth.public_key
124
- key.cert_location.host.must_equal 'auth.prx.org'
125
- key.cert_location.path.must_equal '/api/v1/certs'
126
- end
127
- end
128
- end
129
- end