padlock_auth-jwt 0.1.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +13 -0
- data/config/locales/padlock_auth-jwt.en.yml +22 -0
- data/lib/padlock_auth/jwt/access_token.rb +171 -0
- data/lib/padlock_auth/jwt/errors.rb +25 -0
- data/lib/padlock_auth/jwt/http/forbidden_token_response.rb +14 -0
- data/lib/padlock_auth/jwt/http/invalid_token_response.rb +35 -0
- data/lib/padlock_auth/jwt/railtie.rb +9 -0
- data/lib/padlock_auth/jwt/strategy.rb +208 -0
- data/lib/padlock_auth/jwt/version.rb +5 -0
- data/lib/padlock_auth/jwt.rb +18 -0
- data/lib/padlock_auth-jwt.rb +1 -0
- data/lib/tasks/padlock_auth/jwt_tasks.rake +4 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 76622de62cd8065e302932783de9a3c02bff49880ec8f376fcdeed84cce97223
|
4
|
+
data.tar.gz: 460cb25344c8150779ad54a189a8d9ec371c63ae7bd1adf4b3b9af3183deca62
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 39e6d74dbedae4ca68c3cef9a19bc3bcc8a4d2307877f2b557c616fd56ea4815e0e176726bb1a539141a2eb66b6c0bc503dce1160d290f4802f7cb2a4f928e2d
|
7
|
+
data.tar.gz: 4f3e41598b073669229e2de1da5543801e7b1182976dbdb67e84260af17215bb1245160da24a580bdd31454060d671f5a61b0510908983c31246c9eb3ec6ab0a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright Ben Morrall
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# PadlockAuth::Jwt
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem "padlock_auth-jwt"
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install padlock_auth-jwt
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
en:
|
2
|
+
padlock_auth:
|
3
|
+
errors:
|
4
|
+
messages:
|
5
|
+
invalid_token:
|
6
|
+
invalid_jwt_token: "The access token is not a valid JWT."
|
7
|
+
invalid_signature: "The access token has an invalid signature."
|
8
|
+
missing_exp_claim: "The access token is missing a required exp claim."
|
9
|
+
missing_nbf_claim: "The access token is missing a required nbf claim."
|
10
|
+
missing_iss_claim: "The access token is missing a required iss claim."
|
11
|
+
missing_aud_claim: "The access token is missing a required aud claim."
|
12
|
+
missing_jti_claim: "The access token is missing a required jti claim."
|
13
|
+
missing_iat_claim: "The access token is missing a required iat claim."
|
14
|
+
missing_sub_claim: "The access token is missing a required sub claim."
|
15
|
+
invalid_exp_claim: "The access token has expired."
|
16
|
+
invalid_nbf_claim: "The access token is not yet valid."
|
17
|
+
invalid_iss_claim: "The access token is from an unknown issuer."
|
18
|
+
invalid_jti_claim: "The access token was revoked."
|
19
|
+
invalid_sub_claim: "The access token is for a different subject."
|
20
|
+
forbidden_token:
|
21
|
+
invalid_jwt_token: "The access token is not a valid JWT."
|
22
|
+
invalid_aud_claim: 'Access to this resource requires audience "%{oauth_scopes}".'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require "jwt"
|
2
|
+
|
3
|
+
module PadlockAuth
|
4
|
+
module Jwt
|
5
|
+
class AccessToken < PadlockAuth::AbstractAccessToken
|
6
|
+
def initialize(jwt, strategy)
|
7
|
+
@jwt = jwt
|
8
|
+
@encoded_token = JWT::EncodedToken.new(jwt)
|
9
|
+
@strategy = strategy
|
10
|
+
end
|
11
|
+
|
12
|
+
def accessible?
|
13
|
+
return false unless valid_jwt_token?
|
14
|
+
|
15
|
+
return false unless valid_signature?
|
16
|
+
|
17
|
+
return false unless includes_required_claims?
|
18
|
+
|
19
|
+
# "exp" (Expiration Time) Claim
|
20
|
+
return false unless valid_exp_claim?
|
21
|
+
|
22
|
+
# "nbf" (Not Before) Claim
|
23
|
+
return false unless valid_nbf_claim?
|
24
|
+
|
25
|
+
# "iss" (Issuer) Claim
|
26
|
+
return false unless valid_iss_claim?
|
27
|
+
|
28
|
+
# "jti" (JWT ID) Claim
|
29
|
+
return false unless valid_jti_claim?
|
30
|
+
|
31
|
+
# "sub" (Subject) Claim
|
32
|
+
return false unless valid_sub_claim?
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def invalid_token_reason
|
38
|
+
return :invalid_jwt_token unless valid_jwt_token?
|
39
|
+
return :invalid_signature unless valid_signature?
|
40
|
+
|
41
|
+
return :missing_exp_claim unless includes_required_exp_claim?
|
42
|
+
return :invalid_exp_claim unless valid_exp_claim?
|
43
|
+
|
44
|
+
return :missing_nbf_claim unless includes_required_nbf_claim?
|
45
|
+
return :invalid_nbf_claim unless valid_nbf_claim?
|
46
|
+
|
47
|
+
return :missing_iss_claim unless includes_required_iss_claim?
|
48
|
+
return :invalid_iss_claim unless valid_iss_claim?
|
49
|
+
|
50
|
+
return :missing_aud_claim unless includes_required_aud_claim?
|
51
|
+
# validity checked by #includes_scope?
|
52
|
+
|
53
|
+
return :missing_jti_claim unless includes_required_jti_claim?
|
54
|
+
return :invalid_jti_claim unless valid_jti_claim?
|
55
|
+
|
56
|
+
return :missing_iat_claim unless includes_required_iat_claim?
|
57
|
+
|
58
|
+
return :missing_sub_claim unless includes_required_sub_claim?
|
59
|
+
return :invalid_sub_claim unless valid_sub_claim?
|
60
|
+
|
61
|
+
super # :unknown
|
62
|
+
end
|
63
|
+
|
64
|
+
def includes_scope?(required_scopes)
|
65
|
+
return false unless valid_jwt_token?
|
66
|
+
|
67
|
+
required_scopes.none? || valid_aud_claim?(required_scopes.map(&:to_s))
|
68
|
+
end
|
69
|
+
|
70
|
+
def forbidden_token_reason
|
71
|
+
return :invalid_jwt_token unless valid_jwt_token?
|
72
|
+
|
73
|
+
:invalid_aud_claim
|
74
|
+
end
|
75
|
+
|
76
|
+
def header
|
77
|
+
@encoded_token.header if valid_jwt_token?
|
78
|
+
end
|
79
|
+
|
80
|
+
def payload
|
81
|
+
@encoded_token.payload if valid_jwt_token?
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# https://datatracker.ietf.org/doc/html/rfc9068#JWTATLValidate
|
87
|
+
# The resource server MUST verify that the "typ" header value is "at+jwt" or "application/at+jwt" and reject tokens carrying any other value.
|
88
|
+
def valid_jwt_token?
|
89
|
+
return @valid_jwt_token if instance_variable_defined?(:@valid_jwt_token)
|
90
|
+
@valid_jwt_token = @encoded_token.header.present? &&
|
91
|
+
@strategy.header_types.include?(@encoded_token.header["typ"])
|
92
|
+
rescue JWT::DecodeError
|
93
|
+
@valid_jwt_token = false
|
94
|
+
end
|
95
|
+
|
96
|
+
def valid_signature?
|
97
|
+
return @valid_signature if instance_variable_defined?(:@valid_signature)
|
98
|
+
@valid_signature = @encoded_token.valid_signature?(algorithm: @strategy.algorithm, key: @strategy.secret_key)
|
99
|
+
end
|
100
|
+
|
101
|
+
def includes_required_claims?
|
102
|
+
includes_required_exp_claim? &&
|
103
|
+
includes_required_nbf_claim? &&
|
104
|
+
includes_required_iss_claim? &&
|
105
|
+
includes_required_aud_claim? &&
|
106
|
+
includes_required_jti_claim? &&
|
107
|
+
includes_required_iat_claim? &&
|
108
|
+
includes_required_sub_claim?
|
109
|
+
end
|
110
|
+
|
111
|
+
def includes_required_exp_claim?
|
112
|
+
!@strategy.exp_required? || @encoded_token.valid_claims?(required: ["exp"])
|
113
|
+
end
|
114
|
+
|
115
|
+
def includes_required_nbf_claim?
|
116
|
+
!@strategy.nbf_required? || @encoded_token.valid_claims?(required: ["nbf"])
|
117
|
+
end
|
118
|
+
|
119
|
+
def includes_required_iss_claim?
|
120
|
+
!@strategy.iss_required? || @encoded_token.valid_claims?(required: ["iss"])
|
121
|
+
end
|
122
|
+
|
123
|
+
def includes_required_aud_claim?
|
124
|
+
!@strategy.aud_required? || @encoded_token.valid_claims?(required: ["aud"])
|
125
|
+
end
|
126
|
+
|
127
|
+
def includes_required_jti_claim?
|
128
|
+
!@strategy.jti_required? || @encoded_token.valid_claims?(required: ["jti"])
|
129
|
+
end
|
130
|
+
|
131
|
+
def includes_required_iat_claim?
|
132
|
+
!@strategy.iat_required? || @encoded_token.valid_claims?(required: ["iat"])
|
133
|
+
end
|
134
|
+
|
135
|
+
def includes_required_sub_claim?
|
136
|
+
!@strategy.sub_required? || @encoded_token.valid_claims?(required: ["sub"])
|
137
|
+
end
|
138
|
+
|
139
|
+
def valid_exp_claim?
|
140
|
+
return @valid_exp_claim if instance_variable_defined?(:@valid_exp_claim)
|
141
|
+
@valid_exp_claim = @encoded_token.valid_claims?(exp: {leeway: @strategy.expiry_leeway})
|
142
|
+
end
|
143
|
+
|
144
|
+
def valid_nbf_claim?
|
145
|
+
return @valid_nbf_claim if instance_variable_defined?(:@valid_nbf_claim)
|
146
|
+
@valid_nbf_claim = @encoded_token.valid_claims?(nbf: {leeway: @strategy.not_before_leeway})
|
147
|
+
end
|
148
|
+
|
149
|
+
def valid_iss_claim?
|
150
|
+
return @valid_iss_claim if instance_variable_defined?(:@valid_iss_claim)
|
151
|
+
@valid_iss_claim = @strategy.issuers.none? || @encoded_token.valid_claims?(iss: @strategy.issuers)
|
152
|
+
end
|
153
|
+
|
154
|
+
def valid_aud_claim?(required_scopes)
|
155
|
+
@encoded_token.valid_claims?(aud: required_scopes.map(&:to_s))
|
156
|
+
end
|
157
|
+
|
158
|
+
def valid_jti_claim?
|
159
|
+
return @valid_jti_claim if instance_variable_defined?(:@valid_jti_claim)
|
160
|
+
@valid_jti_claim = @encoded_token.valid_claims?(jti: @strategy.verify_jti)
|
161
|
+
end
|
162
|
+
|
163
|
+
def valid_sub_claim?
|
164
|
+
return true if @strategy.subject.blank?
|
165
|
+
|
166
|
+
return @valid_sub_claim if instance_variable_defined?(:@valid_sub_claim)
|
167
|
+
@valid_sub_claim = @encoded_token.valid_claims?(sub: @strategy.subject)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PadlockAuth
|
2
|
+
module Jwt
|
3
|
+
module Errors
|
4
|
+
# Invalid Token errors
|
5
|
+
|
6
|
+
class InvalidSignature < PadlockAuth::Errors::InvalidToken; end
|
7
|
+
|
8
|
+
class MissingRequiredClaim < PadlockAuth::Errors::InvalidToken; end
|
9
|
+
|
10
|
+
class InvalidExpClaim < PadlockAuth::Errors::TokenExpired; end
|
11
|
+
|
12
|
+
class InvalidNbfClaim < PadlockAuth::Errors::InvalidToken; end
|
13
|
+
|
14
|
+
class InvalidIssClaim < PadlockAuth::Errors::InvalidToken; end
|
15
|
+
|
16
|
+
class InvalidJtiClaim < PadlockAuth::Errors::TokenRevoked; end
|
17
|
+
|
18
|
+
class InvalidSubClaim < PadlockAuth::Errors::InvalidToken; end
|
19
|
+
|
20
|
+
# Forbidden Token errors
|
21
|
+
|
22
|
+
class InvalidAudClaim < PadlockAuth::Errors::TokenForbidden; end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PadlockAuth
|
2
|
+
module Jwt
|
3
|
+
module Http
|
4
|
+
# Adds JWT specific errors to the ForbiddenTokenResponse
|
5
|
+
class ForbiddenTokenResponse < PadlockAuth::Http::ForbiddenTokenResponse
|
6
|
+
protected
|
7
|
+
|
8
|
+
def exception_class
|
9
|
+
PadlockAuth::Jwt::Errors::InvalidAudClaim
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PadlockAuth
|
2
|
+
module Jwt
|
3
|
+
module Http
|
4
|
+
# Adds JWT specific errors to the InvalidTokenResponse
|
5
|
+
class InvalidTokenResponse < PadlockAuth::Http::InvalidTokenResponse
|
6
|
+
protected
|
7
|
+
|
8
|
+
def exception_class
|
9
|
+
jwt_errors_mapping.fetch(reason) { super }
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def jwt_errors_mapping
|
15
|
+
{
|
16
|
+
invalid_signature: PadlockAuth::Jwt::Errors::InvalidSignature,
|
17
|
+
missing_exp_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
18
|
+
missing_nbf_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
19
|
+
missing_iss_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
20
|
+
missing_aud_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
21
|
+
missing_jti_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
22
|
+
missing_iat_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
23
|
+
missing_sub_claim: PadlockAuth::Jwt::Errors::MissingRequiredClaim,
|
24
|
+
invalid_exp_claim: PadlockAuth::Jwt::Errors::InvalidExpClaim,
|
25
|
+
invalid_nbf_claim: PadlockAuth::Jwt::Errors::InvalidNbfClaim,
|
26
|
+
invalid_iss_claim: PadlockAuth::Jwt::Errors::InvalidIssClaim,
|
27
|
+
invalid_jti_claim: PadlockAuth::Jwt::Errors::InvalidJtiClaim,
|
28
|
+
invalid_sub_claim: PadlockAuth::Jwt::Errors::InvalidSubClaim
|
29
|
+
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module PadlockAuthJwt
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
initializer "padlock_auth-jwt.i18n" do
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), "..", "..", "..", "config", "locales", "*.yml")).each do |file|
|
5
|
+
I18n.load_path << File.expand_path(file)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module PadlockAuth
|
2
|
+
module Jwt
|
3
|
+
class Strategy < PadlockAuth::AbstractStrategy
|
4
|
+
include PadlockAuth::Mixins::BuildWith
|
5
|
+
|
6
|
+
class Builder < PadlockAuth::Utils::AbstractBuilder
|
7
|
+
def nbf_leeway(nbf_leeway)
|
8
|
+
not_before_leeway(nbf_leeway)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
build_with Builder
|
13
|
+
|
14
|
+
extend PadlockAuth::Config::Option
|
15
|
+
|
16
|
+
# Token Factory
|
17
|
+
|
18
|
+
def build_access_token(raw_token)
|
19
|
+
Jwt::AccessToken.new(raw_token, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# HTTP Responses
|
23
|
+
|
24
|
+
def build_invalid_token_response(access_token)
|
25
|
+
Jwt::Http::InvalidTokenResponse.from_access_token(access_token)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_forbidden_token_response(access_token, scopes)
|
29
|
+
Jwt::Http::ForbiddenTokenResponse.from_access_token(access_token, scopes)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Signature
|
33
|
+
|
34
|
+
option :secret_key
|
35
|
+
|
36
|
+
# https://datatracker.ietf.org/doc/html/rfc7518#section-3.1
|
37
|
+
option :algorithm, default: "RS256"
|
38
|
+
|
39
|
+
# "typ" (Type) Header Parameter
|
40
|
+
#
|
41
|
+
# RFC9068 registers the "application/at+jwt" media type, which can be used to
|
42
|
+
# indicate that the content is a JWT access token. JWT access tokens MUST include this media
|
43
|
+
# type in the "typ" header parameter to explicitly declare that the JWT represents an access
|
44
|
+
# token complying with this profile.
|
45
|
+
|
46
|
+
# https://datatracker.ietf.org/doc/html/rfc9068#JWTATLValidate
|
47
|
+
REQUIRED_HEADER_TYPES = ["at+jwt", "application/at+jwt"]
|
48
|
+
|
49
|
+
option :header_types, default: REQUIRED_HEADER_TYPES
|
50
|
+
|
51
|
+
def header_types
|
52
|
+
Array.wrap(@header_types || REQUIRED_HEADER_TYPES)
|
53
|
+
end
|
54
|
+
|
55
|
+
# "exp" (Expiration Time) Claim
|
56
|
+
#
|
57
|
+
# The "exp" (expiration time) claim identifies the expiration time on
|
58
|
+
# or after which the JWT MUST NOT be accepted for processing. The
|
59
|
+
# processing of the "exp" claim requires that the current date/time
|
60
|
+
# MUST be before the expiration date/time listed in the "exp" claim.
|
61
|
+
#
|
62
|
+
# Implementers MAY provide for some small leeway, usually no more than
|
63
|
+
# a few minutes, to account for clock skew. Its value MUST be a number
|
64
|
+
# containing a NumericDate value. Use of this claim is OPTIONAL.
|
65
|
+
|
66
|
+
# @!attribute require_exp [r] Boolean
|
67
|
+
option :require_exp, default: true
|
68
|
+
|
69
|
+
def exp_required?
|
70
|
+
!!require_exp
|
71
|
+
end
|
72
|
+
|
73
|
+
option :expiry_leeway, default: 0
|
74
|
+
|
75
|
+
# "nbf" (Not Before) Claim
|
76
|
+
#
|
77
|
+
# The "nbf" (not before) claim identifies the time before which the JWT
|
78
|
+
# MUST NOT be accepted for processing. The processing of the "nbf"
|
79
|
+
# claim requires that the current date/time MUST be after or equal to
|
80
|
+
# the not-before date/time listed in the "nbf" claim. Implementers MAY
|
81
|
+
# provide for some small leeway, usually no more than a few minutes, to
|
82
|
+
# account for clock skew. Its value MUST be a number containing a
|
83
|
+
# NumericDate value. Use of this claim is OPTIONAL.
|
84
|
+
|
85
|
+
option :require_nbf, default: false
|
86
|
+
|
87
|
+
def nbf_required?
|
88
|
+
!!require_nbf
|
89
|
+
end
|
90
|
+
|
91
|
+
option :not_before_leeway, default: 0
|
92
|
+
|
93
|
+
# "iss" (Issuer) Claim
|
94
|
+
#
|
95
|
+
# The "iss" (issuer) claim identifies the principal that issued the
|
96
|
+
# JWT. The processing of this claim is generally application specific.
|
97
|
+
# The "iss" value is a case-sensitive string containing a StringOrURI
|
98
|
+
# value. Use of this claim is OPTIONAL.
|
99
|
+
|
100
|
+
# @!attribute require_iss [r] Boolean
|
101
|
+
option :require_iss, default: false
|
102
|
+
|
103
|
+
def iss_required?
|
104
|
+
!!require_iss
|
105
|
+
end
|
106
|
+
|
107
|
+
option :issuers, default: nil
|
108
|
+
|
109
|
+
def issuers
|
110
|
+
Array.wrap(@issuers)
|
111
|
+
end
|
112
|
+
|
113
|
+
# "aud" (Audience) Claim
|
114
|
+
#
|
115
|
+
# The "aud" (audience) claim identifies the recipients that the JWT is
|
116
|
+
# intended for. Each principal intended to process the JWT MUST
|
117
|
+
# identify itself with a value in the audience claim. If the principal
|
118
|
+
# processing the claim does not identify itself with a value in the
|
119
|
+
# "aud" claim when this claim is present, then the JWT MUST be
|
120
|
+
# rejected. In the general case, the "aud" value is an array of case-
|
121
|
+
# sensitive strings, each containing a StringOrURI value. In the
|
122
|
+
# special case when the JWT has one audience, the "aud" value MAY be a
|
123
|
+
# single case-sensitive string containing a StringOrURI value. The
|
124
|
+
# interpretation of audience values is generally application specific.
|
125
|
+
# Use of this claim is OPTIONAL.
|
126
|
+
|
127
|
+
option :require_aud, default: true
|
128
|
+
|
129
|
+
def aud_required?
|
130
|
+
!!require_aud
|
131
|
+
end
|
132
|
+
|
133
|
+
# "jti" (JWT ID) Claim
|
134
|
+
#
|
135
|
+
# The "jti" (JWT ID) claim provides a unique identifier for the JWT.
|
136
|
+
# The identifier value MUST be assigned in a manner that ensures that
|
137
|
+
# there is a negligible probability that the same value will be
|
138
|
+
# accidentally assigned to a different data object; if the application
|
139
|
+
# uses multiple issuers, collisions MUST be prevented among values
|
140
|
+
# produced by different issuers as well. The "jti" claim can be used
|
141
|
+
# to prevent the JWT from being replayed. The "jti" value is a case-
|
142
|
+
# sensitive string. Use of this claim is OPTIONAL.
|
143
|
+
|
144
|
+
option :require_jti, default: true
|
145
|
+
|
146
|
+
def jti_required?
|
147
|
+
!!require_jti
|
148
|
+
end
|
149
|
+
|
150
|
+
option :verify_jti, default: proc { true }
|
151
|
+
|
152
|
+
# "iat" (Issued At) Claim
|
153
|
+
#
|
154
|
+
# The "iat" (issued at) claim identifies the time at which the JWT was
|
155
|
+
# issued. This claim can be used to determine the age of the JWT. Its
|
156
|
+
# value MUST be a number containing a NumericDate value. Use of this
|
157
|
+
# claim is OPTIONAL.
|
158
|
+
|
159
|
+
option :require_iat, default: true
|
160
|
+
|
161
|
+
def iat_required?
|
162
|
+
!!require_iat
|
163
|
+
end
|
164
|
+
|
165
|
+
# "sub" (Subject) Claim
|
166
|
+
#
|
167
|
+
# The "sub" (subject) claim identifies the principal that is the
|
168
|
+
# subject of the JWT. The claims in a JWT are normally statements
|
169
|
+
# about the subject. The subject value MUST either be scoped to be
|
170
|
+
# locally unique in the context of the issuer or be globally unique.
|
171
|
+
# The processing of this claim is generally application specific. The
|
172
|
+
# "sub" value is a case-sensitive string containing a StringOrURI
|
173
|
+
# value. Use of this claim is OPTIONAL.
|
174
|
+
|
175
|
+
option :require_sub, default: true
|
176
|
+
|
177
|
+
def sub_required?
|
178
|
+
!!require_sub
|
179
|
+
end
|
180
|
+
|
181
|
+
option :subject, default: nil
|
182
|
+
|
183
|
+
def subject
|
184
|
+
Array.wrap(@subject)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Builder Validation
|
188
|
+
|
189
|
+
def validate!
|
190
|
+
raise ArgumentError, "secret_key is required" unless secret_key.present?
|
191
|
+
|
192
|
+
raise ArgumentError, "algorithm is required" unless algorithm.present?
|
193
|
+
|
194
|
+
raise ArgumentError, "header_types cannot be empty" unless header_types.present?
|
195
|
+
|
196
|
+
if subject.present? && !sub_required?
|
197
|
+
raise ArgumentError, "subject is not required"
|
198
|
+
end
|
199
|
+
|
200
|
+
if iss_required? && issuers.blank?
|
201
|
+
raise ArgumentError, "issuers are required when require_iss is true"
|
202
|
+
end
|
203
|
+
|
204
|
+
true
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "padlock_auth"
|
2
|
+
|
3
|
+
require "padlock_auth/jwt/version"
|
4
|
+
require "padlock_auth/jwt/errors"
|
5
|
+
|
6
|
+
module PadlockAuth
|
7
|
+
module Jwt
|
8
|
+
autoload :AccessToken, "padlock_auth/jwt/access_token"
|
9
|
+
autoload :Strategy, "padlock_auth/jwt/strategy"
|
10
|
+
|
11
|
+
module Http
|
12
|
+
autoload :InvalidTokenResponse, "padlock_auth/jwt/http/invalid_token_response"
|
13
|
+
autoload :ForbiddenTokenResponse, "padlock_auth/jwt/http/forbidden_token_response"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require "padlock_auth/jwt/railtie"
|
@@ -0,0 +1 @@
|
|
1
|
+
require "padlock_auth/jwt"
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: padlock_auth-jwt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Morrall
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: padlock_auth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.9.4
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.9.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: standard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.41.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.41.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Allows API endpoints to be secured by a JWT Access Token.
|
84
|
+
email:
|
85
|
+
- bemo56@hotmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- MIT-LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- config/locales/padlock_auth-jwt.en.yml
|
94
|
+
- lib/padlock_auth-jwt.rb
|
95
|
+
- lib/padlock_auth/jwt.rb
|
96
|
+
- lib/padlock_auth/jwt/access_token.rb
|
97
|
+
- lib/padlock_auth/jwt/errors.rb
|
98
|
+
- lib/padlock_auth/jwt/http/forbidden_token_response.rb
|
99
|
+
- lib/padlock_auth/jwt/http/invalid_token_response.rb
|
100
|
+
- lib/padlock_auth/jwt/railtie.rb
|
101
|
+
- lib/padlock_auth/jwt/strategy.rb
|
102
|
+
- lib/padlock_auth/jwt/version.rb
|
103
|
+
- lib/tasks/padlock_auth/jwt_tasks.rake
|
104
|
+
homepage: https://github.com/bmorrall/padlock_auth-jwt
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata:
|
108
|
+
homepage_uri: https://github.com/bmorrall/padlock_auth-jwt
|
109
|
+
source_code_uri: https://github.com/bmorrall/padlock_auth-jwt
|
110
|
+
changelog_uri: https://github.com/bmorrall/padlock_auth-jwt/main/CHANGELOG.md
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubygems_version: 3.5.17
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Adds JWT Support to PadlockAuth.
|
130
|
+
test_files: []
|