mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-29 11:45:18 +08:00
60 lines
1.9 KiB
Ruby
60 lines
1.9 KiB
Ruby
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 = Rails.configuration.x.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 = Rails.configuration.x.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, header = 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), HashWithIndifferentAccess.new(header)]
|
|
end
|
|
end
|
|
end
|