padlock_auth-jwt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|