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 +4 -4
- data/Gemfile +0 -2
- data/lib/authentic-jwt.rb +2 -0
- data/lib/authentic_jwt/authorizer.rb +46 -0
- data/lib/authentic_jwt/grape/auth_methods.rb +1 -1
- data/lib/authentic_jwt/grape/middleware.rb +8 -71
- data/lib/authentic_jwt/validator.rb +66 -0
- data/lib/authentic_jwt/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e6fcda5fe67f9a0e8a4e2831f3d4415ddf32cc9
|
4
|
+
data.tar.gz: 15d4b595ceec5f589edbdfc83dddecf3e7f3db09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce798a44e216d19e62d4f4bdf76b447255d7ee4b72c97e1cb6241a384cf9b525f70b79b673930828d25e70634001ebb7e6c378b35e4784aaa5dfdc81113872c5
|
7
|
+
data.tar.gz: fda56a15d6d0eca70705f4b958010eaad4b5d35dc603e64f55b7f2f3b357f9eeb88622516e2ac7ef651364bb0ae78e4bbae8523beb6c77708b9b524c0f52abf8
|
data/Gemfile
CHANGED
data/lib/authentic-jwt.rb
CHANGED
@@ -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
|
@@ -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
|
-
|
10
|
+
validator = AuthenticJwt::Validator.new
|
11
|
+
payload = validator.call(header: header)
|
13
12
|
|
14
|
-
|
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 =
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
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
|
+
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-
|
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.
|
173
|
+
rubygems_version: 2.5.2
|
172
174
|
signing_key:
|
173
175
|
specification_version: 4
|
174
176
|
summary: Client authentication for Authentic Apps
|