firebase-id-token 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/firebase-id-token.rb +170 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d89290464d362ff7e59e9058263abefe82915661
|
4
|
+
data.tar.gz: 0fc67807f0f9d7538a64252594cd8476702aba35
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50ebce4492b3eae49194b537839fa4776a28a81422b95c4a9f501cec52285995d683213f2051d99f0fbf1e39dbfb4bc140bab72d59be1715da5b9ba59b18874e
|
7
|
+
data.tar.gz: 3986fed0aec5ccae0cf2b0b483c2424a87f702d8294d2261b63165001a9a60974b508899ec43921917b77b8e0aa5295097b465b6c8f32cf62567439e5caaa837
|
@@ -0,0 +1,170 @@
|
|
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 (once per day by default)
|
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 FirebaseIDToken
|
31
|
+
class CertificateError < StandardError; end
|
32
|
+
class ValidationError < StandardError; end
|
33
|
+
class ExpiredTokenError < ValidationError; end
|
34
|
+
class SignatureError < ValidationError; end
|
35
|
+
class InvalidIssuerError < ValidationError; end
|
36
|
+
class AudienceMismatchError < ValidationError; end
|
37
|
+
class ClientIDMismatchError < ValidationError; end
|
38
|
+
|
39
|
+
class Validator
|
40
|
+
|
41
|
+
FIREBASE_CERTS_URI = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'
|
42
|
+
FIREBASE_CERTS_EXPIRY = 86400 # 1 day
|
43
|
+
|
44
|
+
FIREBASE_ISSUERS_PREFIX = 'https://securetoken.google.com/'
|
45
|
+
|
46
|
+
def initialize(keyopts = {})
|
47
|
+
if keyopts[:x509_cert]
|
48
|
+
@certs_mode = :literal
|
49
|
+
@certs = { :_ => keyopts[:x509_cert] }
|
50
|
+
# elsif keyopts[:jwk_uri] # TODO
|
51
|
+
# @certs_mode = :jwk
|
52
|
+
# @certs = {}
|
53
|
+
else
|
54
|
+
@certs_mode = :old_skool
|
55
|
+
@certs = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
@certs_expiry = keyopts.fetch(:expiry, FIREBASE_CERTS_EXPIRY)
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# If it validates, returns a hash with the JWT payload from the ID Token.
|
63
|
+
# You have to provide an "aud" value, which must match the
|
64
|
+
# token's field with that name.
|
65
|
+
# Furthermore the tokens field "iss" must be
|
66
|
+
# "https://securetoken.google.com/<aud>"
|
67
|
+
#
|
68
|
+
# If something fails, raises an error
|
69
|
+
#
|
70
|
+
# @param [String] token
|
71
|
+
# The string form of the token
|
72
|
+
# @param [String] aud
|
73
|
+
# The required audience value
|
74
|
+
#
|
75
|
+
# @return [Hash] The decoded ID token
|
76
|
+
def check(token, aud)
|
77
|
+
payload = check_cached_certs(token, aud)
|
78
|
+
|
79
|
+
unless payload
|
80
|
+
# no certs worked, might've expired, refresh
|
81
|
+
if refresh_certs
|
82
|
+
payload = check_cached_certs(token, aud)
|
83
|
+
|
84
|
+
unless payload
|
85
|
+
raise SignatureError, 'Token not verified as issued by Firebase'
|
86
|
+
end
|
87
|
+
else
|
88
|
+
raise CertificateError, 'Unable to retrieve Firebase public keys'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
payload
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# tries to validate the token against each cached cert.
|
98
|
+
# Returns the token payload or raises a ValidationError or
|
99
|
+
# nil, which means none of the certs validated.
|
100
|
+
def check_cached_certs(token, aud)
|
101
|
+
payload = nil
|
102
|
+
|
103
|
+
# find first public key that validates this token
|
104
|
+
@certs.detect do |key, cert|
|
105
|
+
begin
|
106
|
+
public_key = cert.public_key
|
107
|
+
decoded_token = JWT.decode(token, public_key, true, { :algorithm => 'RS256' })
|
108
|
+
payload = decoded_token.first
|
109
|
+
|
110
|
+
payload
|
111
|
+
rescue JWT::ExpiredSignature
|
112
|
+
raise ExpiredTokenError, 'Token signature is expired'
|
113
|
+
rescue JWT::DecodeError => e
|
114
|
+
nil # go on, try the next cert
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if payload
|
119
|
+
if !(payload.has_key?('aud') && payload['aud'] == aud)
|
120
|
+
raise AudienceMismatchError, 'Token audience mismatch'
|
121
|
+
end
|
122
|
+
if FIREBASE_ISSUERS_PREFIX + aud != payload['iss']
|
123
|
+
raise InvalidIssuerError, 'Token issuer mismatch'
|
124
|
+
end
|
125
|
+
payload
|
126
|
+
else
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# returns false if there was a problem
|
132
|
+
def refresh_certs
|
133
|
+
case @certs_mode
|
134
|
+
when :literal
|
135
|
+
true # no-op
|
136
|
+
when :old_skool
|
137
|
+
old_skool_refresh_certs
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def old_skool_refresh_certs
|
142
|
+
return true unless certs_cache_expired?
|
143
|
+
|
144
|
+
uri = URI(FIREBASE_CERTS_URI)
|
145
|
+
get = Net::HTTP::Get.new uri.request_uri
|
146
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
147
|
+
http.use_ssl = true
|
148
|
+
res = http.request(get)
|
149
|
+
|
150
|
+
if res.is_a?(Net::HTTPSuccess)
|
151
|
+
new_certs = Hash[MultiJson.load(res.body).map do |key, cert|
|
152
|
+
[key, OpenSSL::X509::Certificate.new(cert)]
|
153
|
+
end]
|
154
|
+
@certs.merge! new_certs
|
155
|
+
@certs_last_refresh = Time.now
|
156
|
+
true
|
157
|
+
else
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def certs_cache_expired?
|
163
|
+
if defined? @certs_last_refresh
|
164
|
+
Time.now > @certs_last_refresh + @certs_expiry
|
165
|
+
else
|
166
|
+
true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: firebase-id-token
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Arnreich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: multi_json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: fakeweb
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: openssl
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Firebase ID Token utilities; currently just a parser/checker
|
98
|
+
email: daniel@arnreich.de
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- lib/firebase-id-token.rb
|
104
|
+
homepage: https://github.com/darnreich/firebase-id-token
|
105
|
+
licenses:
|
106
|
+
- APACHE-2.0
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.5.1
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Firebase ID Token utilities
|
128
|
+
test_files: []
|