class GoogleIDToken::Validator
Constants
- GOOGLE_CERTS_EXPIRY
- GOOGLE_CERTS_URI
- GOOGLE_ISSUERS
Public Class Methods
new(options = {})
click to toggle source
Calls superclass method
# File lib/google-id-token.rb, line 50 def initialize(options = {}) super() if options[:x509_cert] @certs_mode = :literal @certs = { :_ => options[:x509_cert] } # elsif options[:jwk_uri] # TODO # @certs_mode = :jwk # @certs = {} else @certs_mode = :old_skool @certs = {} end @certs_expiry = options.fetch(:expiry, GOOGLE_CERTS_EXPIRY) end
Public Instance Methods
check(token, aud, cid = nil)
click to toggle source
If it validates, returns a hash with the JWT payload from the ID Token.
You have to provide an "aud" value, which must match the token's field with that name, and will similarly check cid if provided.
If something fails, raises an error
@param [String] token
The string form of the token
@param [String] aud
The required audience value
@param [String] cid
The optional client-id ("azp" field) value
@return [Hash] The decoded ID token
# File lib/google-id-token.rb, line 82 def check(token, aud, cid = nil) synchronize do payload = check_cached_certs(token, aud, cid) unless payload # no certs worked, might've expired, refresh if refresh_certs payload = check_cached_certs(token, aud, cid) unless payload raise SignatureError, 'Token not verified as issued by Google' end else raise CertificateError, 'Unable to retrieve Google public keys' end end payload end end
Private Instance Methods
certs_cache_expired?()
click to toggle source
# File lib/google-id-token.rb, line 183 def certs_cache_expired? if defined? @certs_last_refresh Time.now > @certs_last_refresh + @certs_expiry else true end end
check_cached_certs(token, aud, cid)
click to toggle source
tries to validate the token against each cached cert. Returns the token payload or raises a ValidationError
or
nil, which means none of the certs validated.
# File lib/google-id-token.rb, line 108 def check_cached_certs(token, aud, cid) payload = nil # find first public key that validates this token @certs.detect do |key, cert| begin public_key = cert.public_key decoded_token = JWT.decode(token, public_key, !!public_key, { :algorithm => 'RS256' }) payload = decoded_token.first # in Feb 2013, the 'cid' claim became the 'azp' claim per changes # in the OIDC draft. At some future point we can go all-azp, but # this should keep everything running for a while if payload['azp'] payload['cid'] = payload['azp'] elsif payload['cid'] payload['azp'] = payload['cid'] end payload rescue JWT::ExpiredSignature raise ExpiredTokenError, 'Token signature is expired' rescue JWT::DecodeError => e nil # go on, try the next cert end end if payload if !(payload.has_key?('aud') && payload['aud'] == aud) raise AudienceMismatchError, 'Token audience mismatch' end if cid && payload['cid'] != cid raise ClientIDMismatchError, 'Token client-id mismatch' end if !GOOGLE_ISSUERS.include?(payload['iss']) raise InvalidIssuerError, 'Token issuer mismatch' end payload else nil end end
old_skool_refresh_certs()
click to toggle source
# File lib/google-id-token.rb, line 162 def old_skool_refresh_certs return true unless certs_cache_expired? uri = URI(GOOGLE_CERTS_URI) get = Net::HTTP::Get.new uri.request_uri http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true res = http.request(get) if res.is_a?(Net::HTTPSuccess) new_certs = Hash[JSON.load(res.body).map do |key, cert| [key, OpenSSL::X509::Certificate.new(cert)] end] @certs.merge! new_certs @certs_last_refresh = Time.now true else false end end
refresh_certs()
click to toggle source
returns false if there was a problem
# File lib/google-id-token.rb, line 151 def refresh_certs case @certs_mode when :literal true # no-op when :old_skool old_skool_refresh_certs # when :jwk # TODO # jwk_refresh_certs end end