-
Notifications
You must be signed in to change notification settings - Fork 374
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Matteo Pierro <[email protected]>
- Loading branch information
1 parent
e674ca3
commit d7f680b
Showing
43 changed files
with
1,105 additions
and
784 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'claims/audience' | ||
require_relative 'claims/expiration' | ||
require_relative 'claims/issued_at' | ||
require_relative 'claims/issuer' | ||
require_relative 'claims/jwt_id' | ||
require_relative 'claims/not_before' | ||
require_relative 'claims/numeric' | ||
require_relative 'claims/required' | ||
require_relative 'claims/subject' | ||
|
||
module JWT | ||
module Claims | ||
VerificationContext = Struct.new(:payload, keyword_init: true) | ||
|
||
VERIFIERS = { | ||
verify_expiration: ->(options) { Claims::Expiration.new(leeway: options[:exp_leeway] || options[:leeway]) }, | ||
verify_not_before: ->(options) { Claims::NotBefore.new(leeway: options[:nbf_leeway] || options[:leeway]) }, | ||
verify_iss: ->(options) { Claims::Issuer.new(issuers: options[:iss]) }, | ||
verify_iat: ->(*) { Claims::IssuedAt.new }, | ||
verify_jti: ->(options) { Claims::JwtId.new(validator: options[:verify_jti]) }, | ||
verify_aud: ->(options) { Claims::Audience.new(expected_audience: options[:aud]) }, | ||
verify_sub: ->(options) { options[:sub] && Claims::Subject.new(expected_subject: options[:sub]) }, | ||
required_claims: ->(options) { Claims::Required.new(required_claims: options[:required_claims]) } | ||
}.freeze | ||
|
||
class << self | ||
def verify!(payload, options) | ||
VERIFIERS.each do |key, verifier_builder| | ||
next unless options[key] | ||
|
||
verifier_builder&.call(options)&.verify!(context: VerificationContext.new(payload: payload)) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Audience | ||
def initialize(expected_audience:) | ||
@expected_audience = expected_audience | ||
end | ||
|
||
def verify!(context:, **_args) | ||
aud = context.payload['aud'] | ||
raise JWT::InvalidAudError, "Invalid audience. Expected #{expected_audience}, received #{aud || '<none>'}" if ([*aud] & [*expected_audience]).empty? | ||
end | ||
|
||
private | ||
|
||
attr_reader :expected_audience | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Expiration | ||
def initialize(leeway:) | ||
@leeway = leeway || 0 | ||
end | ||
|
||
def verify!(context:, **_args) | ||
return unless context.payload.is_a?(Hash) | ||
return unless context.payload.key?('exp') | ||
|
||
raise JWT::ExpiredSignature, 'Signature has expired' if context.payload['exp'].to_i <= (Time.now.to_i - leeway) | ||
end | ||
|
||
private | ||
|
||
attr_reader :leeway | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class IssuedAt | ||
def verify!(context:, **_args) | ||
return unless context.payload.is_a?(Hash) | ||
return unless context.payload.key?('iat') | ||
|
||
iat = context.payload['iat'] | ||
raise(JWT::InvalidIatError, 'Invalid iat') if !iat.is_a?(::Numeric) || iat.to_f > Time.now.to_f | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Issuer | ||
def initialize(issuers:) | ||
@issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item } | ||
end | ||
|
||
def verify!(context:, **_args) | ||
case (iss = context.payload['iss']) | ||
when *issuers | ||
nil | ||
else | ||
raise JWT::InvalidIssuerError, "Invalid issuer. Expected #{issuers}, received #{iss || '<none>'}" | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :issuers | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class JwtId | ||
def initialize(validator:) | ||
@validator = validator | ||
end | ||
|
||
def verify!(context:, **_args) | ||
jti = context.payload['jti'] | ||
if validator.respond_to?(:call) | ||
verified = validator.arity == 2 ? validator.call(jti, context.payload) : validator.call(jti) | ||
raise(JWT::InvalidJtiError, 'Invalid jti') unless verified | ||
elsif jti.to_s.strip.empty? | ||
raise(JWT::InvalidJtiError, 'Missing jti') | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :validator | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class NotBefore | ||
def initialize(leeway:) | ||
@leeway = leeway || 0 | ||
end | ||
|
||
def verify!(context:, **_args) | ||
return unless context.payload.is_a?(Hash) | ||
return unless context.payload.key?('nbf') | ||
|
||
raise JWT::ImmatureSignature, 'Signature nbf has not been reached' if context.payload['nbf'].to_i > (Time.now.to_i + leeway) | ||
end | ||
|
||
private | ||
|
||
attr_reader :leeway | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Numeric | ||
def self.verify!(payload:, **_args) | ||
return unless payload.is_a?(Hash) | ||
|
||
new(payload).verify! | ||
end | ||
|
||
NUMERIC_CLAIMS = %i[ | ||
exp | ||
iat | ||
nbf | ||
].freeze | ||
|
||
def initialize(payload) | ||
@payload = payload.transform_keys(&:to_sym) | ||
end | ||
|
||
def verify! | ||
validate_numeric_claims | ||
|
||
true | ||
end | ||
|
||
private | ||
|
||
def validate_numeric_claims | ||
NUMERIC_CLAIMS.each do |claim| | ||
validate_is_numeric(claim) if @payload.key?(claim) | ||
end | ||
end | ||
|
||
def validate_is_numeric(claim) | ||
return if @payload[claim].is_a?(::Numeric) | ||
|
||
raise InvalidPayload, "#{claim} claim must be a Numeric value but it is a #{@payload[claim].class}" | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Required | ||
def initialize(required_claims:) | ||
@required_claims = required_claims | ||
end | ||
|
||
def verify!(context:, **_args) | ||
required_claims.each do |required_claim| | ||
next if context.payload.is_a?(Hash) && context.payload.key?(required_claim) | ||
|
||
raise JWT::MissingRequiredClaim, "Missing required claim #{required_claim}" | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :required_claims | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# frozen_string_literal: true | ||
|
||
module JWT | ||
module Claims | ||
class Subject | ||
def initialize(expected_subject:) | ||
@expected_subject = expected_subject.to_s | ||
end | ||
|
||
def verify!(context:, **_args) | ||
sub = context.payload['sub'] | ||
raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject | ||
end | ||
|
||
private | ||
|
||
attr_reader :expected_subject | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.