firebase_token_auth 0.9.0 → 1.2.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 +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
|