-
-
Save midnight-wonderer/c40b8c46dc42cb560ccbdcd4a79f52c9 to your computer and use it in GitHub Desktop.
FirebaseAuth::Auth.verify_id_token | Ruby solution for https://firebase.google.com/docs/auth/admin/verify-id-tokens
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Usage: | |
# ======== | |
# FirebaseAuth::Auth.verify_id_token(your_id_token) | |
# | |
# The method call follows the same API of the Node.js, JAVA and Python SDKs. | |
# See https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_the_firebase_admin_sdk | |
# Dependencies: | |
# --------------- | |
# gem 'activesupport' | |
# gem 'faraday' | |
# gem 'jwt', '~> 1.5', '>= 1.5.6' | |
# require 'jwt' | |
# require 'faraday' | |
# require 'active_support/core_ext/module/delegation' | |
# | |
# require 'openssl' | |
# require 'singleton' | |
# require 'ostruct' | |
module FirebaseAuth | |
class PublicKeys | |
URL = 'https://www.googleapis.com/robot/v1/metadata/x509/[email protected]' | |
EXPIRES_HEADER = 'expires' | |
attr_reader :response | |
delegate :keys, :values, to: :data | |
def initialize | |
@response = fetch | |
end | |
def valid? | |
Time.now.utc < time_to_expire | |
end | |
def data | |
@parsed_body ||= JSON.parse(response.body) | |
end | |
def look_up(kid) | |
@certificate_hash ||= Hash[data.map { |k, v| [k, OpenSSL::X509::Certificate.new(v)] }] | |
@certificate_hash[kid] | |
end | |
private | |
def time_to_expire | |
@time_to_expire ||= Time.parse( | |
response.headers[EXPIRES_HEADER] | |
) | |
end | |
def fetch | |
Faraday.get(URL) | |
end | |
end | |
class IDTokenVerifier | |
JWT_OPTIONS = { algorithm: 'RS256', verify_iat: true } | |
def initialize(public_keys) | |
@public_keys = public_keys | |
end | |
def verify(id_token) | |
kid = JWT.decode(id_token, nil, false).last['kid'] rescue nil | |
decode_jwt(id_token, @public_keys.look_up(kid)) | |
end | |
private | |
def decode_jwt(id_token, x509) | |
JWT.decode(id_token, x509.public_key, true, JWT_OPTIONS) | |
rescue JWT::VerificationError | |
nil | |
end | |
end | |
class Auth | |
include Singleton | |
def initialize | |
refresh | |
end | |
def public_keys | |
resolve { @public_keys } | |
end | |
def verify_id_token(id_token) | |
result = resolve { @id_token_verifier.verify(id_token) } | |
if result | |
payload, header = result | |
[OpenStruct.new(payload), OpenStruct.new(header)] | |
end | |
end | |
class << self | |
delegate :verify_id_token, :public_keys, to: :instance | |
end | |
private | |
def refresh | |
@public_keys = PublicKeys.new | |
@id_token_verifier = IDTokenVerifier.new(@public_keys) | |
end | |
def resolve | |
refresh unless @public_keys.valid? | |
yield | |
end | |
end | |
end |
@JeffreyCheng92 sorry I did not see your comment.
It is way too late now but in order to integrate the snippet into Rails project you can use
https://apidock.com/rails/v4.2.7/ActionController/HttpAuthentication/Token/token_and_options
to retrieve the authorization header.
and this is an example of tokens returned from Firebase
[#<OpenStruct
iss = "https://securetoken.google.com/firebase-project-name",
name = "Sanji",
picture = "https://lh3.googleusercontent.com/-Y1_FYLQpPns/AAAAAAAAAAI/AAAAAAAAAZU/T_xT1bjE-xo/photo.jpg",
aud = "firebase-project-name",
auth_time = 1493484250,
user_id = "anFwD7AhKcUGXkb56j22bzkbj4ql",
sub = "anFwD7AhKcUGXkb56j22bzkbj4ql",
iat = 1494164568,
exp = 1494168168,
email = "[email protected]",
email_verified = true,
firebase = {
"identities" => {
"google.com" => ["679820163956820153065"],
"email" => ["[email protected]"]
},
"sign_in_provider" => "google.com"
}>,
#<OpenStruct
alg = "RS256",
kid = "da02f3417dbdaa7b93c9e49bbf7128fddd67cb50"
>]
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, I just had a few questions on the usage of this as I'm implementing it right now since the Firebase Auth sdk for ruby is not out yet.
From what I can see,
PublicKeys.new
gets all the public keys from thefetch
method. TheIDTokenVerifier
is responsible for comparing the JWT token passed in from the front end into the backend rails application. So I'll lay out my steps below:token = request.headers["Authorization"] #JWT token is received from request headers
FirebaseAuth::Auth.new.verify_id_token(token) # Supposedly returns [OpenStruct.new(payload), OpenStruct.new(header)]
#verify_id_token
the data of the user from firebase? Something likeemail
,uid
? Then I can just do something likeUser.find_by_email(email)
?Thanks for your help in advance!