Untitled
unknown
plain_text
18 days ago
6.4 kB
3
Indexable
Never
class JwtService attr_accessor :current_manage, :cache_key HS256 = 'HS256' JURNAL_CONSUMER_ID = ENV['MASTER_JURNAL_ID'] def initialize(current_manage = nil, source='browser') @current_manage = current_manage @source = source if current_manage&.kong_id.present? @cache_key = jwt_cache_key(current_manage.kong_id) end end def generate_old_jwt_token(payload: {}, cached: true) jwt = Kong.create_jwt_credential(@current_manage.kong_id) payload.merge!({ :iss => jwt.key.to_s }) JwtStruct.new({ token: JWT.encode(payload, jwt_secret(jwt.secret.to_s), HS256), secret: jwt.secret, key: jwt.key, id: jwt.id }) end def generate_jwt_token(payload: {}, cached: false) jwt = JwtStruct.new unless available? return jwt end if cached jwt = get_jwt_token end unless jwt_struct_empty?(jwt) return jwt end jwt = get_or_create_jwt_creds generated_at = DateTime.now.strftime('%Q') # epoch version payload.merge!({ iss: "jwt.key.to_s", user_id: @current_manage.user_id, company_id: @current_manage.company_id, generated_at: generated_at }) token = JWT.encode(payload, jwt_secret("jwt.secret.to_s"), HS256) jwt_result = { token: token, secret: "jwt.secret", key: "jwt.key", id: "jwt.id" } Rails.cache.write(@cache_key, jwt_result, expires_in: 2.hours) JwtStruct.new(jwt_result) end def switch_jwt_token(payload: {}, cached: false, prev_kong_id: '') unless available? return JwtStruct.new end prev_cache_key = jwt_cache_key(prev_kong_id) jwt = generate_jwt_token(payload: payload, cached: cached) prev_jwt_ttl = Rails.cache.data.ttl(prev_cache_key) Rails.cache.write(@cache_key, jwt, expires_in: prev_jwt_ttl.seconds) # delete prev token delete_jwt_token(prev_cache_key) jwt end def get_jwt_token unless available? return JwtStruct.new end jwt = Rails.cache.fetch(@cache_key) || {} unless jwt.present? return JwtStruct.new end JwtStruct.new(jwt) end def get_internal_jwt_token return JwtStruct.new unless @current_manage.present? jwt = Rails.cache.fetch("jwt_internal_token:#{@current_manage.kong_id}") unless jwt.present? return JwtStruct.new end JwtStruct.new(jwt) end def delete_jwt_token(cache_key=nil) unless available? return nil end if cache_key.nil? cache_key = @cache_key end Rails.cache.delete(cache_key) end def decode_jwt_token(token, secret=nil) unless available? return {} end if secret.nil? secret = get_or_create_jwt_creds&.secret || '' end JWT.decode(token, jwt_secret(secret), true, { algorithm: HS256 }) end def fetch_or_create_internal_jwt_token return JwtStruct.new if @current_manage.nil? return JwtStruct.new if @current_manage.kong_id.nil? || @current_manage.kong_id.blank? internal_cache_key = "jwt_internal_token:#{@current_manage.kong_id}" cached_jwt = Rails.cache.fetch(internal_cache_key) return JwtStruct.new(cached_jwt) if cached_jwt.present? jwt_credentials = Kong.get_jwt_credential(@current_manage.kong_id) internal_jwt = {} payload = {} if jwt_credentials.blank? internal_jwt = Kong.create_jwt_credential(@current_manage.kong_id) else current_jwt = Rails.cache.fetch(@cache_key) || {} jwt_credentials.each do |jwt| next if current_jwt[:key] == jwt['key'] && current_jwt[:secret] == jwt['secret'] internal_jwt = Kong::Jwt.new( { key: jwt["key"], secret: jwt["secret"], id: jwt["id"] } ) break end if internal_jwt.blank? internal_jwt = Kong.create_jwt_credential(@current_manage.kong_id) end end generated_at = DateTime.now.strftime('%Q') payload.merge!({ iss: internal_jwt.key.to_s, user_id: @current_manage.user_id, company_id: @current_manage.company_id, generated_at: generated_at }) jwt_result = { token: JWT.encode(payload, jwt_secret(internal_jwt.secret.to_s), HS256), secret: internal_jwt.secret, key: internal_jwt.key, id: internal_jwt.id } Rails.cache.write(internal_cache_key, jwt_result, expires_in: 2.hours) JwtStruct.new(jwt_result) end private def get_or_create_jwt_creds(consumer_id=JURNAL_CONSUMER_ID) # get from cache jwt_cred = Rails.cache.fetch("jwt_cred:#{consumer_id}") || {} if jwt_cred.present? return jwt_cred end # get from kong jwt_creds = Kong.get_jwt_credential(consumer_id) if jwt_creds.present? cred = jwt_creds[0] jwt_cred = Kong::Jwt.new( { key: cred["key"], secret: cred["secret"], id: cred["id"] } ) end # create from kong if jwt_cred.nil? jwt_cred = Kong.create_jwt_credential(consumer_id) end # set jwt creds to redis Rails.cache.write("jwt_cred:#{consumer_id}", jwt_cred, expires_in: 7.days) return jwt_cred end def jwt_struct_empty?(jwt) jwt == JwtStruct.new end def jwt_secret(secret) secret += '=' * (4 - secret.length.modulo(4)) Base64.decode64(secret.tr('-_', '+/')) end def jwt_cache_key(kong_id) [ 'jwt_token', @source, kong_id ].join(':') end def available? unless FeatureActivator::FlipperEnabler.toggle_jwt_service?(@current_manage&.company) return false end unless @current_manage.present? return false end return true end class << self def valid_token_with_timestamp_and_expiration?(token, expiration = 5.minutes) begin decoded_token = JWT.decode(token, ENV['MASTER_JURNAL_ID'], true, { algorithm: 'HS256' }) token = decoded_token[0] timestamp_str = token['timestamp'] return false if timestamp_str.blank? timestamp = Time.zone.parse(timestamp_str) return false if timestamp.nil? Time.zone.now - timestamp < expiration rescue JWT::DecodeError => e return false end end def authenticate_jwt_header_by_timestamp(auth_header) return false if auth_header.blank? token = auth_header.delete_prefix('Bearer ') valid_token_with_timestamp_and_expiration?(token, 30.seconds) end end end
Leave a Comment