Class: Google::Auth::TokenValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/googleauth/token_validator.rb,
lib/googleauth/token_validator/version.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

GOOGLE_SIGNON_CERTS_URL =
"https://www.googleapis.com/oauth2/v1/certs".freeze
ISSUERS =
%w(accounts.google.com https://accounts.google.com).freeze
CLOCK_SKEW_SECONDS =

five minutes

300
MAX_TOKEN_LIFETIME_SECONDS =

one day

86400
VERSION =
"0.1.2"

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(jwt, client_id) ⇒ TokenValidator

Returns a new instance of TokenValidator.



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/googleauth/token_validator.rb', line 16

def initialize jwt, client_id
  segments = jwt.split(".")

  fail Error, "Wrong number of segments in token: #{jwt}" unless segments.size.eql? 3

  @client_id = client_id

  @signed = "#{segments[0]}.#{segments[1]}"
  @signature = segments[2]

  @envelope = JSON.parse Base64.decode64(segments[0])
  @payload = JSON.parse Base64.decode64(segments[1])
end

Class Attribute Details

._cached_certsObject

Returns the value of attribute _cached_certs.



78
79
80
# File 'lib/googleauth/token_validator.rb', line 78

def _cached_certs
  @_cached_certs
end

Instance Attribute Details

#client_idObject (readonly)

Returns the value of attribute client_id.



14
15
16
# File 'lib/googleauth/token_validator.rb', line 14

def client_id
  @client_id
end

#envelopeObject (readonly)

Returns the value of attribute envelope.



14
15
16
# File 'lib/googleauth/token_validator.rb', line 14

def envelope
  @envelope
end

#payloadObject (readonly)

Returns the value of attribute payload.



14
15
16
# File 'lib/googleauth/token_validator.rb', line 14

def payload
  @payload
end

#signatureObject (readonly)

Returns the value of attribute signature.



14
15
16
# File 'lib/googleauth/token_validator.rb', line 14

def signature
  @signature
end

#signedObject (readonly)

Returns the value of attribute signed.



14
15
16
# File 'lib/googleauth/token_validator.rb', line 14

def signed
  @signed
end

Class Method Details

._certsObject



80
81
82
83
84
85
# File 'lib/googleauth/token_validator.rb', line 80

def _certs
  @_cached_certs ||= begin
    uri = URI(GOOGLE_SIGNON_CERTS_URL)
    JSON.parse Net::HTTP.get(uri)
  end
end

Instance Method Details

#validate(max_expiry = MAX_TOKEN_LIFETIME_SECONDS) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/googleauth/token_validator.rb', line 30

def validate max_expiry = MAX_TOKEN_LIFETIME_SECONDS
  unless [@signed, @signature, @envelope, @payload].all?
    fail Error, "Validator was not properly initialized"
  end

  unless self.class._certs.keys.include? @envelope["kid"]
    fail Error, "No matching Google cert found for envelope: #{@envelope}"
  end

  pem = self.class._certs[envelope["kid"]]
  cert = OpenSSL::X509::Certificate.new pem
  digest = OpenSSL::Digest::SHA256.new

  verified = cert.public_key.verify(digest, Base64.urlsafe_decode64(@signature), @signed)

  fail Error, "Token signature invalid" unless verified

  fail Error, "No issue time in token" if @payload["iat"].to_s.empty?
  fail Error, "No expiration time in token" if @payload["exp"].to_s.empty?

  now      = Time.now.to_i
  earliest = @payload["iat"] - CLOCK_SKEW_SECONDS
  latest   = @payload["exp"] + CLOCK_SKEW_SECONDS

  fail Error, "Expiration time too far in future" if @payload["exp"] >= Time.now.to_i + max_expiry
  fail Error, "Token used too early" if now < earliest
  fail Error, "Token used too late" if now > latest

  unless ISSUERS.include? @payload["iss"]
    fail Error, "Invalid issuer. Expected one of #{ISSUERS}, but got #{@payload["iss"]}"
  end

  if @client_id.is_a? Array
    aud_verified = @client_id.include?(@payload["aud"])
  elsif @client_id.is_a? String
    aud_verified = @payload["aud"] == @client_id
  else
    fail Error, "Invalid client id(s)"
  end

  unless aud_verified
    fail Error, "Wrong recipient - payload audience doesn't match required audience"
  end

  return true
end