zaikio-jwt_auth 1.0.1 → 2.1.0

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
  SHA256:
3
- metadata.gz: 4103bf1e59a9fa8a68a868c33d785082993255fe24616a01c1113a5a8026dcfe
4
- data.tar.gz: e0be8641749a07f97620c73a56a97211ba51e9fe4f9dc6e1bdbaa5dd0422b0e3
3
+ metadata.gz: e030f7e4c7f0be8b37a722ff691b7bf1398cd31ca449b41a9923b51f5a008df8
4
+ data.tar.gz: 13a0d7a174af3ca58772b116e22d2fcc893291d59010ce127df82f1dca77ea5c
5
5
  SHA512:
6
- metadata.gz: 28c8d6ea0b394450f7ae0027e9388fca1a9bd449213b6825356c65452e8a3efa4ab670802537cf9e08146d8a6980b5cbb22f2efa5cdec4c4b271b4c02b759fcf
7
- data.tar.gz: 647aa957f6e7d82ca26b50169fd265b30e144dcef5a26a8273e786c388569ac09babed909a1ddf295255df404f097bc5a867ff5520920d81f37ae4a48a5c60da
6
+ metadata.gz: c91befdc7f28018a2e19bcafdcbd805ef87467a6fde8fc1b4899b6c6a327a6a89327902f84bdd07f42749c265535cdc3b83bd6289e958fcd2b4d520e46462d94
7
+ data.tar.gz: aa56620ee2936346ef5b6d73ff0e8189ad8cc46d7ab44ca36783ad744f577513fb4786fa48023f4918491b04f166d9f0db5b66ef8b9f9a7d6ee04a315938bde7
data/README.md CHANGED
@@ -28,7 +28,9 @@ $ gem install zaikio-jwt_auth
28
28
  Zaikio::JWTAuth.configure do |config|
29
29
  config.environment = :sandbox # or production
30
30
  config.app_name = "test_app" # Your Zaikio App-Name
31
- config.redis = Redis.new
31
+
32
+ # Enable caching Hub API responses for e.g. revoked tokens
33
+ config.cache = Rails.cache
32
34
  end
33
35
  ```
34
36
 
@@ -63,6 +65,24 @@ end
63
65
 
64
66
  By convention, `authorize_by_jwt_scopes` automatically maps all CRUD actions in a controller. Requests for `show` and `index` with a read or read_write scope are allowed. All other actions like `create`, `update` and `destroy` are accepted if the scope is a write or read_write scope. Therefore it is strongly recommended to always create standard Rails resources. If a custom action is required, you will need to authorize yourself using the `after_jwt_auth`.
65
67
 
68
+ Both of these behaviours are automatically inherited by child classes, for example:
69
+
70
+ ```ruby
71
+ class API::ChildController < API::ResourcesController
72
+ end
73
+
74
+ API::ChildController.authorize_by_jwt_subject_type
75
+ #=> "Organization"
76
+ ```
77
+
78
+ You can always override the behaviour in children if needed:
79
+
80
+ ```ruby
81
+ class API::ChildController < API::ResourcesController
82
+ authorize_by_jwt_subject_type nil
83
+ end
84
+ ```
85
+
66
86
  #### Modifying required scopes
67
87
  If you nonetheless want to change the required scopes for CRUD routes, you can use the `type` option which accepts the following values: `:read`, `:write`, `:read_write`
68
88
 
@@ -165,6 +185,25 @@ rescue JWT::DecodeError, JWT::ExpiredSignature
165
185
  end
166
186
  ```
167
187
 
188
+ ### Using a different cache backend
189
+
190
+ This client supports any implementation of
191
+ [`ActiveSupport::Cache::Store`](https://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html),
192
+ but you can also write your own client that supports these methods: `#read(key)`,
193
+ `#write(key, value)`, `#delete(key)`
194
+
195
+ ### Pass custom options to JWT auth
196
+
197
+ In some cases you want to add custom options to the JWT check. For example you want to allow expired JWTs when revoking access tokens.
198
+
199
+ ```rb
200
+ class API::RevokedAccessTokensController < API::ApplicationController
201
+ def jwt_options
202
+ { verify_expiration: false }
203
+ end
204
+ end
205
+ ```
206
+
168
207
  ## Contributing
169
208
 
170
209
  **Make sure you have the dummy app running locally to validate your changes.**
@@ -11,7 +11,7 @@ module Zaikio
11
11
  production: "https://hub.zaikio.com"
12
12
  }.freeze
13
13
 
14
- attr_accessor :app_name, :redis, :host
14
+ attr_accessor :app_name, :cache, :host
15
15
  attr_reader :environment
16
16
  attr_writer :logger, :revoked_token_ids, :keys
17
17
 
@@ -16,7 +16,7 @@ module Zaikio
16
16
 
17
17
  class << self
18
18
  def fetch(directory_path, options = {})
19
- cache = Zaikio::JWTAuth.configuration.redis.get("zaikio::jwt_auth::#{directory_path}")
19
+ cache = Zaikio::JWTAuth.configuration.cache.read("zaikio::jwt_auth::#{directory_path}")
20
20
 
21
21
  json = Oj.load(cache) if cache
22
22
 
@@ -31,14 +31,14 @@ module Zaikio
31
31
  def update(directory_path, options = {})
32
32
  data = fetch(directory_path, options)
33
33
  data = yield(data)
34
- Zaikio::JWTAuth.configuration.redis.set("zaikio::jwt_auth::#{directory_path}", {
34
+ Zaikio::JWTAuth.configuration.cache.write("zaikio::jwt_auth::#{directory_path}", {
35
35
  fetched_at: Time.now.to_i,
36
36
  data: data
37
37
  }.to_json)
38
38
  end
39
39
 
40
40
  def reset(directory_path)
41
- Zaikio::JWTAuth.configuration.redis.del("zaikio::jwt_auth::#{directory_path}")
41
+ Zaikio::JWTAuth.configuration.cache.delete("zaikio::jwt_auth::#{directory_path}")
42
42
  end
43
43
 
44
44
  private
@@ -49,28 +49,34 @@ module Zaikio
49
49
 
50
50
  def reload_or_enqueue(directory_path)
51
51
  data = fetch_from_directory(directory_path)
52
- Zaikio::JWTAuth.configuration.redis.set("zaikio::jwt_auth::#{directory_path}", {
52
+ Zaikio::JWTAuth.configuration.cache.write("zaikio::jwt_auth::#{directory_path}", {
53
53
  fetched_at: Time.now.to_i,
54
54
  data: data
55
55
  }.to_json)
56
56
 
57
57
  data
58
58
  rescue Errno::ECONNREFUSED, Net::ReadTimeout, BadResponseError
59
- Zaikio::JWTAuth.configuration.logger.info("Error updating DirectoryCache(#{directory_path}), enqueueing job to update")
59
+ Zaikio::JWTAuth.configuration.logger
60
+ .info("Error updating DirectoryCache(#{directory_path}), enqueueing job to update")
60
61
  UpdateJob.set(wait: 10.seconds).perform_later(directory_path)
61
62
  nil
62
63
  end
63
64
 
64
65
  def fetch_from_directory(directory_path)
65
- uri = URI("#{Zaikio::JWTAuth.configuration.host}/#{directory_path}")
66
- http = Net::HTTP.new(uri.host, uri.port)
67
- http.use_ssl = uri.scheme == "https"
68
- response = http.request(Net::HTTP::Get.new(uri.request_uri))
66
+ response = make_http_request(directory_path)
67
+
69
68
  raise BadResponseError unless (200..299).cover?(response.code.to_i)
70
69
  raise BadResponseError unless response["content-type"].to_s.include?("application/json")
71
70
 
72
71
  Oj.load(response.body)
73
72
  end
73
+
74
+ def make_http_request(directory_path)
75
+ uri = URI("#{Zaikio::JWTAuth.configuration.host}/#{directory_path}")
76
+ http = Net::HTTP.new(uri.host, uri.port)
77
+ http.use_ssl = uri.scheme == "https"
78
+ http.request(Net::HTTP::Get.new(uri.request_uri))
79
+ end
74
80
  end
75
81
  end
76
82
  end
@@ -1,5 +1,5 @@
1
1
  module Zaikio
2
2
  module JWTAuth
3
- VERSION = "1.0.1".freeze
3
+ VERSION = "2.1.0".freeze
4
4
  end
5
5
  end
@@ -45,7 +45,7 @@ module Zaikio
45
45
  end
46
46
 
47
47
  def self.mocked_jwt_payload
48
- @mocked_jwt_payload
48
+ instance_variable_defined?(:@mocked_jwt_payload) && @mocked_jwt_payload
49
49
  end
50
50
 
51
51
  def self.mocked_jwt_payload=(payload)
@@ -54,21 +54,27 @@ module Zaikio
54
54
 
55
55
  HEADER_FORMAT = /\ABearer (.+)\z/.freeze
56
56
 
57
- def self.extract(authorization_header_string)
57
+ def self.extract(authorization_header_string, **options)
58
58
  return TokenData.new(Zaikio::JWTAuth.mocked_jwt_payload) if Zaikio::JWTAuth.mocked_jwt_payload
59
59
 
60
60
  return if authorization_header_string.blank?
61
61
 
62
62
  return unless (token = authorization_header_string[HEADER_FORMAT, 1])
63
63
 
64
- payload, = JWT.decode(token, nil, true, algorithms: ["RS256"], jwks: JWK.loader)
64
+ options.reverse_merge!(algorithms: ["RS256"], jwks: JWK.loader)
65
+
66
+ payload, = JWT.decode(token, nil, true, **options)
65
67
 
66
68
  TokenData.new(payload)
67
69
  end
68
70
 
69
71
  module ClassMethods
70
- def authorize_by_jwt_subject_type(type = nil)
71
- @authorize_by_jwt_subject_type ||= type
72
+ def authorize_by_jwt_subject_type(type = :_not_given_)
73
+ if type != :_not_given_
74
+ @authorize_by_jwt_subject_type = type
75
+ elsif instance_variable_defined?(:@authorize_by_jwt_subject_type)
76
+ @authorize_by_jwt_subject_type
77
+ end
72
78
  end
73
79
 
74
80
  def authorize_by_jwt_scopes(scopes = nil, options = {})
@@ -78,11 +84,18 @@ module Zaikio
78
84
 
79
85
  @authorize_by_jwt_scopes
80
86
  end
87
+
88
+ def inherited(child)
89
+ super(child)
90
+
91
+ child.instance_variable_set(:@authorize_by_jwt_subject_type, @authorize_by_jwt_subject_type)
92
+ child.instance_variable_set(:@authorize_by_jwt_scopes, @authorize_by_jwt_scopes)
93
+ end
81
94
  end
82
95
 
83
96
  module InstanceMethods
84
97
  def authenticate_by_jwt
85
- token_data = Zaikio::JWTAuth.extract(request.headers["Authorization"])
98
+ token_data = Zaikio::JWTAuth.extract(request.headers["Authorization"], **jwt_options)
86
99
  return render_error("no_jwt_passed", status: :unauthorized) unless token_data
87
100
 
88
101
  return if show_error_if_token_is_revoked(token_data)
@@ -139,6 +152,10 @@ module Zaikio
139
152
  def render_error(error, status: :forbidden)
140
153
  render(status: status, json: { "errors" => [error] })
141
154
  end
155
+
156
+ def jwt_options
157
+ {}
158
+ end
142
159
  end
143
160
  end
144
161
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zaikio-jwt_auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - crispymtn
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-04-28 00:00:00.000000000 Z
13
+ date: 2022-08-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activejob
@@ -113,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
113
  - !ruby/object:Gem::Version
114
114
  version: '0'
115
115
  requirements: []
116
- rubygems_version: 3.1.4
116
+ rubygems_version: 3.3.11
117
117
  signing_key:
118
118
  specification_version: 4
119
119
  summary: JWT-Based authentication and authorization with zaikio