Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper way to verify Firebase id tokens #216

Closed
soulfly opened this issue Jul 27, 2017 · 8 comments
Closed

Proper way to verify Firebase id tokens #216

soulfly opened this issue Jul 27, 2017 · 8 comments

Comments

@soulfly
Copy link

soulfly commented Jul 27, 2017

Hi guys,
I use ruby-jwt gem to decode and verify Firebase id tokens

Here is a quick brief how it works, what needs to be done
https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library

I have a question about signature validation

As you see we do not know RSA public key before decoding

  1. We need to decode this token first
  2. then grab kid header
  3. then go to https://www.googleapis.com/robot/v1/metadata/x509/[email protected] and grab that certificate where key is our kid
  4. And only then we can verify our JWT token

So it looks like we have to decode 2 times our JWT token using ruby-jwt' gem

So I came up with the following solution:

require 'jwt'

firebase_project_id = "my_firebase_project_id"
firebase_access_token = 'eyJhb....Kc9g'


decoded_token = JWT.decode firebase_access_token, nil, false,
  { :verify_iat => true,
    :verify_aud => true, :aud => firebase_project_id,
    :verify_iss => true, :iss => "https://securetoken.google.com/"+firebase_project_id }

headers = decoded_token[1]
kid = headers['kid']

puts "KID: #{kid}"

# go to https://www.googleapis.com/robot/v1/metadata/x509/[email protected]
# get valid public keys and grab that with key=kid
# ...

cert_string = "-----BEGIN CERTIFICATE-----.....-----END CERTIFICATE-----\n"
cert = OpenSSL::X509::Certificate.new(cert_string)

#
# ...

decoded_token = JWT.decode firebase_access_token, cert.public_key, true,
  { :algorithm => 'RS256',
    :verify_iat => true,
    :verify_aud => true, :aud => firebase_project_id,
    :verify_iss => true, :iss => "https://securetoken.google.com/"+firebase_project_id }

puts decoded_token

Could you please verify that this is a right solution, that we need to decode it twice in Firebase case

thank you

@excpt
Copy link
Member

excpt commented Jul 31, 2017

In your case this is the right solution. The double decoding process itself should not impact the overall performance of your app. If you experience any problems with performance let us know.

The first JWT.decode call simply extracts the data from the token and skips all verification therefore should be fast. The second decoding runs all the verification and is in that case the expensive operation.

@soulfly
Copy link
Author

soulfly commented Jul 31, 2017

Thanks @excpt for your response so we follow the right direction then 👍

I have prepared a post on medium describing some pit fails when work with Firebase JWT. https://medium.com/@igorkhomenko/how-to-validate-firebase-id-token-in-ruby-23f4f54c89ab

maybe someone will find it useful and save some time

@soulfly soulfly closed this as completed Jul 31, 2017
@excpt
Copy link
Member

excpt commented Jul 31, 2017

@soulfly I recommend to make one change in your blog post. This is not a official JWT thing. There is only the official JWT RFC. This is "just" a ruby implementation of that RFC and I am just the maintainer. So you got an answer from the ruby-jwt maintainers. ;)

@soulfly
Copy link
Author

soulfly commented Jul 31, 2017

@excpt got it :) thank you

@jpheos
Copy link

jpheos commented Dec 21, 2018

@soulfly that help me a lot, thank you.

@veeral-patel
Copy link

Also I stumbled upon this library @soulfly

https://github.com/fschuindt/firebase_id_token

@omartorresrios
Copy link

Hello, sorry for commenting even though this is closed!.
I would like to know how we get that cert_string from that url. I don't think we just copy and paste in code that certificate as a string. Is there any way to get the valid public key and grab it?
Thanks!

@elvack
Copy link

elvack commented Jan 19, 2021

JWT.decode(<bearer_token>, nil, true, { algorithm: 'RS256' }) do |header|
  url = URI('https://www.googleapis.com/robot/v1/metadata/x509/[email protected]')
  json = JSON.parse(Net::HTTP.get(url))
  public_key = OpenSSL::X509::Certificate.new(json[header['kid']]).public_key
end

@omartorresrios I have tried the code above and worked well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants