Class: Google::Auth::TokenValidator

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

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
"0.1.0"
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

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.



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

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.



76
77
78
# File 'lib/googleauth/token_validator.rb', line 76

def _cached_certs
  @_cached_certs
end

Instance Attribute Details

#client_idObject (readonly)

Returns the value of attribute client_id.



12
13
14
# File 'lib/googleauth/token_validator.rb', line 12

def client_id
  @client_id
end

#envelopeObject (readonly)

Returns the value of attribute envelope.



12
13
14
# File 'lib/googleauth/token_validator.rb', line 12

def envelope
  @envelope
end

#payloadObject (readonly)

Returns the value of attribute payload.



12
13
14
# File 'lib/googleauth/token_validator.rb', line 12

def payload
  @payload
end

#signatureObject (readonly)

Returns the value of attribute signature.



12
13
14
# File 'lib/googleauth/token_validator.rb', line 12

def signature
  @signature
end

#signedObject (readonly)

Returns the value of attribute signed.



12
13
14
# File 'lib/googleauth/token_validator.rb', line 12

def signed
  @signed
end

Class Method Details

._certsObject



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

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



28
29
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
# File 'lib/googleauth/token_validator.rb', line 28

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