jwt-authorizer 1.0.0.beta1 → 1.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +93 -39
- data/bin/console +4 -0
- data/jwt-authorizer.gemspec +2 -0
- data/lib/jwt/authorizer.rb +11 -12
- data/lib/jwt/authorizer/version.rb +2 -2
- data/lib/jwt/{request_authorizer.rb → endpoint_token.rb} +9 -9
- data/lib/jwt/token.rb +13 -0
- data/lib/jwt/token/builder.rb +15 -0
- data/lib/jwt/token/claim.rb +32 -0
- data/lib/jwt/token/claim_builder.rb +48 -0
- data/lib/jwt/{authorizer → token}/configurable.rb +2 -6
- data/lib/jwt/{authorizer → token}/configuration.rb +1 -1
- data/lib/jwt/token/default_claims.rb +33 -0
- data/lib/jwt/token/verifier.rb +36 -0
- metadata +13 -11
- data/lib/jwt/authorizer/builder.rb +0 -21
- data/lib/jwt/authorizer/claim_validator.rb +0 -32
- data/lib/jwt/authorizer/validation.rb +0 -40
- data/lib/jwt/authorizer/verifier.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e324a064b198c930d1085ec1b633517467b9fb2d2acd5951be2c3613a17190b
|
4
|
+
data.tar.gz: d5f1aac2b3638c7ba12e74f74bd365463079140c5789a88da77cbd9c60e5abd7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23899cec2c6898756fdec308310cd39be9b3ba4da9c9537342678103502bb922a714645ed7c736b78d852abd123e926f1d8918af5f1e352d7ff4ee556ca0eb41
|
7
|
+
data.tar.gz: 4aa067249c8a651a8f1b68c6cd73f887350fe1ca10536e238912421cf34e0f3557ad4926cc7f0c0520eea83f8d6f220f33b4e6db8b090fda3ce848b6ac1a7d65
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/codesthq/jwt-authorizer.svg?branch=master)](https://travis-ci.org/codesthq/jwt-authorizer) [![Test Coverage](https://api.codeclimate.com/v1/badges/5f975bb8720b7ee04326/test_coverage)](https://codeclimate.com/github/codesthq/jwt-authorizer/test_coverage) [![Maintainability](https://api.codeclimate.com/v1/badges/5f975bb8720b7ee04326/maintainability)](https://codeclimate.com/github/codesthq/jwt-authorizer/maintainability)
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/jwt-authorizer.svg)](https://badge.fury.io/rb/jwt-authorizer) [![Build Status](https://travis-ci.org/codesthq/jwt-authorizer.svg?branch=master)](https://travis-ci.org/codesthq/jwt-authorizer) [![Test Coverage](https://api.codeclimate.com/v1/badges/5f975bb8720b7ee04326/test_coverage)](https://codeclimate.com/github/codesthq/jwt-authorizer/test_coverage) [![Maintainability](https://api.codeclimate.com/v1/badges/5f975bb8720b7ee04326/maintainability)](https://codeclimate.com/github/codesthq/jwt-authorizer/maintainability)
|
2
2
|
|
3
3
|
# JWT::Authorizer
|
4
4
|
|
5
|
-
`JWT::Authorizer` makes authorization with [JWT tokens](https://jwt.io/) simple. It allows creating and verifying JWT tokens according to
|
5
|
+
`JWT::Authorizer` makes authorization with [JWT tokens](https://jwt.io/) simple. It allows creating and verifying JWT tokens according to claims set on specific `JWT::Token` class.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -24,19 +24,19 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
### Configuration
|
26
26
|
|
27
|
-
You can configure your `JWT::
|
27
|
+
You can configure your `JWT::Token` classes with `.configuration` and `.configure` options:
|
28
28
|
|
29
29
|
```ruby
|
30
|
-
JWT::
|
30
|
+
JWT::Token.configuration
|
31
31
|
|
32
|
-
JWT::
|
32
|
+
JWT::Token.configure do |config|
|
33
33
|
config.expiry = 12 * 60 * 60
|
34
34
|
config.algorithm = "RS256"
|
35
35
|
config.secret = { private_key: nil, public_key: ENV["SECRET_KEY"] }
|
36
36
|
end
|
37
37
|
```
|
38
38
|
|
39
|
-
`JWT::
|
39
|
+
`JWT::Token` have following options available:
|
40
40
|
|
41
41
|
* `algorithm` - determines algorithm used on signing and verifying JWT tokens. Defaults to `"HS256"`.
|
42
42
|
* `secret` - for [`HMAC`](https://en.wikipedia.org/wiki/HMAC) algorithms it accepts simple `String` with symmetric key, for [`RSA`](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) and [`ECDSA`](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) it requires hash with `:private_key` and `:public_key` keys.
|
@@ -44,75 +44,129 @@ end
|
|
44
44
|
* `issuer` - sets `iss` claim in the token. Defaults to `nil`.
|
45
45
|
* `allowed_issuers` - array of issuers that will be allowed on token verification. Defaults to empty array, tokens with any value in `iss` claim (and without this claim) will be valid. If array contains any elements, *only* listed issuers will be valid.
|
46
46
|
|
47
|
-
Default
|
47
|
+
Default claims can be overriden during instantiation of `JWT::Token` classes:
|
48
48
|
|
49
49
|
```ruby
|
50
|
-
JWT::
|
51
|
-
JWT::
|
50
|
+
JWT::Token.configuration.expiry #=> 3600 (offset)
|
51
|
+
JWT::Token.new(expiry: Time.utc(2018, 3, 1)).expiry #=> 1519862400 (timestamp)
|
52
52
|
```
|
53
53
|
|
54
54
|
### Generating tokens
|
55
55
|
|
56
|
-
To generate JWT token, create instance of `JWT::
|
56
|
+
To generate JWT token, create instance of `JWT::Token`. It accepts hash of additional claims you want in your token.
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
JWT::
|
60
|
-
|
61
|
-
|
59
|
+
class MyToken < JWT::Token
|
60
|
+
claim :level, required: true
|
61
|
+
end
|
62
|
+
token = MyToken.new(level: :admin)
|
63
|
+
token.to_s # or token.to_jwt
|
64
|
+
#=> "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI2MTcsImxldmVsIjoiYWRtaW4ifQ.Ak8qDlxSG9IcPVHYnelQHPK5U6Rj5hBYQ5mmoznuYso"
|
62
65
|
```
|
63
66
|
|
64
67
|
### Verifying tokens
|
65
68
|
|
66
|
-
To verify token, use `JWT::
|
69
|
+
To verify token, use `JWT::Token.verify` method.
|
67
70
|
|
68
71
|
```ruby
|
69
|
-
|
70
|
-
token
|
71
|
-
JWT::
|
72
|
-
|
73
|
-
JWT::Authorizer.new.verify(nil)
|
72
|
+
token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI2Nzd9.EgiqWfDjXzlJHTwaFn26X3iOl2gBkQv3fADtMsFIQDY"
|
73
|
+
JWT::Token.verify(token)
|
74
|
+
#=> #<JWT::Token @claims={"exp"=>1520412677, "iss"=>nil}>
|
75
|
+
JWT::Token.verify(nil)
|
74
76
|
# JWT::DecodeError: Nil JSON web token
|
75
|
-
JWT::
|
77
|
+
JWT::Token.verify("eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjB9.nooope")
|
76
78
|
# JWT::VerificationError: Signature verification raised
|
77
79
|
```
|
78
80
|
|
79
|
-
###
|
81
|
+
### Claims
|
80
82
|
|
81
|
-
You can use
|
83
|
+
You can use claims to define and verify non-standard claims.
|
82
84
|
|
83
85
|
```ruby
|
84
|
-
class
|
85
|
-
|
86
|
+
class AdminToken < JWT::Token
|
87
|
+
claim :level, key: "lvl", required: true do |value|
|
86
88
|
raise JWT::DecodeError, "Level must be admin" unless value == "admin"
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
valid_token = "eyJhbGciOiJIUzI1NiJ9.
|
91
|
-
|
92
|
-
|
93
|
-
missing_claim = "eyJhbGciOiJIUzI1NiJ9.
|
94
|
-
|
95
|
-
# JWT::
|
96
|
-
invalid_value = "eyJhbGciOiJIUzI1NiJ9.
|
97
|
-
|
92
|
+
valid_token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI3ODksImxldmVsIjoiYWRtaW4ifQ.GGD0dXWg7v8BiEg8fsjmdCXQBryAHRpx_8AihyNVmgs"
|
93
|
+
AdminToken.verify(valid_token)
|
94
|
+
#=> #<AdminToken @claims={"exp"=>1520412789, "iss"=>nil, "lvl"=>"admin"}>
|
95
|
+
missing_claim = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0MTI3ODl9.efq_LuSpfp5VRwFl3rIf0FC_b2CCrpEC_oeDssvLDy4"
|
96
|
+
AdminToken.verify(missing_claim)
|
97
|
+
# JWT::Token::MissingClaim: Token is missing required claim: lvl
|
98
|
+
invalid_value = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MjA0NTI3ODksImx2bCI6InJlZ3VsYXIifQ.EjXX9zhE4SpzFSlGIPD5l0xKtMKgWSbWa5smw3OvBEo"
|
99
|
+
AdminToken.verify(invalid_value)
|
98
100
|
# JWT::DecodeError: Level must be admin
|
99
101
|
```
|
100
102
|
|
101
103
|
`required` option is by default set to `false`. If set to `true`, given claim *must* be present in verified token.
|
104
|
+
`key` options is by default the same as claim name. It corresponds to JSON inside JWT.
|
102
105
|
|
103
|
-
You can pass additional context to
|
106
|
+
You can pass additional context to claims:
|
104
107
|
|
105
108
|
```ruby
|
106
|
-
class
|
107
|
-
|
109
|
+
class AdminToken < JWT::Token
|
110
|
+
claim :path do |value, rack_request|
|
108
111
|
raise JWT::DecodeError, "invalid path" unless value == rack_request.path
|
109
112
|
end
|
110
113
|
end
|
111
114
|
|
112
|
-
|
115
|
+
AdminToken.verify(token, rack_request)
|
116
|
+
```
|
117
|
+
|
118
|
+
See [`JWT::EndpointToken`](lib/jwt/endpoint_token.rb) and it's [spec](spec/jwt/endpoint_token_spec.rb) for examples.
|
119
|
+
|
120
|
+
### Default claims
|
121
|
+
|
122
|
+
Gem currently supports two of the standard claims: `exp` and `iss`.
|
123
|
+
|
124
|
+
#### Expiry
|
125
|
+
|
126
|
+
You can set `expiry` option on configuration to a preferred offset for generated tokens:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class LongLivedToken < JWT::Token
|
130
|
+
configuration.expiry = 2 * 365 * 24 * 60 * 60
|
131
|
+
end
|
132
|
+
|
133
|
+
token = LongLivedToken.new
|
134
|
+
Time.at token.expiry
|
135
|
+
#=> 2020-03-06 08:59:52 +0100
|
136
|
+
```
|
137
|
+
|
138
|
+
Note that `expiry` option in configuration is an offset, while on token instance it's a timestamp.
|
139
|
+
|
140
|
+
On instance you can either assign timestamp, or a `Time` instance.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
token = JWT::Token.new
|
144
|
+
token.expiry = Time.utc(2021, 1, 1)
|
145
|
+
token.expiry
|
146
|
+
#=> 1609459200
|
147
|
+
|
148
|
+
JWT::Token.new(expiry: Time.utc(2021, 1, 1)).expiry
|
149
|
+
#=> 1609459200
|
113
150
|
```
|
114
151
|
|
115
|
-
|
152
|
+
`exp` claim will be validated if present.
|
153
|
+
|
154
|
+
### Issuer
|
155
|
+
|
156
|
+
In order to validate `issuer` claim, set `allowed_issuers` on token class:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class MicroserviceToken < JWT::Token
|
160
|
+
configuration.allowed_issuers = ["apiservice", "cronservice"]
|
161
|
+
end
|
162
|
+
|
163
|
+
MicroserviceToken.verify(MicroserviceToken.new(issuer: "apiservice").to_jwt)
|
164
|
+
#=> #<MicroserviceToken @claims={"exp"=>1520413510, "iss"=>"apiservice"}>
|
165
|
+
MicroserviceToken.verify(MicroserviceToken.new(issuer: "otherservice").to_jwt)
|
166
|
+
# JWT::InvalidIssuerError: Invalid issuer. Expected ["apiservice", "cronservice"], received otherservice
|
167
|
+
MicroserviceToken.verify(MicroserviceToken.new(issuer: nil).to_jwt)
|
168
|
+
# JWT::InvalidIssuerError: Invalid issuer. Expected ["apiservice", "cronservice"], received <none>
|
169
|
+
```
|
116
170
|
|
117
171
|
## Development
|
118
172
|
|
@@ -122,7 +176,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
122
176
|
|
123
177
|
## Contributing
|
124
178
|
|
125
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
179
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/codesthq/jwt-authorizer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
126
180
|
|
127
181
|
## License
|
128
182
|
|
@@ -130,4 +184,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
130
184
|
|
131
185
|
## Code of Conduct
|
132
186
|
|
133
|
-
Everyone interacting in the JWT::
|
187
|
+
Everyone interacting in the JWT::Token project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/codesthq/jwt-authorizer/blob/master/CODE_OF_CONDUCT.md).
|
data/bin/console
CHANGED
@@ -7,5 +7,9 @@ require "jwt/authorizer"
|
|
7
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
8
8
|
# with your gem easier. You can also use a different console, if you like.
|
9
9
|
|
10
|
+
JWT::Token.configure do |config|
|
11
|
+
config.secret = "hmac"
|
12
|
+
end
|
13
|
+
|
10
14
|
require "pry"
|
11
15
|
Pry.start
|
data/jwt-authorizer.gemspec
CHANGED
@@ -10,6 +10,8 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ["Michał Begejowicz"]
|
11
11
|
spec.email = ["michal.begejowicz@codesthq.com"]
|
12
12
|
|
13
|
+
spec.required_ruby_version = "~> 2.4"
|
14
|
+
|
13
15
|
spec.summary = "Authorization of requests for microservices based on JWT"
|
14
16
|
spec.description = "Authorization of requests for microservices based on JWT"
|
15
17
|
spec.homepage = "https://github.com/codesthq/jwt-authorizer"
|
data/lib/jwt/authorizer.rb
CHANGED
@@ -3,21 +3,20 @@
|
|
3
3
|
require "jwt/authorizer/version"
|
4
4
|
require "jwt"
|
5
5
|
|
6
|
-
require "jwt/
|
7
|
-
require "jwt/
|
8
|
-
require "jwt/
|
9
|
-
require "jwt/
|
6
|
+
require "jwt/token/builder"
|
7
|
+
require "jwt/token/configuration"
|
8
|
+
require "jwt/token/configurable"
|
9
|
+
require "jwt/token/verifier"
|
10
10
|
|
11
|
-
require "jwt/
|
12
|
-
require "jwt/
|
11
|
+
require "jwt/token/default_claims"
|
12
|
+
require "jwt/token/claim"
|
13
|
+
require "jwt/token/claim_builder"
|
14
|
+
|
15
|
+
require "jwt/token"
|
13
16
|
|
14
17
|
module JWT
|
15
|
-
|
16
|
-
include Configurable
|
17
|
-
include Builder
|
18
|
-
include Verifier
|
19
|
-
include Validation
|
18
|
+
module Authorizer
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
|
-
require "jwt/
|
22
|
+
require "jwt/endpoint_token"
|
@@ -1,27 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
class
|
4
|
+
class EndpointToken < Token
|
5
5
|
class << self
|
6
6
|
attr_writer :token_extractor
|
7
7
|
|
8
8
|
def token_extractor
|
9
9
|
@token_extractor ||= proc { |req| req.env["X-Auth-Token"] || req.params["_t"] }
|
10
10
|
end
|
11
|
+
|
12
|
+
def verify(rack_request)
|
13
|
+
token = token_extractor.call(rack_request)
|
14
|
+
|
15
|
+
super(token, rack_request)
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
|
-
|
19
|
+
claim :path, required: true do |value, rack_req|
|
14
20
|
raise JWT::DecodeError, "Unexpected path: #{value}" unless value == rack_req.path
|
15
21
|
end
|
16
22
|
|
17
|
-
|
23
|
+
claim :verb, required: true do |value, rack_req|
|
18
24
|
raise JWT::DecodeError, "Unexpected request method: #{value}" unless value.to_s.upcase == rack_req.request_method
|
19
25
|
end
|
20
|
-
|
21
|
-
def verify(rack_request)
|
22
|
-
token = self.class.token_extractor.call(rack_request)
|
23
|
-
|
24
|
-
super(token, rack_request)
|
25
|
-
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/jwt/token.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
class Token
|
5
|
+
module Builder
|
6
|
+
def initialize(claims = {})
|
7
|
+
claims.each { |claim, value| send("#{claim}=", value) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_jwt
|
11
|
+
JWT.encode claims.compact, secret[:private], algorithm
|
12
|
+
end; alias to_s to_jwt
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
class Token
|
5
|
+
class MissingClaim < JWT::DecodeError
|
6
|
+
attr_reader :claim
|
7
|
+
|
8
|
+
def initialize(claim)
|
9
|
+
@claim = claim
|
10
|
+
super("Token is missing required claim: #{claim}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Claim
|
15
|
+
attr_reader :name, :key, :required, :verifier
|
16
|
+
|
17
|
+
def initialize(name, key, required, verifier)
|
18
|
+
@name = name
|
19
|
+
@key = key
|
20
|
+
@required = required
|
21
|
+
@verifier = verifier
|
22
|
+
end
|
23
|
+
|
24
|
+
def verify(token, context = nil)
|
25
|
+
value = token.send(name)
|
26
|
+
|
27
|
+
raise(MissingClaim, key) if required && value.nil?
|
28
|
+
verifier.call(value, context) if value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
class Token
|
5
|
+
module ClaimBuilder
|
6
|
+
class << self
|
7
|
+
def define_accessor_methods(token_class, claim)
|
8
|
+
define_getter(token_class, claim)
|
9
|
+
define_setter(token_class, claim)
|
10
|
+
end
|
11
|
+
|
12
|
+
def define_getter(token_class, claim)
|
13
|
+
token_class.send(:define_method, claim.name) { claims[claim.key] }
|
14
|
+
token_class.send(:alias_method, claim.key, claim.name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_setter(token_class, claim)
|
18
|
+
method_name = "#{claim.name}="
|
19
|
+
token_class.send(:define_method, method_name) { |value| claims[claim.key] = value }
|
20
|
+
token_class.send(:alias_method, "#{claim.key}=", method_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def included(base)
|
24
|
+
base.extend(ClassMethods)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def inherited(subclass)
|
31
|
+
subclass.claims.concat(claims)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def claims
|
36
|
+
@claims ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def claim(name, key: name.to_s, required: false, &verifier)
|
40
|
+
claim = JWT::Token::Claim.new(name, key, required, verifier).tap(&:freeze)
|
41
|
+
|
42
|
+
claims << claim
|
43
|
+
JWT::Token::ClaimBuilder.define_accessor_methods(self, claim)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,16 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JWT
|
4
|
-
class
|
4
|
+
class Token
|
5
5
|
module Configurable
|
6
6
|
def self.included(base)
|
7
7
|
base.extend(ClassMethods)
|
8
8
|
base.extend(Forwardable)
|
9
|
-
base.delegate %i[algorithm secret
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(**options)
|
13
|
-
@config = self.class.configuration.dup.merge(options)
|
9
|
+
base.delegate %i[algorithm secret allowed_issuers] => "self.class.configuration"
|
14
10
|
end
|
15
11
|
|
16
12
|
module ClassMethods
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
class Token
|
5
|
+
module DefaultClaims
|
6
|
+
def expiry
|
7
|
+
claims["exp"]
|
8
|
+
end; alias exp expiry
|
9
|
+
|
10
|
+
def issuer
|
11
|
+
claims["iss"]
|
12
|
+
end; alias iss issuer
|
13
|
+
|
14
|
+
def expiry=(value)
|
15
|
+
claims["exp"] = value.is_a?(Time) ? value.to_i : value
|
16
|
+
end; alias exp= expiry=
|
17
|
+
|
18
|
+
def issuer=(value)
|
19
|
+
claims["iss"] = value
|
20
|
+
end; alias iss= issuer=
|
21
|
+
|
22
|
+
def claims
|
23
|
+
@claims ||= { "exp" => fetch_expiry, "iss" => self.class.configuration.issuer }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def fetch_expiry
|
29
|
+
(Time.now + self.class.configuration.expiry).to_i if self.class.configuration.expiry
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JWT
|
4
|
+
class Token
|
5
|
+
module Verifier
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def verify(jwt_token, context = nil)
|
13
|
+
decoded = JWT.decode(jwt_token, configuration.secret[:public], true, decode_options)
|
14
|
+
|
15
|
+
new(decoded[0]).tap do |token|
|
16
|
+
claims.each do |claim|
|
17
|
+
claim.verify(token, context)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def decode_options
|
25
|
+
{}.tap do |result|
|
26
|
+
if configuration.allowed_issuers.any?
|
27
|
+
result[:iss] = configuration.allowed_issuers
|
28
|
+
result[:verify_iss] = true
|
29
|
+
end
|
30
|
+
result[:algorithm] = configuration.algorithm
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt-authorizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michał Begejowicz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -159,14 +159,16 @@ files:
|
|
159
159
|
- bin/setup
|
160
160
|
- jwt-authorizer.gemspec
|
161
161
|
- lib/jwt/authorizer.rb
|
162
|
-
- lib/jwt/authorizer/builder.rb
|
163
|
-
- lib/jwt/authorizer/claim_validator.rb
|
164
|
-
- lib/jwt/authorizer/configurable.rb
|
165
|
-
- lib/jwt/authorizer/configuration.rb
|
166
|
-
- lib/jwt/authorizer/validation.rb
|
167
|
-
- lib/jwt/authorizer/verifier.rb
|
168
162
|
- lib/jwt/authorizer/version.rb
|
169
|
-
- lib/jwt/
|
163
|
+
- lib/jwt/endpoint_token.rb
|
164
|
+
- lib/jwt/token.rb
|
165
|
+
- lib/jwt/token/builder.rb
|
166
|
+
- lib/jwt/token/claim.rb
|
167
|
+
- lib/jwt/token/claim_builder.rb
|
168
|
+
- lib/jwt/token/configurable.rb
|
169
|
+
- lib/jwt/token/configuration.rb
|
170
|
+
- lib/jwt/token/default_claims.rb
|
171
|
+
- lib/jwt/token/verifier.rb
|
170
172
|
homepage: https://github.com/codesthq/jwt-authorizer
|
171
173
|
licenses:
|
172
174
|
- MIT
|
@@ -177,9 +179,9 @@ require_paths:
|
|
177
179
|
- lib
|
178
180
|
required_ruby_version: !ruby/object:Gem::Requirement
|
179
181
|
requirements:
|
180
|
-
- - "
|
182
|
+
- - "~>"
|
181
183
|
- !ruby/object:Gem::Version
|
182
|
-
version: '
|
184
|
+
version: '2.4'
|
183
185
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
184
186
|
requirements:
|
185
187
|
- - ">"
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
class Authorizer
|
5
|
-
module Builder
|
6
|
-
def build(claims = {})
|
7
|
-
payload = default_claims.merge!(claims)
|
8
|
-
JWT.encode payload, secret[:private], algorithm
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def default_claims
|
14
|
-
{}.tap do |result|
|
15
|
-
result[:exp] = (Time.now + expiry).to_i if expiry
|
16
|
-
result[:iss] = issuer if issuer
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
class Authorizer
|
5
|
-
class MissingClaim < StandardError
|
6
|
-
attr_reader :claim
|
7
|
-
|
8
|
-
def initialize(claim)
|
9
|
-
@claim = claim
|
10
|
-
super("Token is missing required claim: #{claim}")
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class ClaimValidator
|
15
|
-
attr_reader :name, :required, :verifier
|
16
|
-
|
17
|
-
def initialize(name:, required: false, &block)
|
18
|
-
@name = name.to_s
|
19
|
-
@required = required
|
20
|
-
@verifier = block
|
21
|
-
end
|
22
|
-
|
23
|
-
def validate(token, context)
|
24
|
-
value = token.dig(0, name)
|
25
|
-
raise MissingClaim, name if required && !value
|
26
|
-
return unless value
|
27
|
-
|
28
|
-
verifier.call(value, context)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
class Authorizer
|
5
|
-
module Validation
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
end
|
9
|
-
|
10
|
-
def verify(token, context = nil)
|
11
|
-
super(token).tap do |decoded|
|
12
|
-
validate_token(decoded, context)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def validate_token(token, context)
|
19
|
-
self.class.validators.each do |validator|
|
20
|
-
validator.validate(token, context)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
module ClassMethods
|
25
|
-
def inherited(subclass)
|
26
|
-
subclass.instance_variable_set("@validators", validators.dup)
|
27
|
-
super
|
28
|
-
end
|
29
|
-
|
30
|
-
def validators
|
31
|
-
@validators ||= []
|
32
|
-
end
|
33
|
-
|
34
|
-
def validate(claim_name, required: false, &block)
|
35
|
-
validators << ClaimValidator.new(name: claim_name, required: required, &block)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JWT
|
4
|
-
class Authorizer
|
5
|
-
module Verifier
|
6
|
-
def verify(token)
|
7
|
-
JWT.decode token, secret[:public], true, decode_options
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def decode_options
|
13
|
-
{}.tap do |result|
|
14
|
-
if allowed_issuers.any?
|
15
|
-
result[:iss] = allowed_issuers
|
16
|
-
result[:verify_iss] = true
|
17
|
-
end
|
18
|
-
result[:algorithm] = algorithm
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|