Untitled
unknown
plain_text
2 years ago
6.4 kB
12
Indexable
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
Editor is loading...
Leave a Comment