devise-fireauth 0.0.1.pre.alpha.1 → 0.1.0.rc1
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/.rubocop.yml +15 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +40 -5
- data/README.md +4 -2
- data/Rakefile +1 -1
- data/devise-fireauth.gemspec +13 -5
- data/lib/devise/fireauth.rb +13 -11
- data/lib/devise/fireauth/models/firebase_authenticatable.rb +11 -26
- data/lib/devise/fireauth/strategies/firebase_authenticatable.rb +32 -7
- data/lib/devise/fireauth/version.rb +1 -1
- data/lib/firebase_id_token.rb +138 -0
- metadata +122 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd558dba905e43488164024dcae0048e789b03c07284ac81dbbcef754b45b156
|
|
4
|
+
data.tar.gz: 9837813df4f5a0dd72e7c4cdcdda99df489b6f35ed162a8e7c84460f0022ed8e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3d688da001109f4ad9c3834222155a2336dbe508d155e037409985bda0f64a40a4570765b4c8564e75e4f42347f51bc8a3b3561582cc7e8d0aff732f113ccc5
|
|
7
|
+
data.tar.gz: a9d26c321cc8170c795bdcc46cc9df7bb3d3b93f0153291b550222a91dc14440dbb86040921ad681f95fcdde0f2805dcb48397965ea1b41767187004cbc5057a
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require: rubocop-rspec
|
|
2
|
+
inherit_gem:
|
|
3
|
+
rubocop-github:
|
|
4
|
+
- config/default.yml
|
|
5
|
+
- config/rails.yml
|
|
6
|
+
AllCops:
|
|
7
|
+
Exclude:
|
|
8
|
+
- gemfiles/**/*
|
|
9
|
+
- tmp/**/*
|
|
10
|
+
- vendor/**/*
|
|
11
|
+
- spec/dummy/**/*
|
|
12
|
+
GitHub/RailsControllerRenderLiteral:
|
|
13
|
+
Enabled: false
|
|
14
|
+
GitHub/RailsControllerRenderPathsExist:
|
|
15
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
devise-fireauth (0.
|
|
4
|
+
devise-fireauth (0.1.0.rc1)
|
|
5
5
|
activesupport (>= 4)
|
|
6
6
|
devise (~> 4.0)
|
|
7
7
|
dry-configurable
|
|
8
|
-
|
|
8
|
+
jwt (>= 1)
|
|
9
|
+
warden
|
|
9
10
|
|
|
10
11
|
GEM
|
|
11
12
|
remote: https://rubygems.org/
|
|
@@ -23,13 +24,17 @@ GEM
|
|
|
23
24
|
erubi (~> 1.4)
|
|
24
25
|
rails-dom-testing (~> 2.0)
|
|
25
26
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
27
|
+
activemodel (5.2.1)
|
|
28
|
+
activesupport (= 5.2.1)
|
|
26
29
|
activesupport (5.2.1)
|
|
27
30
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
28
31
|
i18n (>= 0.7, < 2)
|
|
29
32
|
minitest (~> 5.1)
|
|
30
33
|
tzinfo (~> 1.1)
|
|
34
|
+
ast (2.4.0)
|
|
31
35
|
bcrypt (3.1.12)
|
|
32
36
|
builder (3.2.3)
|
|
37
|
+
coderay (1.1.2)
|
|
33
38
|
concurrent-ruby (1.0.5)
|
|
34
39
|
crass (1.0.4)
|
|
35
40
|
devise (4.5.0)
|
|
@@ -42,20 +47,28 @@ GEM
|
|
|
42
47
|
dry-configurable (0.7.0)
|
|
43
48
|
concurrent-ruby (~> 1.0)
|
|
44
49
|
erubi (1.7.1)
|
|
45
|
-
|
|
46
|
-
multi_xml (>= 0.5.2)
|
|
50
|
+
fakeweb (1.3.0)
|
|
47
51
|
i18n (1.1.0)
|
|
48
52
|
concurrent-ruby (~> 1.0)
|
|
53
|
+
jaro_winkler (1.5.1)
|
|
54
|
+
jwt (2.1.0)
|
|
49
55
|
loofah (2.2.2)
|
|
50
56
|
crass (~> 1.0.2)
|
|
51
57
|
nokogiri (>= 1.5.9)
|
|
52
58
|
method_source (0.9.0)
|
|
53
59
|
mini_portile2 (2.3.0)
|
|
54
60
|
minitest (5.11.3)
|
|
55
|
-
multi_xml (0.6.0)
|
|
56
61
|
nokogiri (1.8.4)
|
|
57
62
|
mini_portile2 (~> 2.3.0)
|
|
63
|
+
openssl (2.1.1)
|
|
58
64
|
orm_adapter (0.5.0)
|
|
65
|
+
parallel (1.12.1)
|
|
66
|
+
parser (2.5.1.2)
|
|
67
|
+
ast (~> 2.4.0)
|
|
68
|
+
powerpack (0.1.2)
|
|
69
|
+
pry (0.11.3)
|
|
70
|
+
coderay (~> 1.1.0)
|
|
71
|
+
method_source (~> 0.9.0)
|
|
59
72
|
rack (2.0.5)
|
|
60
73
|
rack-test (1.1.0)
|
|
61
74
|
rack (>= 1.0, < 3)
|
|
@@ -70,6 +83,7 @@ GEM
|
|
|
70
83
|
method_source
|
|
71
84
|
rake (>= 0.8.7)
|
|
72
85
|
thor (>= 0.19.0, < 2.0)
|
|
86
|
+
rainbow (3.0.0)
|
|
73
87
|
rake (10.5.0)
|
|
74
88
|
responders (2.4.0)
|
|
75
89
|
actionpack (>= 4.2.0, < 5.3)
|
|
@@ -87,10 +101,24 @@ GEM
|
|
|
87
101
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
88
102
|
rspec-support (~> 3.8.0)
|
|
89
103
|
rspec-support (3.8.0)
|
|
104
|
+
rubocop (0.59.1)
|
|
105
|
+
jaro_winkler (~> 1.5.1)
|
|
106
|
+
parallel (~> 1.10)
|
|
107
|
+
parser (>= 2.5, != 2.5.1.1)
|
|
108
|
+
powerpack (~> 0.1)
|
|
109
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
110
|
+
ruby-progressbar (~> 1.7)
|
|
111
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
|
112
|
+
rubocop-github (0.12.0)
|
|
113
|
+
rubocop (~> 0.59)
|
|
114
|
+
rubocop-rspec (1.29.1)
|
|
115
|
+
rubocop (>= 0.58.0)
|
|
116
|
+
ruby-progressbar (1.10.0)
|
|
90
117
|
thor (0.20.0)
|
|
91
118
|
thread_safe (0.3.6)
|
|
92
119
|
tzinfo (1.2.5)
|
|
93
120
|
thread_safe (~> 0.1)
|
|
121
|
+
unicode-display_width (1.4.0)
|
|
94
122
|
warden (1.2.7)
|
|
95
123
|
rack (>= 1.0)
|
|
96
124
|
|
|
@@ -98,10 +126,17 @@ PLATFORMS
|
|
|
98
126
|
ruby
|
|
99
127
|
|
|
100
128
|
DEPENDENCIES
|
|
129
|
+
activemodel
|
|
101
130
|
bundler (~> 1.16)
|
|
102
131
|
devise-fireauth!
|
|
132
|
+
fakeweb
|
|
133
|
+
openssl
|
|
134
|
+
pry
|
|
135
|
+
railties (>= 4)
|
|
103
136
|
rake (~> 10.0)
|
|
104
137
|
rspec (~> 3.0)
|
|
138
|
+
rubocop-github
|
|
139
|
+
rubocop-rspec
|
|
105
140
|
|
|
106
141
|
BUNDLED WITH
|
|
107
142
|
1.16.4
|
data/README.md
CHANGED
|
@@ -32,6 +32,7 @@ Devise.setup do |config|
|
|
|
32
32
|
]
|
|
33
33
|
config.fireauth do |f|
|
|
34
34
|
f.api_key = "YoUR-weB-aPi-KEy"
|
|
35
|
+
f.project_id = "firebase-project-id"
|
|
35
36
|
f.token_key = :id_token
|
|
36
37
|
end
|
|
37
38
|
end
|
|
@@ -54,8 +55,9 @@ end
|
|
|
54
55
|
```
|
|
55
56
|
|
|
56
57
|
- Restart the server
|
|
57
|
-
- From now on, you can authenticate with the API via firebase `idToken`
|
|
58
|
-
-
|
|
58
|
+
- From now on, you can authenticate with the API via firebase `idToken` by one of:
|
|
59
|
+
- Add params `id_token` to URL query
|
|
60
|
+
- Attach the header `Authorization: Bearer #{id_token}` to the request
|
|
59
61
|
|
|
60
62
|
## Development
|
|
61
63
|
|
data/Rakefile
CHANGED
data/devise-fireauth.gemspec
CHANGED
|
@@ -15,19 +15,27 @@ Gem::Specification.new do |spec|
|
|
|
15
15
|
|
|
16
16
|
# Specify which files should be added to the gem when it is released.
|
|
17
17
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
18
|
-
spec.files = Dir.chdir(File.expand_path(
|
|
18
|
+
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
|
|
19
19
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
20
20
|
end
|
|
21
21
|
spec.bindir = "exe"
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
23
|
spec.require_paths = ["lib"]
|
|
24
24
|
|
|
25
|
-
spec.
|
|
26
|
-
spec.
|
|
27
|
-
spec.
|
|
28
|
-
spec.
|
|
25
|
+
spec.add_runtime_dependency "activesupport", ">= 4"
|
|
26
|
+
spec.add_runtime_dependency "devise", "~> 4.0"
|
|
27
|
+
spec.add_runtime_dependency "dry-configurable"
|
|
28
|
+
spec.add_runtime_dependency "jwt", ">= 1"
|
|
29
|
+
spec.add_runtime_dependency "warden"
|
|
29
30
|
|
|
31
|
+
spec.add_development_dependency "activemodel" # required by devise
|
|
30
32
|
spec.add_development_dependency "bundler", "~> 1.16"
|
|
33
|
+
spec.add_development_dependency "fakeweb"
|
|
34
|
+
spec.add_development_dependency "openssl"
|
|
35
|
+
spec.add_development_dependency "pry"
|
|
31
36
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
37
|
+
spec.add_development_dependency "railties", ">= 4"
|
|
32
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
|
39
|
+
spec.add_development_dependency "rubocop-github"
|
|
40
|
+
spec.add_development_dependency "rubocop-rspec"
|
|
33
41
|
end
|
data/lib/devise/fireauth.rb
CHANGED
|
@@ -2,25 +2,23 @@
|
|
|
2
2
|
require "devise"
|
|
3
3
|
require "dry-configurable"
|
|
4
4
|
require "devise/fireauth/version"
|
|
5
|
+
require_relative "../firebase_id_token"
|
|
5
6
|
|
|
6
7
|
module Devise
|
|
7
8
|
def self.fireauth
|
|
8
9
|
yield(Devise::Fireauth.config)
|
|
10
|
+
Devise::Fireauth.firebase_validator = FirebaseIDToken::Validator.new(aud: Devise::Fireauth.project_id)
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
warden do |manager|
|
|
12
|
-
manager.strategies.add(:firebase_authenticatable, Devise::Strategies::FirebaseAuthenticatable)
|
|
13
|
-
manager.default_strategies(scope: :user).unshift :firebase_authenticatable
|
|
14
|
-
end
|
|
15
|
-
add_module :firebase_authenticatable, controller: :sessions, route: { session: :routes }
|
|
16
|
-
|
|
17
13
|
module Fireauth
|
|
18
14
|
extend Dry::Configurable
|
|
19
15
|
|
|
20
16
|
setting :api_key, reader: true
|
|
21
17
|
setting :token_key, :id_token, reader: true
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
setting :project_id, reader: true
|
|
19
|
+
|
|
20
|
+
# Firebase Validator
|
|
21
|
+
mattr_accessor :firebase_validator
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
# Those modules must be loaded after Fireauth configuration done
|
|
@@ -34,9 +32,13 @@ module Devise
|
|
|
34
32
|
"devise/fireauth/strategies/firebase_authenticatable"
|
|
35
33
|
end
|
|
36
34
|
|
|
37
|
-
# TODO: verify the correct way to add strategies to warden
|
|
38
35
|
warden do |manager|
|
|
39
|
-
manager.strategies.add
|
|
36
|
+
manager.strategies.add :firebase_authenticatable,
|
|
37
|
+
Devise::Strategies::FirebaseAuthenticatable
|
|
38
|
+
mappings.keys.each do |scope|
|
|
39
|
+
manager.default_strategies(scope: scope).unshift :firebase_authenticatable
|
|
40
|
+
end
|
|
40
41
|
end
|
|
41
|
-
add_module :firebase_authenticatable,
|
|
42
|
+
add_module :firebase_authenticatable,
|
|
43
|
+
controller: :sessions, route: { session: :routes }
|
|
42
44
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "active_support/concern"
|
|
3
|
-
require "httparty"
|
|
4
3
|
|
|
5
4
|
module Devise
|
|
6
5
|
module Models
|
|
@@ -12,7 +11,6 @@ module Devise
|
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
module ClassMethods
|
|
15
|
-
FIREBASE_USER_INFO_URL = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo?key=#{Fireauth.api_key}"
|
|
16
14
|
####################################
|
|
17
15
|
# Overriden methods from Devise::Models::Authenticatable
|
|
18
16
|
####################################
|
|
@@ -42,6 +40,11 @@ module Devise
|
|
|
42
40
|
[record.id]
|
|
43
41
|
end
|
|
44
42
|
|
|
43
|
+
def from_firebase(auth_hash)
|
|
44
|
+
raise NotImplementedError,
|
|
45
|
+
"#{self.name} model must implement class method `from_firebase'"
|
|
46
|
+
end
|
|
47
|
+
|
|
45
48
|
#
|
|
46
49
|
# Here you do the request to the external webservice
|
|
47
50
|
#
|
|
@@ -50,38 +53,20 @@ module Devise
|
|
|
50
53
|
#
|
|
51
54
|
# If the authentication fails you should return false
|
|
52
55
|
#
|
|
53
|
-
def firebase_authentication(
|
|
54
|
-
auth_hash = firebase_verification(
|
|
56
|
+
def firebase_authentication(id_token)
|
|
57
|
+
auth_hash = firebase_verification(id_token)
|
|
55
58
|
return nil if auth_hash.empty?
|
|
56
|
-
unless defined? self.from_firebase
|
|
57
|
-
raise NotImplementedError,
|
|
58
|
-
"#{self.name} model must implement class method `from_firebase'"
|
|
59
|
-
end
|
|
60
59
|
# Create new user here and return user
|
|
61
60
|
self.from_firebase(auth_hash)
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
private
|
|
65
64
|
|
|
66
|
-
# TODO:
|
|
67
|
-
# Verify the correct token
|
|
68
|
-
# https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
|
|
69
65
|
def firebase_verification(id_token)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
body: {
|
|
76
|
-
'idToken': id_token
|
|
77
|
-
}.to_json
|
|
78
|
-
)
|
|
79
|
-
if firebase_verification_call.response.code == "200"
|
|
80
|
-
firebase_infos = firebase_verification_call.parsed_response
|
|
81
|
-
firebase_infos["users"][0]
|
|
82
|
-
else
|
|
83
|
-
{}
|
|
84
|
-
end
|
|
66
|
+
Fireauth.firebase_validator.check id_token
|
|
67
|
+
rescue => e
|
|
68
|
+
puts e.message
|
|
69
|
+
{}
|
|
85
70
|
end
|
|
86
71
|
end
|
|
87
72
|
end
|
|
@@ -3,23 +3,25 @@
|
|
|
3
3
|
module Devise
|
|
4
4
|
module Strategies
|
|
5
5
|
class FirebaseAuthenticatable < Authenticatable
|
|
6
|
+
def valid?
|
|
7
|
+
!token.nil?
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Don't use Session
|
|
11
|
+
def store?
|
|
12
|
+
false
|
|
13
|
+
end
|
|
6
14
|
#
|
|
7
15
|
# For an example check : https://github.com/plataformatec/devise/blob/master/lib/devise/strategies/database_authenticatable.rb
|
|
8
16
|
#
|
|
9
17
|
# Method called by warden to authenticate a resource.
|
|
10
18
|
#
|
|
11
19
|
def authenticate!
|
|
12
|
-
#
|
|
13
|
-
# authentication_hash doesn't include the password
|
|
14
|
-
#
|
|
15
|
-
auth_params = authentication_hash
|
|
16
|
-
return fail! unless auth_params[Fireauth.token_key]
|
|
17
|
-
|
|
18
20
|
#
|
|
19
21
|
# mapping.to is a wrapper over the resource model
|
|
20
22
|
#
|
|
21
23
|
# Treat the password as idToken
|
|
22
|
-
resource = mapping.to.firebase_authentication(
|
|
24
|
+
resource = mapping.to.firebase_authentication(token)
|
|
23
25
|
|
|
24
26
|
return fail! unless resource
|
|
25
27
|
|
|
@@ -35,6 +37,29 @@ module Devise
|
|
|
35
37
|
success!(resource)
|
|
36
38
|
end
|
|
37
39
|
end
|
|
40
|
+
|
|
41
|
+
def authenticate
|
|
42
|
+
resource = mapping.to.firebase_authentication(token)
|
|
43
|
+
# If authenticated, stop immediately - or continue
|
|
44
|
+
resource ? success!(resource) : fail(resource)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def token
|
|
50
|
+
token_from_headers || token_from_params
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def token_from_headers
|
|
54
|
+
authorization_header = env["HTTP_AUTHORIZATION"]
|
|
55
|
+
return unless authorization_header
|
|
56
|
+
type, token = authorization_header.split
|
|
57
|
+
type =~ /Bearer/i ? token : nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def token_from_params
|
|
61
|
+
params.dig Devise::Fireauth.token_key
|
|
62
|
+
end
|
|
38
63
|
end
|
|
39
64
|
end
|
|
40
65
|
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Original idea from https://github.com/google/google-id-token
|
|
3
|
+
require "json"
|
|
4
|
+
require "jwt"
|
|
5
|
+
require "monitor"
|
|
6
|
+
require "net/http"
|
|
7
|
+
require "openssl"
|
|
8
|
+
|
|
9
|
+
module FirebaseIDToken
|
|
10
|
+
class CertificateError < StandardError; end
|
|
11
|
+
class ValidationError < StandardError; end
|
|
12
|
+
class ExpiredTokenError < ValidationError; end
|
|
13
|
+
class SignatureError < ValidationError; end
|
|
14
|
+
class InvalidIssuerError < ValidationError; end
|
|
15
|
+
class AudienceMismatchError < ValidationError; end
|
|
16
|
+
class ClientIDMismatchError < ValidationError; end
|
|
17
|
+
|
|
18
|
+
# Verify the correct token
|
|
19
|
+
# https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
|
|
20
|
+
class Validator
|
|
21
|
+
include MonitorMixin
|
|
22
|
+
|
|
23
|
+
GOOGLE_CERTS_URI = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"
|
|
24
|
+
GOOGLE_ISSUER_PREFIX = "https://securetoken.google.com/" # Has a trailing `/'
|
|
25
|
+
GOOGLE_CERTS_EXPIRY = 3600 # 1 hour
|
|
26
|
+
|
|
27
|
+
def initialize(aud:, expiry: GOOGLE_CERTS_EXPIRY)
|
|
28
|
+
super()
|
|
29
|
+
|
|
30
|
+
@aud = aud.to_s
|
|
31
|
+
@iss = "#{GOOGLE_ISSUER_PREFIX}#{aud}"
|
|
32
|
+
@certs_expiry = expiry
|
|
33
|
+
@certs = {}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# If it validates, returns a hash with the JWT payload from the ID Token.
|
|
38
|
+
# You have to provide an "aud" value, which must match the
|
|
39
|
+
# token"s field with that name, and will similarly check cid if provided.
|
|
40
|
+
#
|
|
41
|
+
# If something fails, raises an error
|
|
42
|
+
#
|
|
43
|
+
# @param [String] token
|
|
44
|
+
# The string form of the token
|
|
45
|
+
#
|
|
46
|
+
# @return [Hash] The decoded ID token
|
|
47
|
+
def check(token)
|
|
48
|
+
synchronize do
|
|
49
|
+
payload = check_cached_certs(token)
|
|
50
|
+
|
|
51
|
+
unless payload
|
|
52
|
+
# no certs worked, might've expired, refresh
|
|
53
|
+
if refresh_certs
|
|
54
|
+
payload = check_cached_certs(token)
|
|
55
|
+
|
|
56
|
+
unless payload
|
|
57
|
+
raise SignatureError, "Token not verified as issued by Google"
|
|
58
|
+
end
|
|
59
|
+
else
|
|
60
|
+
raise CertificateError, "Unable to retrieve Google public keys"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
payload
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# tries to validate the token against each cached cert.
|
|
71
|
+
# Returns the token payload or raises a ValidationError or `nil',
|
|
72
|
+
# which means none of the certs validated.
|
|
73
|
+
def check_cached_certs(token)
|
|
74
|
+
payload = nil
|
|
75
|
+
|
|
76
|
+
# find first public key that validates this token
|
|
77
|
+
@certs.detect do |key, cert|
|
|
78
|
+
begin
|
|
79
|
+
public_key = cert.public_key
|
|
80
|
+
decoded_token = JWT.decode(
|
|
81
|
+
token,
|
|
82
|
+
public_key,
|
|
83
|
+
!!public_key,
|
|
84
|
+
{ algorithm: "RS256", kid: key }
|
|
85
|
+
)
|
|
86
|
+
payload = decoded_token.first
|
|
87
|
+
|
|
88
|
+
payload
|
|
89
|
+
rescue JWT::ExpiredSignature
|
|
90
|
+
raise ExpiredTokenError, "Token signature is expired"
|
|
91
|
+
rescue JWT::DecodeError
|
|
92
|
+
nil # go on, try the next cert
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if payload
|
|
97
|
+
if !(payload.has_key?("aud") && payload["aud"] == @aud)
|
|
98
|
+
raise AudienceMismatchError, "Token audience mismatch"
|
|
99
|
+
end
|
|
100
|
+
if payload["iss"] != @iss
|
|
101
|
+
raise InvalidIssuerError, "Token issuer mismatch"
|
|
102
|
+
end
|
|
103
|
+
payload
|
|
104
|
+
else
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def refresh_certs
|
|
110
|
+
return true unless certs_cache_expired?
|
|
111
|
+
|
|
112
|
+
uri = URI(GOOGLE_CERTS_URI)
|
|
113
|
+
get = Net::HTTP::Get.new uri.request_uri
|
|
114
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
115
|
+
http.use_ssl = true
|
|
116
|
+
res = http.request(get)
|
|
117
|
+
|
|
118
|
+
if res.is_a?(Net::HTTPSuccess)
|
|
119
|
+
new_certs = Hash[JSON.load(res.body).map do |key, cert|
|
|
120
|
+
[key, OpenSSL::X509::Certificate.new(cert)]
|
|
121
|
+
end]
|
|
122
|
+
@certs.merge! new_certs
|
|
123
|
+
@certs_last_refresh = Time.now
|
|
124
|
+
true
|
|
125
|
+
else
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def certs_cache_expired?
|
|
131
|
+
if defined? @certs_last_refresh
|
|
132
|
+
Time.now > @certs_last_refresh + @certs_expiry
|
|
133
|
+
else
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise-fireauth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.1.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- yeuem1vannam
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-09-
|
|
11
|
+
date: 2018-09-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: devise
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -25,21 +39,35 @@ dependencies:
|
|
|
25
39
|
- !ruby/object:Gem::Version
|
|
26
40
|
version: '4.0'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
42
|
+
name: dry-configurable
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - ">="
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
47
|
+
version: '0'
|
|
34
48
|
type: :runtime
|
|
35
49
|
prerelease: false
|
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
51
|
requirements:
|
|
38
52
|
- - ">="
|
|
39
53
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
54
|
+
version: '0'
|
|
41
55
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
56
|
+
name: jwt
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: warden
|
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
|
44
72
|
requirements:
|
|
45
73
|
- - ">="
|
|
@@ -53,13 +81,13 @@ dependencies:
|
|
|
53
81
|
- !ruby/object:Gem::Version
|
|
54
82
|
version: '0'
|
|
55
83
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
84
|
+
name: activemodel
|
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
|
58
86
|
requirements:
|
|
59
87
|
- - ">="
|
|
60
88
|
- !ruby/object:Gem::Version
|
|
61
89
|
version: '0'
|
|
62
|
-
type: :
|
|
90
|
+
type: :development
|
|
63
91
|
prerelease: false
|
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
93
|
requirements:
|
|
@@ -80,6 +108,48 @@ dependencies:
|
|
|
80
108
|
- - "~>"
|
|
81
109
|
- !ruby/object:Gem::Version
|
|
82
110
|
version: '1.16'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: fakeweb
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: openssl
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: pry
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
83
153
|
- !ruby/object:Gem::Dependency
|
|
84
154
|
name: rake
|
|
85
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +164,20 @@ dependencies:
|
|
|
94
164
|
- - "~>"
|
|
95
165
|
- !ruby/object:Gem::Version
|
|
96
166
|
version: '10.0'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: railties
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '4'
|
|
174
|
+
type: :development
|
|
175
|
+
prerelease: false
|
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
+
requirements:
|
|
178
|
+
- - ">="
|
|
179
|
+
- !ruby/object:Gem::Version
|
|
180
|
+
version: '4'
|
|
97
181
|
- !ruby/object:Gem::Dependency
|
|
98
182
|
name: rspec
|
|
99
183
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,6 +192,34 @@ dependencies:
|
|
|
108
192
|
- - "~>"
|
|
109
193
|
- !ruby/object:Gem::Version
|
|
110
194
|
version: '3.0'
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: rubocop-github
|
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
|
198
|
+
requirements:
|
|
199
|
+
- - ">="
|
|
200
|
+
- !ruby/object:Gem::Version
|
|
201
|
+
version: '0'
|
|
202
|
+
type: :development
|
|
203
|
+
prerelease: false
|
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - ">="
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: '0'
|
|
209
|
+
- !ruby/object:Gem::Dependency
|
|
210
|
+
name: rubocop-rspec
|
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
|
212
|
+
requirements:
|
|
213
|
+
- - ">="
|
|
214
|
+
- !ruby/object:Gem::Version
|
|
215
|
+
version: '0'
|
|
216
|
+
type: :development
|
|
217
|
+
prerelease: false
|
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
+
requirements:
|
|
220
|
+
- - ">="
|
|
221
|
+
- !ruby/object:Gem::Version
|
|
222
|
+
version: '0'
|
|
111
223
|
description: Firebase as authentication service behind the devise
|
|
112
224
|
email:
|
|
113
225
|
executables: []
|
|
@@ -116,6 +228,7 @@ extra_rdoc_files: []
|
|
|
116
228
|
files:
|
|
117
229
|
- ".gitignore"
|
|
118
230
|
- ".rspec"
|
|
231
|
+
- ".rubocop.yml"
|
|
119
232
|
- ".travis.yml"
|
|
120
233
|
- CODE_OF_CONDUCT.md
|
|
121
234
|
- Gemfile
|
|
@@ -130,6 +243,7 @@ files:
|
|
|
130
243
|
- lib/devise/fireauth/models/firebase_authenticatable.rb
|
|
131
244
|
- lib/devise/fireauth/strategies/firebase_authenticatable.rb
|
|
132
245
|
- lib/devise/fireauth/version.rb
|
|
246
|
+
- lib/firebase_id_token.rb
|
|
133
247
|
homepage: https://github.com/yeuem1vannam/devise-fireauth
|
|
134
248
|
licenses:
|
|
135
249
|
- MIT
|