# frozen_string_literal: true require 'zache' class Zache def last(key) @hash[key][:value] if @hash.key?(key) end end module Minty module Mixins module Validation class JWTAlgorithm private_class_method :new def name raise 'Must be overriden by the subclasses' end end module Algorithm class HS256 < JWTAlgorithm class << self private :new def secret(secret) new secret end end attr_accessor :secret def initialize(secret) raise Minty::InvalidParameter, 'Must supply a valid secret' if secret.to_s.empty? @secret = secret end def name 'HS256' end end class RS256 < JWTAlgorithm include Minty::Mixins::HTTPProxy @@cache = Zache.new.freeze class << self private :new def jwks_url(url, lifetime: 10 * 60) new url, lifetime end def remove_jwks @@cache.remove_by { true } end end def initialize(jwks_url, lifetime) raise Minty::InvalidParameter, 'Must supply a valid jwks_url' if jwks_url.to_s.empty? unless lifetime.is_a?(Integer) && lifetime >= 0 raise Minty::InvalidParameter, 'Must supply a valid lifetime' end @lifetime = lifetime @jwks_url = jwks_url @did_fetch_jwks = false end def name 'RS256' end def jwks(force: false) result = fetch_jwks if force if result @@cache.put(@jwks_url, result, lifetime: @lifetime) return result end previous_value = @@cache.last(@jwks_url) @@cache.get(@jwks_url, lifetime: @lifetime, dirty: true) do new_value = fetch_jwks raise Minty::InvalidIdToken, 'Could not fetch the JWK set' unless new_value || previous_value new_value || previous_value end end def fetched_jwks? @did_fetch_jwks end private def fetch_jwks result = request_with_retry(:get, @jwks_url, {}, {}) @did_fetch_jwks = result.is_a?(Hash) && result.key?('keys') result if @did_fetch_jwks end end end end end end