rack-prx_auth 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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