authentic-jwt 0.0.4 → 0.0.5

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: 59f788359a77abb136b7c38fdcab253d45c00f13
4
- data.tar.gz: 5d21ada90226e6763e18f4e360e5f1eed29ad991
3
+ metadata.gz: 3e6fcda5fe67f9a0e8a4e2831f3d4415ddf32cc9
4
+ data.tar.gz: 15d4b595ceec5f589edbdfc83dddecf3e7f3db09
5
5
  SHA512:
6
- metadata.gz: fb34b5b23fe8000435777be76f5e25d09293523c88658361e6fca0b21c5dbb1d807b2281429d72302ccef32f67cfcf14e810c0bd4d1f4a8415a8ef343fba74fd
7
- data.tar.gz: aea12556a8dac589d6e52ad51cbca4bdd30c488975415cf8fe6a1351159992e06e552f25985e53de72f8dfdacf8d1696366f99a2ef33d2413b1eb578489d8fbd
6
+ metadata.gz: ce798a44e216d19e62d4f4bdf76b447255d7ee4b72c97e1cb6241a384cf9b525f70b79b673930828d25e70634001ebb7e6c378b35e4784aaa5dfdc81113872c5
7
+ data.tar.gz: fda56a15d6d0eca70705f4b958010eaad4b5d35dc603e64f55b7f2f3b357f9eeb88622516e2ac7ef651364bb0ae78e4bbae8523beb6c77708b9b524c0f52abf8
data/Gemfile CHANGED
@@ -1,5 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
-
5
- gem "google-protobuf", "3.2.0.rc2"
data/lib/authentic-jwt.rb CHANGED
@@ -2,3 +2,5 @@ require "authentic_jwt/version"
2
2
  require "authentic_jwt/errors"
3
3
  require "authentic_jwt/payload_pb"
4
4
  require "authentic_jwt/role"
5
+ require "authentic_jwt/validator"
6
+ require "authentic_jwt/authorizer"
@@ -0,0 +1,46 @@
1
+ module AuthenticJwt
2
+ class Authorizer
3
+ def initialize(account_id: ENV["AUTHENTIC_AUTH_ACCOUNT_ID"])
4
+ unless account_id.to_s.empty?
5
+ @account_id = account_id.to_s
6
+ end
7
+ end
8
+
9
+ attr_reader :account_id
10
+
11
+ def call(payload:, scope: nil)
12
+ return unless account_id
13
+
14
+ account = payload.accounts.detect { |account| account.aud == account_id }
15
+
16
+ unless account
17
+ raise Forbidden, "No access to account"
18
+ end
19
+
20
+ roles = account.roles.collect(&:downcase)
21
+
22
+ unless roles.any?
23
+ raise Forbidden, "Account has no roles"
24
+ end
25
+
26
+ acceptable_roles = calculate_acceptable_roles(scope: scope)
27
+
28
+ unless (acceptable_roles & roles).any?
29
+ raise Forbidden, "Account role is too low"
30
+ end
31
+
32
+ true
33
+ end
34
+
35
+ protected
36
+
37
+ def calculate_acceptable_roles(scope:)
38
+ return [] unless scope
39
+ case scope
40
+ when "read" then AuthenticJwt::Role.read
41
+ when "write" then AuthenticJwt::Role.write
42
+ else raise ArgumentError
43
+ end
44
+ end
45
+ end
46
+ end
@@ -5,7 +5,7 @@ module AuthenticJwt
5
5
 
6
6
  def jwt_sub
7
7
  return unless jwt_payload
8
- jwt_payload["sub"]
8
+ jwt_payload.sub
9
9
  end
10
10
  end
11
11
  end
@@ -1,7 +1,5 @@
1
1
  require "authentic_jwt/grape/extension"
2
2
  require "authentic_jwt/grape/auth_methods"
3
- require "openssl"
4
- require "jwt"
5
3
 
6
4
  module AuthenticJwt
7
5
  module Grape
@@ -9,39 +7,26 @@ module AuthenticJwt
9
7
  def before
10
8
  return unless scope
11
9
 
12
- raise Unauthorized, "JWT public key not present" unless public_key
10
+ validator = AuthenticJwt::Validator.new
11
+ payload = validator.call(header: header)
13
12
 
14
- raise Unauthorized, "Authorization header not present" unless authorization_header
15
-
16
- raise Unauthorized, "Bearer token not present" unless bearer_token
17
-
18
- raise Unauthorized, "JWT payload not present" unless jwt_payload
13
+ authorizer = AuthenticJwt::Authorizer.new
14
+ authorizer.call(payload: payload, scope: scope)
19
15
 
20
16
  context.extend(AuthMethods)
21
- context.jwt_payload = jwt_payload
22
-
23
- return unless account_id
24
-
25
- raise Forbidden, "Account has no role" unless account_roles.any?
26
-
27
- raise Forbidden, "Account role is too low" unless (acceptable_roles & account_roles).any?
17
+ context.jwt_payload = payload
28
18
  end
29
19
 
30
20
  protected
31
21
 
32
- PUBLIC_KEY_ENV_VAR = "AUTHENTIC_AUTH_PUBLIC_KEY".freeze
33
- ACCOUNT_ID_ENV_VAR = "AUTHENTIC_AUTH_ACCOUNT_ID".freeze
34
- BEARER_PATTERN = /Bearer (.+)/
22
+ def header
23
+ env["HTTP_AUTHORIZATION"]
24
+ end
35
25
 
36
26
  def context
37
27
  env["api.endpoint"]
38
28
  end
39
29
 
40
- def authorization_header
41
- return if env["HTTP_AUTHORIZATION"].to_s.empty?
42
- env["HTTP_AUTHORIZATION"]
43
- end
44
-
45
30
  def route_setting
46
31
  context.route_setting(:oauth2)
47
32
  end
@@ -50,54 +35,6 @@ module AuthenticJwt
50
35
  return unless route_setting
51
36
  route_setting.fetch(:scope, nil)
52
37
  end
53
-
54
- def bearer_token
55
- return unless authorization_header
56
- if authorization_header =~ BEARER_PATTERN
57
- result = Regexp.last_match(1)
58
- unless result.to_s.empty?
59
- result
60
- end
61
- end
62
- end
63
-
64
- def public_key
65
- result = ENV[PUBLIC_KEY_ENV_VAR].to_s
66
- return if result.empty?
67
- OpenSSL::PKey::RSA.new(result)
68
- end
69
-
70
- def jwt_payload
71
- return unless bearer_token
72
- return unless public_key
73
- payload, header = JWT.decode(bearer_token, public_key, true, algorithm: "RS512")
74
- payload
75
- end
76
-
77
- def account_id
78
- result = ENV[ACCOUNT_ID_ENV_VAR].to_s
79
- return if result.empty?
80
- result
81
- end
82
-
83
- def account_payload
84
- return unless jwt_payload
85
- jwt_payload["accounts"].detect { |account| account["aud"] == account_id }
86
- end
87
-
88
- def account_roles
89
- return [] unless account_payload
90
- account_payload["roles"].collect(&:downcase)
91
- end
92
-
93
- def acceptable_roles
94
- return [] unless scope
95
- case scope
96
- when "read" then AuthenticJwt::Role.read
97
- when "write" then AuthenticJwt::Role.write
98
- else raise ArgumentError
99
- end
100
- end
101
38
  end
102
39
  end
103
40
  end
@@ -0,0 +1,66 @@
1
+ require "openssl"
2
+ require "jwt"
3
+
4
+ module AuthenticJwt
5
+ class Validator
6
+ def initialize(public_key: ENV["AUTHENTIC_AUTH_PUBLIC_KEY"])
7
+ if public_key.to_s.empty?
8
+ raise Unauthorized, "JWT public key not present"
9
+ end
10
+
11
+ @public_key = OpenSSL::PKey::RSA.new(public_key.to_s)
12
+ end
13
+
14
+ def call(header:)
15
+ if header.to_s.empty?
16
+ raise Unauthorized, "Authorization header missing"
17
+ end
18
+
19
+ bearer_token = extract_bearer_token(header: header)
20
+
21
+ extract_payload(bearer_token: bearer_token)
22
+ end
23
+
24
+ protected
25
+
26
+ BEARER_PATTERN = /Bearer (.+)/
27
+ ALGORITHM = "RS512"
28
+
29
+ attr_reader :public_key
30
+
31
+ def extract_bearer_token(header:)
32
+ if header =~ BEARER_PATTERN
33
+ bearer_token = Regexp.last_match(1)
34
+ else
35
+ raise Unauthorized, "Authorization header is not a Bearer token"
36
+ end
37
+
38
+ if bearer_token.to_s.empty?
39
+ raise Unauthorized, "Bearer token not present"
40
+ end
41
+
42
+ bearer_token
43
+ end
44
+
45
+ def extract_payload(bearer_token:)
46
+ raw, header = begin
47
+ JWT.decode(bearer_token, public_key, true, algorithm: ALGORITHM)
48
+ rescue JWT::DecodeError => error
49
+ if error.message =~ /Signature verification raised/
50
+ raise Unauthorized, "JWT does not match signature"
51
+ else
52
+ raise Unauthorized, "Bearer token is not a valid JWT"
53
+ end
54
+ end
55
+
56
+ # TODO bypass this step
57
+ json = JSON.dump(raw)
58
+
59
+ begin
60
+ Payload.decode_json(json)
61
+ rescue Google::Protobuf::ParseError
62
+ raise Unauthorized, "JWT is not in the correct format"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module AuthenticJwt
2
- VERSION = "0.0.4".freeze
2
+ VERSION = "0.0.5".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authentic-jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Authentic Limited
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-06 00:00:00.000000000 Z
11
+ date: 2017-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf
@@ -142,12 +142,14 @@ files:
142
142
  - bin/setup
143
143
  - definitions/payload.proto
144
144
  - lib/authentic-jwt.rb
145
+ - lib/authentic_jwt/authorizer.rb
145
146
  - lib/authentic_jwt/errors.rb
146
147
  - lib/authentic_jwt/grape/auth_methods.rb
147
148
  - lib/authentic_jwt/grape/extension.rb
148
149
  - lib/authentic_jwt/grape/middleware.rb
149
150
  - lib/authentic_jwt/payload_pb.rb
150
151
  - lib/authentic_jwt/role.rb
152
+ - lib/authentic_jwt/validator.rb
151
153
  - lib/authentic_jwt/version.rb
152
154
  homepage: https://github.com/mytours/authentic-jwt
153
155
  licenses: []
@@ -168,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
170
  version: '0'
169
171
  requirements: []
170
172
  rubyforge_project:
171
- rubygems_version: 2.6.8
173
+ rubygems_version: 2.5.2
172
174
  signing_key:
173
175
  specification_version: 4
174
176
  summary: Client authentication for Authentic Apps