google-id-token 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.rdoc +17 -0
  2. data/lib/google-id-token.rb +132 -0
  3. metadata +128 -0
@@ -0,0 +1,17 @@
1
+ = GoogleIDToken
2
+
3
+ GoogleIDToken currently provides a single useful class "Validator", which provides a single method "#check", which parses and validates an ID Token allegedly generated by Google auth servers.
4
+
5
+ === Installation
6
+
7
+ gem install google-id-token
8
+
9
+ === Examples
10
+
11
+ validator = GoogleIDToken::Validator.new
12
+ jwt = validator.validate(token, required_audience, required_client_id)
13
+ if jwt
14
+ email = jwt['email']
15
+ else
16
+ report "Cannot validate: #{validator.problem}"
17
+ end
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+ # Copyright 2012 Google Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ ##
17
+ # Validates strings alleged to be ID Tokens issued by Google; if validation
18
+ # succeeds, returns the decoded ID Token as a hash.
19
+ # It's a good idea to keep an instance of this class around for a long time,
20
+ # because it caches the keys, performs validation statically, and only
21
+ # refreshes from Google when required (typically once per day)
22
+ #
23
+ # @author Tim Bray, adapted from code by Bob Aman
24
+
25
+ require 'multi_json'
26
+ require 'jwt'
27
+ require 'openssl'
28
+ require 'net/http'
29
+
30
+ module GoogleIDToken
31
+ class Validator
32
+
33
+ GOOGLE_CERTS_URI = 'https://www.googleapis.com/oauth2/v1/certs'
34
+
35
+ # @!attribute [r] problem
36
+ # Reason for failure, if #check returns nil
37
+ attr_reader :problem
38
+
39
+ def initialize
40
+ @certs = {}
41
+ end
42
+
43
+ ##
44
+ # If it validates, returns a hash with the JWT fields from the ID Token.
45
+ # You have to provide an "aud" value, which must match the
46
+ # token's field with that name, and will similarly check cid if provided.
47
+ #
48
+ # If something fails, returns nil; #problem returns error text
49
+ #
50
+ # @param [String] token
51
+ # The string form of the token
52
+ # @param [String] aud
53
+ # The required audience value
54
+ # @param [String] cid
55
+ # The optional client-id value
56
+ #
57
+ # @return [Hash] The decoded ID token, or null
58
+ def check(token, aud, cid = nil)
59
+ case check_cached_certs(token, aud, cid)
60
+ when :valid
61
+ @token
62
+ when :problem
63
+ nil
64
+ else
65
+ # no certs worked, might've expired, refresh
66
+ if refresh_certs
67
+ @problem = 'Unable to retrieve Google public keys'
68
+ nil
69
+ else
70
+ case check_cached_certs(token, aud, cid)
71
+ when :valid
72
+ @token
73
+ when :problem
74
+ nil
75
+ else
76
+ @problem = 'Token not verified as issued by Google'
77
+ nil
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # tries to validate the token against each cached cert.
86
+ # Returns :valid (sets @token) or :problem (sets @problem) or
87
+ # nil, which means none of the certs validated.
88
+ def check_cached_certs(token, aud, cid)
89
+ @problem = @token = nil
90
+
91
+ # find first public key that validates this token
92
+ @certs.detect do |key, cert|
93
+ begin
94
+ public_key = cert.public_key
95
+ @token = JWT.decode(token, public_key, !!public_key)
96
+ rescue JWT::DecodeError
97
+ nil # go on, try the next cert
98
+ end
99
+ end
100
+
101
+ if @token
102
+ if !(@token.has_key?('aud') && (@token['aud'] == aud))
103
+ @problem = 'Token audience mismatch'
104
+ elsif cid && !(@token.has_key?('cid') && (@token['cid'] == cid))
105
+ @problem = 'Token client-id mismatch'
106
+ end
107
+ @problem ? :problem : :valid
108
+ else
109
+ nil
110
+ end
111
+ end
112
+
113
+ # returns true if there was a problem
114
+ def refresh_certs
115
+ uri = URI(GOOGLE_CERTS_URI)
116
+ get = Net::HTTP::Get.new uri.request_uri
117
+ res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => true) do |http|
118
+ http.request(get)
119
+ end
120
+
121
+ if res.kind_of?(Net::HTTPSuccess)
122
+ new_certs = Hash[MultiJson.load(res.body).map do |key, cert|
123
+ [key, OpenSSL::X509::Certificate.new(cert)]
124
+ end]
125
+ @certs.merge! new_certs
126
+ false
127
+ else
128
+ true
129
+ end
130
+ end
131
+ end
132
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google-id-token
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Bray
9
+ - Bob Aman
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-12-06 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: multi_json
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: jwt
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: fakeweb
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rake
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ - !ruby/object:Gem::Dependency
80
+ name: rspec
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ description: Google ID Token utilities; currently just a parser/checker
96
+ email: tbray@textuality.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - lib/google-id-token.rb
102
+ - README.rdoc
103
+ homepage: https://code.google.com/p/google-id-token/
104
+ licenses: []
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 1.8.21
124
+ signing_key:
125
+ specification_version: 3
126
+ summary: Google ID Token utilities
127
+ test_files: []
128
+ has_rdoc: