firebase_token_auth 0.9.0 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +56 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +3 -0
- data/README.md +124 -8
- data/firebase_token_auth.gemspec +2 -2
- data/lib/firebase_token_auth/admin_client.rb +13 -0
- data/lib/firebase_token_auth/client.rb +8 -3
- data/lib/firebase_token_auth/configuration.rb +8 -3
- data/lib/firebase_token_auth/exceptions.rb +104 -0
- data/lib/firebase_token_auth/public_key_manager.rb +8 -1
- data/lib/firebase_token_auth/validator.rb +7 -7
- data/lib/firebase_token_auth/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70ba9577033bd9cea4a260988ef4f59d12bf8740316e1ac0f063d9b3b4c96814
|
4
|
+
data.tar.gz: 15f4220908cf05164357bc438494a92b8730ebcb5ec6689a367b7dfcbdb638ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6e52a8dd7b81c06d140b9a7e8d350879e74fc21c6c261267a247ce4f80d233dd2b3ebbea4945d9bd1f0d5450a6ae2ce819dc4a1d325c976290c48f8d38ec4e5
|
7
|
+
data.tar.gz: 6812e67073338a0a5af317b83255a64916023054ce1e9638dd0ade1dd997353df4b2b3aba51ea19e6367e9b428c5d94ffb953384359ed01e8de93cac567670f3
|
@@ -0,0 +1,56 @@
|
|
1
|
+
name: rspec
|
2
|
+
|
3
|
+
on: push
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
rspec:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [ 2.4, 2.5, 2.6, 2.7, "3.0" ]
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
|
19
|
+
- name: Configure Bundler
|
20
|
+
run: |
|
21
|
+
gem update --system
|
22
|
+
gem --version
|
23
|
+
gem install -N bundler -v 2
|
24
|
+
|
25
|
+
- uses: actions/cache@v1
|
26
|
+
with:
|
27
|
+
path: vendor/bundle
|
28
|
+
key: ${{ runner.os }}-gem-${{ hashFiles(format('{0}{1}', github.workspace, '/Gemfile.lock')) }}
|
29
|
+
restore-keys: ${{ runner.os }}-gem-
|
30
|
+
|
31
|
+
- name: Install ruby dependencies
|
32
|
+
run: |
|
33
|
+
bundle check || bundle install --jobs 4 --retry 3 --path vendor/bundle
|
34
|
+
|
35
|
+
- name: rspec
|
36
|
+
run: |
|
37
|
+
bundle exec rspec
|
38
|
+
env:
|
39
|
+
TEST_UID: ${{ secrets.TEST_UID }}
|
40
|
+
TEST_PROJECT_ID: ${{ secrets.TEST_PROJECT_ID }}
|
41
|
+
TEST_WEB_API_KEY: ${{ secrets.TEST_WEB_API_KEY }}
|
42
|
+
TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
|
43
|
+
GOOGLE_ACCOUNT_TYPE: ${{ secrets.GOOGLE_ACCOUNT_TYPE }}
|
44
|
+
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
|
45
|
+
GOOGLE_CLIENT_EMAIL: ${{ secrets.GOOGLE_CLIENT_EMAIL }}
|
46
|
+
GOOGLE_PRIVATE_KEY: ${{ secrets.GOOGLE_PRIVATE_KEY }}
|
47
|
+
|
48
|
+
- name: slack notification
|
49
|
+
uses: 8398a7/action-slack@v2
|
50
|
+
if: always()
|
51
|
+
with:
|
52
|
+
status: ${{ job.status }}
|
53
|
+
author_name: 'github action firebase_token_auth build'
|
54
|
+
env:
|
55
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
56
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# FirebaseTokenAuth
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
FirebaseTokenAuth is an Firebase Auth Client. It supports below.
|
4
|
+
- verify id_token method
|
5
|
+
- create custom token
|
6
|
+
- fetch user info by uid/email
|
7
|
+
- update user info
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -22,17 +24,131 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
on Rails, config/initializers/firebase_token_auth.rb
|
28
|
+
```ruby
|
29
|
+
FirebaseTokenAuth.configure do |config|
|
30
|
+
## for id_token_verify
|
31
|
+
# firebase web console => project settings => general => project ID
|
32
|
+
config.project_id = "your_project_id" # required
|
33
|
+
|
34
|
+
# firebase web console => project settings => service account => firebase admin sdk => generate new private key
|
35
|
+
# pass string of path to credential file to config.json_key_io
|
36
|
+
config.json_key_io = "#{Rails.root}/path/to/service_account_credentials.json"
|
37
|
+
# Or content of json key file wrapped with StringIO
|
38
|
+
# config.json_key_io = StringIO.new('{ ... }')
|
39
|
+
|
40
|
+
# Or set environment variables
|
41
|
+
# ENV['GOOGLE_ACCOUNT_TYPE'] = 'service_account'
|
42
|
+
# ENV['GOOGLE_CLIENT_ID'] = '000000000000000000000'
|
43
|
+
# ENV['GOOGLE_CLIENT_EMAIL'] = 'xxxx@xxxx.iam.gserviceaccount.com'
|
44
|
+
# ENV['GOOGLE_PRIVATE_KEY'] = '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n\'
|
45
|
+
end
|
46
|
+
```
|
47
|
+
for more detail. see [here](https://github.com/googleapis/google-auth-library-ruby#example-service-account).
|
48
|
+
|
49
|
+
### token verify
|
50
|
+
```ruby
|
51
|
+
require 'firebase_token_auth'
|
52
|
+
|
53
|
+
FirebaseTokenAuth.configure do |config|
|
54
|
+
config.project_id = 'your_project_id'
|
55
|
+
end
|
56
|
+
|
57
|
+
client = FirebaseTokenAuth.build
|
58
|
+
result = client.verify_id_token(id_token)
|
59
|
+
|
60
|
+
puts result.uid
|
61
|
+
# => "hMPHt8RyDpOsHi1oH5XaVirSYyq2"
|
62
|
+
|
63
|
+
puts result.id_token.payload # you can see decoded id_token payload
|
64
|
+
# => {"iss"=>"https://securetoken.google.com/<your_project_id>",
|
65
|
+
# "aud"=>"<your_project_id>",
|
66
|
+
# "auth_time"=>1594494935,
|
67
|
+
# "user_id"=>"hMPHt8RyDpOsHi1oH5XaVirSYyq2",
|
68
|
+
# "sub"=>"hMPHt8RyDpOsHi1oH5XaVirSYyq2",
|
69
|
+
# "iat"=>1594494935,
|
70
|
+
# "exp"=>1594498535,
|
71
|
+
# "email"=>"<your_user_email>",
|
72
|
+
# "email_verified"=>false,
|
73
|
+
# "firebase"=>{"identities"=>{"email"=>["<your_user_email>"]}, "sign_in_provider"=>"custom"}}
|
74
|
+
|
75
|
+
puts result.id_token.header
|
76
|
+
# => {"alg"=>"RS256", "kid"=>"7623e10a045140f1cfd4be0466cf80352b59f81e", "typ"=>"JWT"}
|
77
|
+
```
|
78
|
+
|
79
|
+
### custom token create
|
80
|
+
```ruby
|
81
|
+
require 'firebase_token_auth'
|
26
82
|
|
27
|
-
|
83
|
+
FirebaseTokenAuth.configure do |config|
|
84
|
+
config.project_id = 'your_project_id'
|
85
|
+
config.json_key_io = "#{Rails.root}/path/to/service_account_credentials.json"
|
86
|
+
end
|
28
87
|
|
29
|
-
|
88
|
+
client = FirebaseTokenAuth.new
|
89
|
+
c_token = client.create_custom_token(test_uid)
|
90
|
+
puts c_token
|
91
|
+
# => "eyJhbGciOXXX.eyJpc3MiOiJmaXJlYmFzXXXX.v7y7LoBXXXXX" # dummy
|
92
|
+
```
|
30
93
|
|
31
|
-
|
94
|
+
### fetch users info from firebase
|
95
|
+
```ruby
|
96
|
+
require 'firebase_token_auth'
|
97
|
+
|
98
|
+
FirebaseTokenAuth.configure do |config|
|
99
|
+
config.project_id = 'your_project_id'
|
100
|
+
config.json_key_io = "#{Rails.root}/path/to/service_account_credentials.json"
|
101
|
+
end
|
102
|
+
|
103
|
+
client = FirebaseTokenAuth.new
|
104
|
+
result = client.user_search_by_email(test_user_email)
|
105
|
+
# result = client.user_search_by_uid(test_uid)
|
106
|
+
puts result
|
107
|
+
# => [{:created_at=>1594132097140,
|
108
|
+
# :custom_auth=>true,
|
109
|
+
# :disabled=>false,
|
110
|
+
# :email=>"<your_user_email>",
|
111
|
+
# :email_verified=>false,
|
112
|
+
# :last_login_at=>1594495792373,
|
113
|
+
# :local_id=>"hMPHt8RyDpOsHi1oH5XaVirSYyq2",
|
114
|
+
# :password_hash=>"REDACTED",
|
115
|
+
# :password_updated_at=>1594132097140,
|
116
|
+
# :provider_user_info=>
|
117
|
+
# [{:email=>"<your_user_email>",
|
118
|
+
# :federated_id=>"<your_user_email>",
|
119
|
+
# :provider_id=>"password",
|
120
|
+
# :raw_id=>"<your_user_email>"}],
|
121
|
+
# :valid_since=>1594132097}]
|
122
|
+
```
|
123
|
+
|
124
|
+
### update user info
|
125
|
+
```ruby
|
126
|
+
require 'firebase_token_auth'
|
127
|
+
|
128
|
+
FirebaseTokenAuth.configure do |config|
|
129
|
+
config.project_id = 'your_project_id'
|
130
|
+
config.json_key_io = "#{Rails.root}/path/to/service_account_credentials.json"
|
131
|
+
end
|
132
|
+
|
133
|
+
client = FirebaseTokenAuth.new
|
134
|
+
# NOTE: parameter_name is snake_case
|
135
|
+
update_params = { # ref. https://firebase.google.com/docs/reference/rest/auth#section-update-profile
|
136
|
+
display_name: 'updated_name',
|
137
|
+
}
|
138
|
+
result = client.update_user(test_uid, update_params)
|
139
|
+
puts result
|
140
|
+
# => {:display_name=>"updated_name",
|
141
|
+
# :email=>"<your_user_email>",
|
142
|
+
# :email_verified=>false,
|
143
|
+
# :kind=>"identitytoolkit#SetAccountInfoResponse",
|
144
|
+
# :local_id=>"hMPHt8RyDpOsHi1oH5XaVirSYyq2",
|
145
|
+
# :password_hash=>"REDACTED",
|
146
|
+
# :provider_user_info=>[{:display_name=>"updated_name", :federated_id=>"<your_user_email>", :provider_id=>"password"}]}
|
147
|
+
```
|
32
148
|
|
33
149
|
## Contributing
|
34
150
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
151
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/miyataka/firebase_token_auth.
|
36
152
|
|
37
153
|
|
38
154
|
## License
|
data/firebase_token_auth.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = 'Firebase Authentication API wrapper for serverside. It support custom token auth. Of course it has id_token verify feature.'
|
11
11
|
spec.homepage = 'https://github.com/miyataka/firebase_token_auth'
|
12
12
|
spec.license = 'MIT'
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
14
14
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
16
16
|
spec.metadata['source_code_uri'] = spec.homepage
|
@@ -24,6 +24,6 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ['lib']
|
26
26
|
|
27
|
-
spec.add_dependency 'google-
|
27
|
+
spec.add_dependency 'google-apis-identitytoolkit_v3'
|
28
28
|
spec.add_dependency 'jwt'
|
29
29
|
end
|
@@ -13,5 +13,18 @@ module FirebaseTokenAuth
|
|
13
13
|
request = Google::Apis::IdentitytoolkitV3::GetAccountInfoRequest.new(**params)
|
14
14
|
service.get_account_info(request)
|
15
15
|
end
|
16
|
+
|
17
|
+
def update_existing_account(uid, attributes)
|
18
|
+
update_params = { local_id: uid }.merge!(permit_attributes(attributes))
|
19
|
+
request = Google::Apis::IdentitytoolkitV3::SetAccountInfoRequest.new(**update_params)
|
20
|
+
service.set_account_info(request)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def permit_attributes(attr_hash)
|
26
|
+
permit_keys = %i[disabled display_name email email_verified password phone_number photo_url multi_factor]
|
27
|
+
attr_hash.select { |k, _v| permit_keys.include?(k) }
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
@@ -5,6 +5,7 @@ require 'jwt'
|
|
5
5
|
require 'firebase_token_auth/public_key_manager'
|
6
6
|
require 'firebase_token_auth/validator'
|
7
7
|
require 'firebase_token_auth/admin_client'
|
8
|
+
require 'firebase_token_auth/exceptions'
|
8
9
|
|
9
10
|
module FirebaseTokenAuth
|
10
11
|
ALGORITHM = 'RS256'.freeze
|
@@ -25,19 +26,19 @@ module FirebaseTokenAuth
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def verify_id_token(id_token, options = {})
|
28
|
-
raise if id_token.nil? || id_token.empty?
|
29
|
+
raise ArgumentError, 'Firebase ID token must not null or blank strings.' if id_token.nil? || id_token.empty?
|
29
30
|
|
30
31
|
public_key_id, decoded_jwt = validator.extract_kid(id_token)
|
31
32
|
public_key_manager.refresh_publickeys!
|
32
33
|
validator.validate(configuration.project_id, decoded_jwt)
|
33
34
|
default_options = { algorithm: ALGORITHM, verify_iat: true, verify_expiration: true, exp_leeway: configuration.exp_leeway }
|
35
|
+
raise ValidationError, 'Public key may have expired.' unless public_key_manager.public_keys.include?(public_key_id)
|
34
36
|
jwt = JWT.decode(id_token, public_key_manager.public_keys[public_key_id].public_key, true, default_options.merge!(options))
|
35
37
|
IdTokenResult.new(jwt[0]['sub'], IdToken.new(jwt[0], jwt[1]))
|
36
38
|
end
|
37
39
|
|
38
40
|
def create_custom_token(uid, additional_claims = nil)
|
39
|
-
|
40
|
-
raise unless configuration.configured_for_custom_token?
|
41
|
+
raise ConfigurationError, 'To create custom token, You must configure credentials via json or environmental variables.' unless configuration.configured_for_custom_token?
|
41
42
|
|
42
43
|
now_seconds = Time.now.to_i
|
43
44
|
payload = { iss: configuration.client_email,
|
@@ -58,6 +59,10 @@ module FirebaseTokenAuth
|
|
58
59
|
admin_client.get_account_info({ local_id: [uid] })&.users&.map(&:to_h)
|
59
60
|
end
|
60
61
|
|
62
|
+
def update_user(uid, attribute_hash)
|
63
|
+
admin_client.update_existing_account(uid, attribute_hash).to_h
|
64
|
+
end
|
65
|
+
|
61
66
|
private
|
62
67
|
|
63
68
|
def admin_client
|
@@ -24,8 +24,7 @@ module FirebaseTokenAuth
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def prepare
|
27
|
-
|
28
|
-
raise unless project_id
|
27
|
+
raise ConfigurationError, 'project_id is required to use firebase_token_auth gem.' unless project_id
|
29
28
|
return unless configured_for_custom_token?
|
30
29
|
|
31
30
|
@auth = if json_key_io
|
@@ -45,7 +44,7 @@ module FirebaseTokenAuth
|
|
45
44
|
@private_key = OpenSSL::PKey::RSA.new(parsed['private_key'])
|
46
45
|
@client_email = parsed['client_email']
|
47
46
|
else
|
48
|
-
@private_key = OpenSSL::PKey::RSA.new(ENV['GOOGLE_PRIVATE_KEY'])
|
47
|
+
@private_key = OpenSSL::PKey::RSA.new(unescape(ENV['GOOGLE_PRIVATE_KEY']))
|
49
48
|
@client_email = ENV['GOOGLE_CLIENT_EMAIL']
|
50
49
|
end
|
51
50
|
end
|
@@ -53,5 +52,11 @@ module FirebaseTokenAuth
|
|
53
52
|
def configured_for_custom_token?
|
54
53
|
json_key_io || (ENV['GOOGLE_PRIVATE_KEY'] && ENV['GOOGLE_CLIENT_EMAIL'])
|
55
54
|
end
|
55
|
+
|
56
|
+
def unescape(str)
|
57
|
+
str = str.gsub('\n', "\n")
|
58
|
+
str = str[1..-2] if str.start_with?('"') && str.end_with?('"')
|
59
|
+
str
|
60
|
+
end
|
56
61
|
end
|
57
62
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module FirebaseTokenAuth
|
2
|
+
class APIError < StandardError; end
|
3
|
+
class NetworkError < APIError; end
|
4
|
+
|
5
|
+
class HttpError < APIError
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
def initialize(message, response)
|
9
|
+
super(message)
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ClientError < HttpError; end
|
15
|
+
|
16
|
+
class BadRequest < ClientError; end # status: 400
|
17
|
+
class Unauthorized < ClientError; end # status: 401
|
18
|
+
class PaymentRequired < ClientError; end # status: 402
|
19
|
+
class Forbidden < ClientError; end # status: 403
|
20
|
+
class NotFound < ClientError; end # status: 404
|
21
|
+
class MethodNotAllowed < ClientError; end # status: 405
|
22
|
+
class NotAcceptable < ClientError; end # status: 406
|
23
|
+
class ProxyAuthenticationRequired < ClientError; end # status: 407
|
24
|
+
class RequestTimeout < ClientError; end # status: 408
|
25
|
+
class Conflict < ClientError; end # status: 409
|
26
|
+
class Gone < ClientError; end # status: 410
|
27
|
+
class LengthRequired < ClientError; end # status: 411
|
28
|
+
class PreconditionFailed < ClientError; end # status: 412
|
29
|
+
class PayloadTooLarge < ClientError; end # status: 413
|
30
|
+
class URITooLong < ClientError; end # status: 414
|
31
|
+
class UnsupportedMediaType < ClientError; end # status: 415
|
32
|
+
class RangeNotSatisfiable < ClientError; end # status: 416
|
33
|
+
class ExpectationFailed < ClientError; end # status: 417
|
34
|
+
class ImaTeapot < ClientError; end # status: 418
|
35
|
+
class MisdirectedRequest < ClientError; end # status: 421
|
36
|
+
class UnprocessableEntity < ClientError; end # status: 422
|
37
|
+
class Locked < ClientError; end # status: 423
|
38
|
+
class FailedDependency < ClientError; end # status: 424
|
39
|
+
class UpgradeRequired < ClientError; end # status: 426
|
40
|
+
class PreconditionRequired < ClientError; end # status: 428
|
41
|
+
class TooManyRequests < ClientError; end # status: 429
|
42
|
+
class RequestHeaderFieldsTooLarge < ClientError; end # status: 431
|
43
|
+
class UnavailableForLegalReasons < ClientError; end # status: 451
|
44
|
+
|
45
|
+
class ServerError < HttpError; end
|
46
|
+
|
47
|
+
class InternalServerError < ServerError; end # status: 500
|
48
|
+
class NotImplemented < ServerError; end # status: 501
|
49
|
+
class BadGateway < ServerError; end # status: 502
|
50
|
+
class ServiceUnavailable < ServerError; end # status: 503
|
51
|
+
class GatewayTimeout < ServerError; end # status: 504
|
52
|
+
class HTTPVersionNotSupported < ServerError; end # status: 505
|
53
|
+
class VariantAlsoNegotiates < ServerError; end # status: 506
|
54
|
+
class InsufficientStorage < ServerError; end # status: 507
|
55
|
+
class LoopDetected < ServerError; end # status: 508
|
56
|
+
class NotExtended < ServerError; end # status: 510
|
57
|
+
class NetworkAuthenticationRequired < ServerError; end # status: 511
|
58
|
+
|
59
|
+
STATUS_TO_EXCEPTION_MAPPING = {
|
60
|
+
'400' => BadRequest,
|
61
|
+
'401' => Unauthorized,
|
62
|
+
'402' => PaymentRequired,
|
63
|
+
'403' => Forbidden,
|
64
|
+
'404' => NotFound,
|
65
|
+
'405' => MethodNotAllowed,
|
66
|
+
'406' => NotAcceptable,
|
67
|
+
'407' => ProxyAuthenticationRequired,
|
68
|
+
'408' => RequestTimeout,
|
69
|
+
'409' => Conflict,
|
70
|
+
'410' => Gone,
|
71
|
+
'411' => LengthRequired,
|
72
|
+
'412' => PreconditionFailed,
|
73
|
+
'413' => PayloadTooLarge,
|
74
|
+
'414' => URITooLong,
|
75
|
+
'415' => UnsupportedMediaType,
|
76
|
+
'416' => RangeNotSatisfiable,
|
77
|
+
'417' => ExpectationFailed,
|
78
|
+
'418' => ImaTeapot,
|
79
|
+
'421' => MisdirectedRequest,
|
80
|
+
'422' => UnprocessableEntity,
|
81
|
+
'423' => Locked,
|
82
|
+
'424' => FailedDependency,
|
83
|
+
'426' => UpgradeRequired,
|
84
|
+
'428' => PreconditionRequired,
|
85
|
+
'429' => TooManyRequests,
|
86
|
+
'431' => RequestHeaderFieldsTooLarge,
|
87
|
+
'451' => UnavailableForLegalReasons,
|
88
|
+
'500' => InternalServerError,
|
89
|
+
'501' => NotImplemented,
|
90
|
+
'502' => BadGateway,
|
91
|
+
'503' => ServiceUnavailable,
|
92
|
+
'504' => GatewayTimeout,
|
93
|
+
'505' => HTTPVersionNotSupported,
|
94
|
+
'506' => VariantAlsoNegotiates,
|
95
|
+
'507' => InsufficientStorage,
|
96
|
+
'508' => LoopDetected,
|
97
|
+
'510' => NotExtended,
|
98
|
+
'511' => NetworkAuthenticationRequired
|
99
|
+
}.freeze
|
100
|
+
|
101
|
+
class ValidationError < StandardError; end
|
102
|
+
class ConfigurationError < StandardError; end
|
103
|
+
class ArgumentError < StandardError; end
|
104
|
+
end
|
@@ -19,7 +19,7 @@ module FirebaseTokenAuth
|
|
19
19
|
private
|
20
20
|
|
21
21
|
def fetch_publickeys_hash
|
22
|
-
res = Net::HTTP.get_response(URI(PUBLIC_KEY_URL))
|
22
|
+
res = exception_handler(Net::HTTP.get_response(URI(PUBLIC_KEY_URL)))
|
23
23
|
@public_keys = JSON.parse(res.body).transform_values! { |v| OpenSSL::X509::Certificate.new(v) }
|
24
24
|
@expire_time = cache_control_header_to_expire_time(res['Cache-Control'])
|
25
25
|
end
|
@@ -31,5 +31,12 @@ module FirebaseTokenAuth
|
|
31
31
|
def cache_control_header_to_expire_time(cache_control_header)
|
32
32
|
Time.now.to_i + cache_control_header.match(/max-age=([0-9]*)/)[1].to_i
|
33
33
|
end
|
34
|
+
|
35
|
+
def exception_handler(response)
|
36
|
+
error = STATUS_TO_EXCEPTION_MAPPING[response.code]
|
37
|
+
raise error.new("Receieved an error response #{response.code} #{error.to_s.split('::').last}: #{response.body}", response) if error
|
38
|
+
|
39
|
+
response
|
40
|
+
end
|
34
41
|
end
|
35
42
|
end
|
@@ -7,13 +7,13 @@ module FirebaseTokenAuth
|
|
7
7
|
payload = decoded_jwt[0]
|
8
8
|
header = decoded_jwt[1]
|
9
9
|
issuer = ISSUER_BASE_URL + project_id
|
10
|
-
raise unless header['kid']
|
11
|
-
raise unless header['alg'] == ALGORITHM
|
12
|
-
raise unless payload['aud'] == project_id
|
13
|
-
raise unless payload['iss'] == issuer
|
14
|
-
raise unless payload['sub'].is_a?(String)
|
15
|
-
raise if payload['sub'].empty?
|
16
|
-
raise if payload['sub'].size > 128
|
10
|
+
raise ValidationError, 'Firebase ID token has no "kid" claim.' unless header['kid']
|
11
|
+
raise ValidationError, "Firebase ID token has incorrect algorithm. Expected \"#{ALGORITHM}\" but got \"#{header['alg']}\"." unless header['alg'] == ALGORITHM
|
12
|
+
raise ValidationError, "Firebase ID token has incorrect \"aud\" (audience) claim. Expected \"#{project_id}\" but got \"#{payload['aud']}\"." unless payload['aud'] == project_id
|
13
|
+
raise ValidationError, "Firebase ID token has \"iss\" (issuer) claim. Expected \"#{issuer}\" but got \"#{payload['iss']}\"." unless payload['iss'] == issuer
|
14
|
+
raise ValidationError, 'Firebase ID token has no "sub" (subject) claim.' unless payload['sub'].is_a?(String)
|
15
|
+
raise ValidationError, 'Firebase ID token has an empty string "sub" (subject) claim.' if payload['sub'].empty?
|
16
|
+
raise ValidationError, 'Firebase ID token has "sub" (subject) claim longer than 128 characters.' if payload['sub'].size > 128
|
17
17
|
end
|
18
18
|
|
19
19
|
def extract_kid(id_token)
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: firebase_token_auth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- miyataka
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: google-
|
14
|
+
name: google-apis-identitytoolkit_v3
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -46,6 +46,7 @@ executables: []
|
|
46
46
|
extensions: []
|
47
47
|
extra_rdoc_files: []
|
48
48
|
files:
|
49
|
+
- ".github/workflows/test.yml"
|
49
50
|
- ".gitignore"
|
50
51
|
- ".rspec"
|
51
52
|
- ".rubocop.yml"
|
@@ -61,6 +62,7 @@ files:
|
|
61
62
|
- lib/firebase_token_auth/admin_client.rb
|
62
63
|
- lib/firebase_token_auth/client.rb
|
63
64
|
- lib/firebase_token_auth/configuration.rb
|
65
|
+
- lib/firebase_token_auth/exceptions.rb
|
64
66
|
- lib/firebase_token_auth/public_key_manager.rb
|
65
67
|
- lib/firebase_token_auth/validator.rb
|
66
68
|
- lib/firebase_token_auth/version.rb
|
@@ -78,14 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
80
|
requirements:
|
79
81
|
- - ">="
|
80
82
|
- !ruby/object:Gem::Version
|
81
|
-
version: 2.
|
83
|
+
version: 2.4.0
|
82
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
85
|
requirements:
|
84
86
|
- - ">="
|
85
87
|
- !ruby/object:Gem::Version
|
86
88
|
version: '0'
|
87
89
|
requirements: []
|
88
|
-
rubygems_version: 3.1.
|
90
|
+
rubygems_version: 3.1.4
|
89
91
|
signing_key:
|
90
92
|
specification_version: 4
|
91
93
|
summary: Firebase Authentication API wrapper for serverside. It support custom token
|