Skip to content

Commit

Permalink
fix: Cache keyset for decoding
Browse files Browse the repository at this point in the history
Cache keyset for decoding
  • Loading branch information
KoenSengers committed Aug 2, 2024
1 parent b078859 commit f17c142
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 5 deletions.
2 changes: 1 addition & 1 deletion keypairs.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rubocop-performance' # Linter for Performance optimization analysis
spec.add_development_dependency 'rubocop-rails' # Linter for Rails-specific analysis
spec.add_development_dependency 'shoulda-matchers' # RSpec matchers
spec.add_development_dependency 'sqlite3' # Database adapter
spec.add_development_dependency 'sqlite3', '~> 1.4' # Database adapter
spec.add_development_dependency 'timecop' # Freeze time to test time-dependent code
end
21 changes: 19 additions & 2 deletions lib/keypair.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
# @attr [Time] not_before The time before which no payloads may be signed using the keypair.
# @attr [Time] not_after The time after which no payloads may be signed using the keypair.
# @attr [Time] expires_at The time after which the keypair may not be used for signature validation.
class Keypair < ActiveRecord::Base
class Keypair < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
ALGORITHM = 'RS256'
ROTATION_INTERVAL = 1.month

Expand Down Expand Up @@ -128,12 +128,15 @@ def self.jwt_encode_without_nonce(payload)
# @raise [JWT::DecodeError] or any of it's subclasses if the decoding / validation fails.
# @return [Hash] Decoded payload hash with indifferent access.
def self.jwt_decode(id_token, options = {})
decoded_keyset = Rails.cache.fetch('keypairs/Keypair/keyset_decode', expires_in: decode_cache_expires_in) do
keyset
end
# Add default decoding options
options.reverse_merge!(
# Change the default algorithm to match the encoding algorithm
algorithm: ALGORITHM,
# Load our own keyset as valid keys
jwks: keyset,
jwks: decoded_keyset,
# If the `sub` is provided, validate that it matches the payload `sub`
verify_sub: true
)
Expand Down Expand Up @@ -245,4 +248,18 @@ def secure_payload(payload, nonce: true)

payload.reverse_merge!(secure_payload)
end

class << self
def decode_cache_expires_in
expiring_time = first_expiring_or_not_before_key
expiring_time ? expiring_time - Time.zone.now : nil
end

def first_expiring_or_not_before_key
valid_keys = valid
expires_in_key = valid_keys.minimum(:expires_at)
not_before_key = valid_keys.where(arel_table[:not_before].gt(Time.zone.now)).minimum(:not_before)
[expires_in_key, not_before_key].compact.min
end
end
end
4 changes: 2 additions & 2 deletions spec/support/matchers/encrypt_attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
match do |model|
# Correct responds to methods
model.respond_to?(attribute) &&
model.respond_to?("#{attribute}=") &&
model.respond_to?(:"#{attribute}=") &&
model.respond_to?(database_column_name) &&
model.respond_to?("#{database_column_name}=") &&
model.respond_to?(:"#{database_column_name}=") &&
# Correct database columns
model.class.column_names.exclude?(attribute.to_s) &&
model.class.column_names.include?(database_column_name)
Expand Down

0 comments on commit f17c142

Please sign in to comment.