scinote-web/app/services/api/azure_jwt.rb

61 lines
1.9 KiB
Ruby
Raw Normal View History

module Api
class AzureJwt
require 'jwt'
KEYS_CACHING_PERIOD = 7.days
LEEWAY = 30
def self.fetch_rsa_key(k_id, app_id)
cache_key = "api_azure_ad_rsa_key_#{k_id}"
Rails.cache.fetch(cache_key, expires_in: KEYS_CACHING_PERIOD) do
conf_url = Api.configuration.azure_ad_apps[app_id][:conf_url]
keys_url = JSON.parse(Net::HTTP.get(URI(conf_url)))['jwks_uri']
data = JSON.parse(Net::HTTP.get(URI.parse(keys_url)))
verif_key = data['keys'].find { |key| key['kid'] == k_id }
unless verif_key
raise JWT::VerificationError,
'Azure AD: No keys from key endpoint match the key in the token'
end
JSON::JWK.new(verif_key).to_key.to_s
end
end
def self.decode(token)
# First, extract key id from token header,
# [1] is position of the header.
# We will use this ID to fetch correct public key needed for
# verification of the token signature
unverified_token = JWT.decode(token, nil, false)
k_id = unverified_token[1]['kid']
unless k_id
raise JWT::VerificationError, 'Azure AD: No Key ID in token header'
end
# Now search for matching app variables in configuration
app_id = unverified_token[0]['aud']
app_config = Api.configuration.azure_ad_apps[app_id]
unless app_config
raise JWT::VerificationError,
'Azure AD: No application configured with such ID'
end
# Decode token payload and verify it's signature.
payload, = JWT.decode(
token,
OpenSSL::PKey::RSA.new(fetch_rsa_key(k_id, app_id)),
true,
algorithm: 'RS256',
verify_expiration: true,
verify_aud: true,
aud: app_id,
verify_iss: true,
iss: app_config[:iss],
nbf_leeway: LEEWAY
)
HashWithIndifferentAccess.new(payload)
end
end
end