jwt-authenticator 1.0.0.rc1 → 1.0.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: c128a8bddd8a546812be283f492f8dc6463073ff067c0e0eecb70891ed4695fb
4
- data.tar.gz: eb1e0781bbda0d8445340790f28251c7a513b624683e2e140cca86e3c74ec3eb
3
+ metadata.gz: 6a0f3e5579beda1099857bfb7731cf03f9a531f5178289ac89721561f51b9394
4
+ data.tar.gz: b4d779f5919c2bcce341e484b720fe995eea90eb4d1dd1f7faa7fde9fa669293
5
5
  SHA512:
6
- metadata.gz: 9582a70c04c9c136973eebad066ea834a7d320a6e0c4bc6385d76ac0f9cae68d1bc76ae63b10f1699b46da2a55b2bbd19ad6af11a4dcc5f539a5826a3e6817c8
7
- data.tar.gz: c034bc475866eacd92fde3a3f774da618461d7ed7a19b828fde16f2e945dd8d758686713b842479acaa30aee83f0624582bae59affc4e7d323a19c192252cbd9
6
+ metadata.gz: fc10f802cea4187f46f4c8ef876e8caf11010f8477fa2461d210d75a63cdfda109f711e320b8a4b2e06b7e22f0c86845a2cd4b1ce4f08189648f3e292488eda9
7
+ data.tar.gz: 77512700bcc753b7505b82682595baeabc46b50cbff09cf1199ff4fb559e19143d106958f485051b77ed393c2eca1516cec85e84fd6cc734d66c175129e960fd
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ pkg/
4
+ tmp/
5
+ /.idea
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.1
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ source "https://rubygems.org"
5
+
6
+ gemspec
7
+
8
+ gem "rake", "~> 12.3"
9
+ gem "test-unit", "~> 3.2"
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ jwt-authenticator (1.0.0)
5
+ activesupport (>= 4.0, < 6.0)
6
+ jwt (~> 2.1.0)
7
+ method-not-implemented (~> 1.0.1)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (5.2.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ concurrent-ruby (1.0.5)
18
+ i18n (1.1.0)
19
+ concurrent-ruby (~> 1.0)
20
+ jwt (2.1.0)
21
+ method-not-implemented (1.0.1)
22
+ minitest (5.11.3)
23
+ power_assert (1.1.3)
24
+ rake (12.3.1)
25
+ test-unit (3.2.8)
26
+ power_assert
27
+ thread_safe (0.3.6)
28
+ tzinfo (1.2.5)
29
+ thread_safe (~> 0.1)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bundler (~> 1.7)
36
+ jwt-authenticator!
37
+ rake (~> 12.3)
38
+ test-unit (~> 3.2)
39
+
40
+ BUNDLED WITH
41
+ 1.16.4
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2018 Yaroslav Konoplov <eahome00@gmail.com>
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # JSON Web Token authentication Ruby service
2
+
3
+ The gem provides easy & extendable way to perform JSON Web Token authentication.
4
+
5
+ ## Usage
6
+
7
+ Please, see the code sample below.
8
+
9
+ ```ruby
10
+ module MyAPI
11
+ #
12
+ # Define JWT verification options using variables below:
13
+ #
14
+ # MY_API_JWT_VERIFY_EXP
15
+ # MY_API_JWT_VERIFY_NBF
16
+ # MY_API_JWT_ISS
17
+ # MY_API_JWT_VERIFY_IAT
18
+ # MY_API_JWT_VERIFY_JTI
19
+ # MY_API_JWT_AUD (could be comma-separated)
20
+ # MY_API_JWT_SUB
21
+ # MY_API_JWT_ALG (could be comma-separated)
22
+ # MY_API_JWT_LEEWAY
23
+ # MY_API_JWT_IAT_LEEWAY
24
+ # MY_API_JWT_EXP_LEEWAY
25
+ # MY_API_JWT_NBF_LEEWAY
26
+ #
27
+ class JWTAuthenticator < JWT::Authenticator
28
+ def call(*)
29
+ payload, = super
30
+ # You may want to do some additional checks here like verifying JTI is not revoked.
31
+ # You also can return any value you want. For example, here we can return user.
32
+ User.new(payload.slice(:uid, :email, :level))
33
+ end
34
+
35
+ protected
36
+
37
+ def public_key(header)
38
+ # You have to determine what key should be user for signature verification (based on «header») and return public key.
39
+ # The returned value must be instance of OpenSSL::PKey::RSA.
40
+ end
41
+ end
42
+ end
43
+ ```
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require "rake/testtask"
5
+
6
+ ENV["TESTOPTS"] = "--verbose"
7
+ Rake::TestTask.new { |t| t.libs << "test" }
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require File.expand_path("../lib/jwt-authenticator/version", __FILE__)
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "jwt-authenticator"
8
+ s.version = JWT::Authenticator::VERSION
9
+ s.author = "Yaroslav Konoplov"
10
+ s.email = "eahome00@gmail.com"
11
+ s.summary = "JSON Web Token authentication Ruby service."
12
+ s.description = "The gem provides easy & extendable way to perform JSON Web Token authentication."
13
+ s.homepage = "https://github.com/ruby-jwt/jwt-authenticator"
14
+ s.license = "Apache-2.0"
15
+ s.files = `git ls-files -z`.split("\x0")
16
+ s.test_files = `git ls-files -z -- {test,spec,features}/*`.split("\x0")
17
+ s.require_paths = ["lib"]
18
+ s.required_ruby_version = "~> 2.5"
19
+
20
+ s.add_dependency "jwt", "~> 2.1.0"
21
+ s.add_dependency "method-not-implemented", "~> 1.0.1"
22
+ s.add_dependency "activesupport", ">= 4.0", "< 6.0"
23
+ s.add_development_dependency "bundler", "~> 1.7"
24
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module JWT
5
+ class Authenticator
6
+ VERSION = "1.0.0"
7
+ end
8
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require "jwt"
5
+ require "method-not-implemented"
6
+ require "active_support/core_ext/string/inflections"
7
+ require "active_support/core_ext/object/blank"
8
+ require "active_support/core_ext/string/filters"
9
+ require "active_support/core_ext/hash/keys"
10
+ require "jwt-authenticator/version"
11
+
12
+ class JWT::Authenticator
13
+ include Singleton
14
+
15
+ def initialize
16
+ @verification_options = token_verification_options_from_environment \
17
+ self.class.name.split("::").first.underscore.upcase.gsub(/_?JWT\z/, "") + "_JWT"
18
+ end
19
+
20
+ def call(token)
21
+ error! "Token is missing.", 101 if token.blank?
22
+ token_type, token_value = token.to_s.squish.split(" ")
23
+ error! "Token type is not provided or invalid.", 102 unless token_type == "Bearer"
24
+ returned = JWT.decode(token_value, nil, true, @verification_options) { |header| public_key(header.deep_symbolize_keys) }
25
+ returned.map(&:deep_symbolize_keys)
26
+ rescue JWT::DecodeError => e
27
+ error!(e.inspect, 103)
28
+ end
29
+
30
+ protected
31
+
32
+ def public_key(header)
33
+ method_not_implemented
34
+ end
35
+
36
+ def token_verification_options_from_environment(prefix)
37
+ { verify_expiration: ENV["#{prefix}_VERIFY_EXP"] != "false",
38
+ verify_not_before: ENV["#{prefix}_VERIFY_NBF"] != "false",
39
+ iss: ENV["#{prefix}_ISS"].to_s.squish.presence,
40
+ verify_iat: ENV["#{prefix}_VERIFY_IAT"] != "false",
41
+ verify_jti: ENV["#{prefix}_VERIFY_JTI"] != "false",
42
+ aud: ENV["#{prefix}_AUD"].to_s.split(",").map(&:squish).reject(&:blank?).presence, # Comma-separated values.
43
+ sub: ENV["#{prefix}_SUB"].to_s.squish.presence,
44
+ algorithms: ENV["#{prefix}_ALG"].to_s.split(",").map(&:squish).reject(&:blank?).presence, # Comma-separated values.
45
+ leeway: ENV["#{prefix}_LEEWAY"].to_s.squish.yield_self { |n| n.to_i if n.present? },
46
+ iat_leeway: ENV["#{prefix}_IAT_LEEWAY"].to_s.squish.yield_self { |n| n.to_i if n.present? },
47
+ exp_leeway: ENV["#{prefix}_EXP_LEEWAY"].to_s.squish.yield_self { |n| n.to_i if n.present? },
48
+ nbf_leeway: ENV["#{prefix}_NBF_LEEWAY"].to_s.squish.yield_self { |n| n.to_i if n.present? }
49
+ }.tap { |options|
50
+ options.merge! \
51
+ verify_sub: options[:sub].present?,
52
+ verify_iss: options[:iss].present?,
53
+ verify_aud: options[:aud].present?
54
+ }.compact
55
+ end
56
+
57
+ def error!(message, code = nil)
58
+ raise Error.new(message, code)
59
+ end
60
+
61
+ class << self
62
+ def call(token)
63
+ instance.call(token)
64
+ end
65
+ end
66
+
67
+ class Error < StandardError
68
+ attr_reader :code
69
+
70
+ def initialize(message, code = nil)
71
+ super(message)
72
+ @code = code
73
+ end
74
+ end
75
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ Bundler.require
5
+
6
+ Test::Unit::TestCase.test_order = :random
7
+
8
+ require "active_support/inflections"
9
+ require "active_support/core_ext/kernel/reporting"
10
+ require "securerandom"
11
+ require "openssl"
12
+ require "base64"
13
+ require "set"
14
+
15
+ ActiveSupport::Inflector.inflections do |inflect|
16
+ inflect.acronym "API"
17
+ inflect.acronym "v1"
18
+ inflect.acronym "v2"
19
+ end
20
+
21
+ module MyAPIv1
22
+ class JWTAuthenticator < JWT::Authenticator
23
+
24
+ end
25
+ end
26
+
27
+ ENV["MY_API_V2_JWT_ISS"] = "foo"
28
+ ENV["MY_API_V2_JWT_AUD"] = "foo,bar,baz"
29
+ ENV["MY_API_V2_JWT_SUB"] = "session"
30
+ ENV["MY_API_V2_JWT_ALG"] = "RS256"
31
+ ENV["MY_API_V2_JWT_KEY"] = Base64.urlsafe_encode64(OpenSSL::PKey::RSA.generate(2048).to_pem)
32
+
33
+ module MyAPIv2
34
+ class JWTAuthenticator < JWT::Authenticator
35
+
36
+ private
37
+
38
+ def public_key(*)
39
+ OpenSSL::PKey.read(Base64.urlsafe_decode64(ENV["MY_API_V2_JWT_KEY"])).public_key
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,184 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "helper"
5
+
6
+ class JWTAuthenticatorTest < Test::Unit::TestCase
7
+ test "gem version" do
8
+ assert defined?(JWT::Authenticator::VERSION)
9
+ end
10
+
11
+ test "singleton" do
12
+ assert_equal 1, ([JWT::Authenticator] * 3).map(&:instance).map(&:object_id).uniq.count
13
+ end
14
+
15
+ test "loading token verification options from environment" do
16
+ default = {
17
+ verify_aud: false,
18
+ verify_expiration: true,
19
+ verify_iat: true,
20
+ verify_iss: false,
21
+ verify_jti: true,
22
+ verify_not_before: true,
23
+ verify_sub: false }
24
+
25
+ assert_equal(default, my_api_v1_token_verification_options)
26
+
27
+ modify_environment "MY_API_V1_JWT_VERIFY_EXP", "false" do
28
+ assert_equal(default.merge(verify_expiration: false), my_api_v1_token_verification_options)
29
+ end
30
+
31
+ modify_environment "MY_API_V1_JWT_VERIFY_NBF", "false" do
32
+ assert_equal(default.merge(verify_not_before: false), my_api_v1_token_verification_options)
33
+ end
34
+
35
+ modify_environment "MY_API_V1_JWT_ISS", " foo " do
36
+ assert_equal(default.merge(iss: "foo", verify_iss: true), my_api_v1_token_verification_options)
37
+ end
38
+
39
+ modify_environment "MY_API_V1_JWT_VERIFY_IAT", "false" do
40
+ assert_equal(default.merge(verify_iat: false), my_api_v1_token_verification_options)
41
+ end
42
+
43
+ modify_environment "MY_API_V1_JWT_VERIFY_JTI", "false" do
44
+ assert_equal(default.merge(verify_jti: false), my_api_v1_token_verification_options)
45
+ end
46
+
47
+ modify_environment "MY_API_V1_JWT_AUD", "foo, bar, baz" do
48
+ assert_equal(default.merge(aud: %w[foo bar baz], verify_aud: true), my_api_v1_token_verification_options)
49
+ end
50
+
51
+ modify_environment "MY_API_V1_JWT_SUB", " session " do
52
+ assert_equal(default.merge(sub: "session", verify_sub: true), my_api_v1_token_verification_options)
53
+ end
54
+
55
+ modify_environment "MY_API_V1_JWT_ALG", " RS256" do
56
+ assert_equal(default.merge(algorithms: %w[RS256]), my_api_v1_token_verification_options)
57
+ end
58
+
59
+ modify_environment "MY_API_V1_JWT_LEEWAY", "30" do
60
+ assert_equal(default.merge(leeway: 30), my_api_v1_token_verification_options)
61
+ end
62
+
63
+ modify_environment "MY_API_V1_JWT_IAT_LEEWAY", "30" do
64
+ assert_equal(default.merge(iat_leeway: 30), my_api_v1_token_verification_options)
65
+ end
66
+
67
+ modify_environment "MY_API_V1_JWT_EXP_LEEWAY", "30" do
68
+ assert_equal(default.merge(exp_leeway: 30), my_api_v1_token_verification_options)
69
+ end
70
+
71
+ modify_environment "MY_API_V1_JWT_NBF_LEEWAY", "30" do
72
+ assert_equal(default.merge(nbf_leeway: 30), my_api_v1_token_verification_options)
73
+ end
74
+ end
75
+
76
+ test "blank token" do
77
+ error = assert_raises(JWT::Authenticator::Error) { JWT::Authenticator.instance.call(" ") }
78
+ assert_match(/\bmissing\b/i, error.message)
79
+ assert_equal(101, error.code)
80
+ end
81
+
82
+ test "token with invalid type" do
83
+ error = assert_raises(JWT::Authenticator::Error) { JWT::Authenticator.instance.call("Beer XXX.YYY.ZZZ") }
84
+ assert_match(/\binvalid\b/i, error.message)
85
+ assert_equal(102, error.code)
86
+ end
87
+
88
+ test "token decoding and verification" do
89
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload)
90
+ payload, header = my_api_v2_jwt_decode(jwt)
91
+ assert %i[jti iss aud sub dat].to_set == payload.keys.to_set
92
+ assert %i[alg].to_set == header.keys.to_set
93
+ end
94
+
95
+ test "wrong iss" do
96
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.merge(iss: "qux"))
97
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
98
+ assert_match(/\binvalid issuer\b/i, error.message)
99
+ assert_equal(103, error.code)
100
+ end
101
+
102
+ test "missing iss" do
103
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.tap { |p| p.delete(:iss) })
104
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
105
+ assert_match(/\binvalid issuer\b/i, error.message)
106
+ assert_equal(103, error.code)
107
+ end
108
+
109
+ test "missing aud" do
110
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.tap { |p| p.delete(:aud) })
111
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
112
+ assert_match(/\binvalid audience\b/i, error.message)
113
+ assert_equal(103, error.code)
114
+ end
115
+
116
+ test "wrong aud" do
117
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.merge(aud: "qux"))
118
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
119
+ assert_match(/\binvalid audience\b/i, error.message)
120
+ assert_equal(103, error.code)
121
+ end
122
+
123
+ test "missing sub" do
124
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.tap { |p| p.delete(:sub) })
125
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
126
+ assert_match(/\binvalid subject\b/i, error.message)
127
+ assert_equal(103, error.code)
128
+ end
129
+
130
+ test "wrong sub" do
131
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.merge(sub: "qux"))
132
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
133
+ assert_match(/\binvalid subject\b/i, error.message)
134
+ assert_equal(103, error.code)
135
+ end
136
+
137
+ test "token is expired" do
138
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.merge(exp: Time.now.to_i - 5))
139
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
140
+ assert_match(/\bexpired\b/i, error.message)
141
+ assert_equal(103, error.code)
142
+ end
143
+
144
+ test "missing jti" do
145
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.tap { |p| p.delete(:jti) })
146
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
147
+ assert_match(/\bmissing jti\b/i, error.message)
148
+ assert_equal(103, error.code)
149
+ end
150
+
151
+ test "issued at in future" do
152
+ jwt = my_api_v2_jwt_encode(my_api_v2_jwt_payload.merge(iat: Time.now.to_i + 30))
153
+ error = assert_raises(JWT::Authenticator::Error) { my_api_v2_jwt_decode(jwt) }
154
+ assert_match(/\binvalid iat\b/i, error.message)
155
+ assert_equal(103, error.code)
156
+ end
157
+
158
+ private
159
+
160
+ def my_api_v1_token_verification_options
161
+ silence_warnings { Singleton.__init__(MyAPIv1::JWTAuthenticator) }
162
+ MyAPIv1::JWTAuthenticator.instance.instance_variable_get(:@verification_options)
163
+ end
164
+
165
+ def modify_environment(var, val)
166
+ prev = ENV[var]
167
+ ENV[var] = val
168
+ yield
169
+ ensure
170
+ ENV[var] = prev
171
+ end
172
+
173
+ def my_api_v2_jwt_payload
174
+ { iss: "foo", jti: SecureRandom.uuid, aud: %w[bar baz], sub: "session", dat: {} }
175
+ end
176
+
177
+ def my_api_v2_jwt_encode(payload)
178
+ JWT.encode(payload, OpenSSL::PKey.read(Base64.urlsafe_decode64(ENV["MY_API_V2_JWT_KEY"])), "RS256")
179
+ end
180
+
181
+ def my_api_v2_jwt_decode(jwt)
182
+ MyAPIv2::JWTAuthenticator.call("Bearer " + jwt)
183
+ end
184
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt-authenticator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Konoplov
@@ -72,13 +72,25 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '1.7'
75
- description: JWT auth.
75
+ description: The gem provides easy & extendable way to perform JSON Web Token authentication.
76
76
  email: eahome00@gmail.com
77
77
  executables: []
78
78
  extensions: []
79
79
  extra_rdoc_files: []
80
- files: []
81
- homepage: https://github.com/yivo/jwt-authenticator
80
+ files:
81
+ - ".gitignore"
82
+ - ".ruby-version"
83
+ - Gemfile
84
+ - Gemfile.lock
85
+ - LICENSE
86
+ - README.md
87
+ - Rakefile
88
+ - jwt-authenticator.gemspec
89
+ - lib/jwt-authenticator.rb
90
+ - lib/jwt-authenticator/version.rb
91
+ - test/helper.rb
92
+ - test/jwt-authenticator-test.rb
93
+ homepage: https://github.com/ruby-jwt/jwt-authenticator
82
94
  licenses:
83
95
  - Apache-2.0
84
96
  metadata: {}
@@ -88,18 +100,20 @@ require_paths:
88
100
  - lib
89
101
  required_ruby_version: !ruby/object:Gem::Requirement
90
102
  requirements:
91
- - - ">="
103
+ - - "~>"
92
104
  - !ruby/object:Gem::Version
93
- version: '0'
105
+ version: '2.5'
94
106
  required_rubygems_version: !ruby/object:Gem::Requirement
95
107
  requirements:
96
- - - ">"
108
+ - - ">="
97
109
  - !ruby/object:Gem::Version
98
- version: 1.3.1
110
+ version: '0'
99
111
  requirements: []
100
112
  rubyforge_project:
101
113
  rubygems_version: 2.7.6
102
114
  signing_key:
103
115
  specification_version: 4
104
- summary: JWT auth.
105
- test_files: []
116
+ summary: JSON Web Token authentication Ruby service.
117
+ test_files:
118
+ - test/helper.rb
119
+ - test/jwt-authenticator-test.rb