firebase-verifier 0.1.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ba82cb9c05bac921ca409201ba45c3157e2c9c9050c4d05b839d26e54af6b18
4
- data.tar.gz: cbbb89f4ea2f26f48fa7d9b6b34f228d4e0243dd4a85dc7b127830f723ea634e
3
+ metadata.gz: ec6a37622e586bd362d2a8435447a0bd9f1032ae86bfe6b6f48836c0108e6cb7
4
+ data.tar.gz: 304865d4091509f73e08a75d111f2382aab8fc0f064b23d0274dcdddfd150b64
5
5
  SHA512:
6
- metadata.gz: e8904cb82df7fcd92b6a91815ba3b8e1ce109715d465d4afc9395b8d668a38bdb451c2a2a5a7b123fc6f5ff52924a7aee3e22b72a4b7b078795843aec7c578cb
7
- data.tar.gz: 3aebcf7585b5a67f729af6836f9cdeaf05342c54ef4a5d22ac7ed88d5a7c02e7e60ac5d70f3eb1531b4a8d3fc100545bd4aa277538dd4714743715397c4b5937
6
+ metadata.gz: a43723c7e762feb715ef087de087beac7de787e707b0a74b776b9f2a44d791b028798818dfdff929e8203f66833d33a454276dabedb30aaaa42ddc8ed7395cbc
7
+ data.tar.gz: 00da9c1b6d851f4dd989630084d39487ebee3672ecd50015a733ad033b483709bc89823b72aa87cdb4b7dd08b8e663eafda64689969563917480a915634f58d0
Binary file
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class FirebaseVerifier
4
+ VERSION = "0.1.2"
5
+ end
@@ -1,2 +1,97 @@
1
- require "firebase-verifier/verifier/version"
2
- require "firebase-verifier/verifier"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "firebase-verifier/version"
4
+ require 'net/http'
5
+ require 'json'
6
+ require 'jwt'
7
+
8
+ class FirebaseVerifier
9
+ VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY = "firebase_jwt_public_keys_cache_key"
10
+ JWT_ALGORITHM = 'RS256'
11
+
12
+ def initialize(firebase_project_id)
13
+ @firebase_project_id = firebase_project_id
14
+ end
15
+
16
+ def decode(id_token)
17
+ payload, header = decode_jwt_token(id_token, @firebase_project_id)
18
+
19
+ alg = header['alg']
20
+ raise "Invalid access token 'alg' header (#{alg}). Must be '#{JWT_ALGORITHM}'." unless alg == JWT_ALGORITHM
21
+
22
+ typ = header['typ']
23
+ raise "Invalid access token 'typ' header (#{typ}). Must be 'JWT'." unless typ == 'JWT'
24
+
25
+ iss = payload['iss']
26
+ issuer = "https://securetoken.google.com/#{@firebase_project_id}"
27
+ raise "Invalid access token 'iss' payload (#{iss}). Must be '#{issuer}'." unless iss == issuer
28
+
29
+ exp = payload['exp']
30
+ raise "Invalid access token 'exp' payload (#{exp}). Token has expired." unless Time.at(exp) > Time.now
31
+
32
+ aud = payload['aud']
33
+ audience = @firebase_project_id
34
+ raise "Invalid access token 'aud' payload (#{aud}). Must be '#{audience}'." unless aud == audience
35
+
36
+ kid = header['kid']
37
+ valid_public_keys = retrieve_and_cache_jwt_valid_public_keys
38
+ raise "Invalid access token 'kid' header, do not correspond to valid public keys." unless valid_public_keys.keys.include?(kid)
39
+
40
+ sub = payload['sub']
41
+ raise "Invalid access token. 'Subject' (sub) must be a non-empty string." if sub.nil? || sub.empty?
42
+
43
+ payload
44
+ end
45
+
46
+ private
47
+
48
+ def decode_jwt_token(firebase_jwt_token, firebase_project_id)
49
+ public_key = nil
50
+ custom_options = {
51
+ verify_iat: true,
52
+ verify_aud: true,
53
+ aud: firebase_project_id,
54
+ verify_iss: true,
55
+ iss: "https://securetoken.google.com/#{firebase_project_id}"
56
+ }
57
+
58
+ custom_options[:algorithm] = JWT_ALGORITHM unless public_key.nil?
59
+
60
+ begin
61
+ decoded_token = JWT.decode(firebase_jwt_token, public_key, !public_key.nil?, custom_options)
62
+ rescue JWT::ExpiredSignature
63
+ return nil, "Invalid access token. 'Expiration time' (exp) must be in the future."
64
+ rescue JWT::InvalidIatError
65
+ return nil, "Invalid access token. 'Issued-at time' (iat) must be in the past."
66
+ rescue JWT::InvalidAudError
67
+ return nil, "Invalid access token. 'Audience' (aud) must be your Firebase project ID, the unique identifier for your Firebase project."
68
+ rescue JWT::InvalidIssuerError
69
+ return nil, "Invalid access token. 'Issuer' (iss) Must be 'https://securetoken.google.com/<projectId>', where <projectId> is your Firebase project ID."
70
+ rescue JWT::VerificationError
71
+ return nil, "Invalid access token. Signature verification failed."
72
+ end
73
+
74
+ return decoded_token
75
+ end
76
+
77
+ def retrieve_and_cache_jwt_valid_public_keys
78
+ valid_public_keys = Rails.cache.read(VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY)
79
+ if valid_public_keys.nil?
80
+ uri = URI("https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com")
81
+ response = Net::HTTP.get_response(uri)
82
+
83
+ if response.code != '200'
84
+ raise "Something went wrong: can't obtain valid JWT public keys from Google."
85
+ end
86
+
87
+ valid_public_keys = JSON.parse(response.body)
88
+
89
+ cc = response["cache-control"]
90
+ max_age = cc[/max-age=(\d+?),/m, 1]
91
+
92
+ Rails.cache.write(VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY, valid_public_keys, :expires_in => max_age.to_i)
93
+ end
94
+
95
+ valid_public_keys
96
+ end
97
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firebase-verifier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - KuruStudio
@@ -22,9 +22,9 @@ files:
22
22
  - README.md
23
23
  - Rakefile
24
24
  - firebase-verifier-0.1.0.gem
25
+ - firebase-verifier-0.1.1.gem
25
26
  - lib/firebase-verifier.rb
26
- - lib/firebase-verifier/verifier.rb
27
- - lib/firebase-verifier/verifier/version.rb
27
+ - lib/firebase-verifier/version.rb
28
28
  - sig/firebase-verifier/verifier.rbs
29
29
  homepage: https://kuru.studio
30
30
  licenses: []
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FirebaseVerifier
4
- module Verifier
5
- VERSION = "0.1.1"
6
- end
7
- end
@@ -1,100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "verifier/version"
4
- require 'net/http'
5
- require 'json'
6
- require 'jwt'
7
-
8
- module FirebaseVerifier
9
- module Verifier
10
- class Error < StandardError; end
11
- VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY = "firebase_jwt_public_keys_cache_key"
12
- JWT_ALGORITHM = 'RS256'
13
-
14
- def initialize(firebase_project_id)
15
- @firebase_project_id = firebase_project_id
16
- end
17
-
18
- def decode(id_token)
19
- payload, header = decode_jwt_token(id_token, @firebase_project_id)
20
-
21
- alg = header['alg']
22
- raise "Invalid access token 'alg' header (#{alg}). Must be '#{JWT_ALGORITHM}'." unless alg == JWT_ALGORITHM
23
-
24
- typ = header['typ']
25
- raise "Invalid access token 'typ' header (#{typ}). Must be 'JWT'." unless typ == 'JWT'
26
-
27
- iss = payload['iss']
28
- issuer = "https://securetoken.google.com/#{@firebase_project_id}"
29
- raise "Invalid access token 'iss' payload (#{iss}). Must be '#{issuer}'." unless iss == issuer
30
-
31
- exp = payload['exp']
32
- raise "Invalid access token 'exp' payload (#{exp}). Token has expired." unless Time.at(exp) > Time.now
33
-
34
- aud = payload['aud']
35
- audience = @firebase_project_id
36
- raise "Invalid access token 'aud' payload (#{aud}). Must be '#{audience}'." unless aud == audience
37
-
38
- kid = header['kid']
39
- valid_public_keys = retrieve_and_cache_jwt_valid_public_keys
40
- raise "Invalid access token 'kid' header, do not correspond to valid public keys." unless valid_public_keys.keys.include?(kid)
41
-
42
- sub = payload['sub']
43
- raise "Invalid access token. 'Subject' (sub) must be a non-empty string." if sub.nil? || sub.empty?
44
-
45
- payload
46
- end
47
-
48
- private
49
-
50
- def decode_jwt_token(firebase_jwt_token, firebase_project_id)
51
- public_key = nil
52
- custom_options = {
53
- verify_iat: true,
54
- verify_aud: true,
55
- aud: firebase_project_id,
56
- verify_iss: true,
57
- iss: "https://securetoken.google.com/#{firebase_project_id}"
58
- }
59
-
60
- custom_options[:algorithm] = JWT_ALGORITHM unless public_key.nil?
61
-
62
- begin
63
- decoded_token = JWT.decode(firebase_jwt_token, public_key, !public_key.nil?, custom_options)
64
- rescue JWT::ExpiredSignature
65
- return nil, "Invalid access token. 'Expiration time' (exp) must be in the future."
66
- rescue JWT::InvalidIatError
67
- return nil, "Invalid access token. 'Issued-at time' (iat) must be in the past."
68
- rescue JWT::InvalidAudError
69
- return nil, "Invalid access token. 'Audience' (aud) must be your Firebase project ID, the unique identifier for your Firebase project."
70
- rescue JWT::InvalidIssuerError
71
- return nil, "Invalid access token. 'Issuer' (iss) Must be 'https://securetoken.google.com/<projectId>', where <projectId> is your Firebase project ID."
72
- rescue JWT::VerificationError
73
- return nil, "Invalid access token. Signature verification failed."
74
- end
75
-
76
- return decoded_token
77
- end
78
-
79
- def retrieve_and_cache_jwt_valid_public_keys
80
- valid_public_keys = Rails.cache.read(VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY)
81
- if valid_public_keys.nil?
82
- uri = URI("https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com")
83
- response = Net::HTTP.get_response(uri)
84
-
85
- if response.code != '200'
86
- raise "Something went wrong: can't obtain valid JWT public keys from Google."
87
- end
88
-
89
- valid_public_keys = JSON.parse(response.body)
90
-
91
- cc = response["cache-control"]
92
- max_age = cc[/max-age=(\d+?),/m, 1]
93
-
94
- Rails.cache.write(VALID_JWT_PUBLIC_KEYS_RESPONSE_CACHE_KEY, valid_public_keys, :expires_in => max_age.to_i)
95
- end
96
-
97
- valid_public_keys
98
- end
99
- end
100
- end