{"id":13682537,"url":"https://github.com/fschuindt/firebase_id_token","last_synced_at":"2025-04-04T08:09:49.669Z","repository":{"id":20108126,"uuid":"88820437","full_name":"fschuindt/firebase_id_token","owner":"fschuindt","description":"A Ruby gem to verify the signature of Firebase ID Tokens.","archived":false,"fork":false,"pushed_at":"2023-05-23T23:18:54.000Z","size":165,"stargazers_count":154,"open_issues_count":3,"forks_count":47,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-10-30T06:58:41.396Z","etag":null,"topics":["firebase","firebase-auth","jwt","ruby","ruby-on-rails","token"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fschuindt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-04-20T04:22:54.000Z","updated_at":"2024-10-14T12:04:45.000Z","dependencies_parsed_at":"2024-01-14T15:23:35.779Z","dependency_job_id":"934d0318-faf0-46fe-8422-5c0b8c6eb96d","html_url":"https://github.com/fschuindt/firebase_id_token","commit_stats":{"total_commits":113,"total_committers":11,"mean_commits":"10.272727272727273","dds":"0.15929203539823011","last_synced_commit":"ce21d1c57f43be3650626c533d6229e0757a3a72"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschuindt%2Ffirebase_id_token","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschuindt%2Ffirebase_id_token/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschuindt%2Ffirebase_id_token/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fschuindt%2Ffirebase_id_token/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fschuindt","download_url":"https://codeload.github.com/fschuindt/firebase_id_token/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142074,"owners_count":20890653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["firebase","firebase-auth","jwt","ruby","ruby-on-rails","token"],"created_at":"2024-08-02T13:01:47.676Z","updated_at":"2025-04-04T08:09:49.648Z","avatar_url":"https://github.com/fschuindt.png","language":"Ruby","readme":"# Ruby Firebase ID Token verifier\n\n[![Actions Status](https://github.com/fschuindt/firebase_id_token/workflows/Test/badge.svg?branch=master)](https://github.com/fschuindt/firebase_id_token/actions?query=workflow%3ATest)\n[![Test Coverage](https://codeclimate.com/github/fschuindt/firebase_id_token/badges/coverage.svg)](https://codeclimate.com/github/fschuindt/firebase_id_token/coverage)\n[![Code Climate](https://codeclimate.com/github/fschuindt/firebase_id_token/badges/gpa.svg)](https://codeclimate.com/github/fschuindt/firebase_id_token)\n[![Issue Count](https://codeclimate.com/github/fschuindt/firebase_id_token/badges/issue_count.svg)](https://codeclimate.com/github/fschuindt/firebase_id_token)\n[![Inline docs](http://inch-ci.org/github/fschuindt/firebase_id_token.svg?branch=master)](http://inch-ci.org/github/fschuindt/firebase_id_token)\n\nA Ruby gem to verify the signature of Firebase ID Tokens (JWT). It uses Redis to store Google's x509 certificates and manage their expiration time, so you don't need to request Google's API in every execution and can access it as fast as reading from memory.\n\nIt also checks the JWT payload parameters as recommended [here](https://firebase.google.com/docs/auth/admin/verify-id-tokens) by Firebase official documentation.\n\nFeel free to open any issue or to [contact me](https://fschuindt.github.io/blog/about/) directly.  \nAny contribution is welcome.\n\n## Docs\n\n + http://www.rubydoc.info/gems/firebase_id_token\n\n## Requirements\n\n+ Redis\n\n## Installing\n\n```\ngem install firebase_id_token\n```\n\nor in your Gemfile\n```\ngem 'firebase_id_token', '~\u003e 3.0.0'\n```\nthen\n```\nbundle install\n```\n\n## Configuration\n\nIt's needed to set up your Firebase Project ID.\n\nIf you are using Rails, this should probably go into `config/initializers/firebase_id_token.rb`.\n```ruby\nFirebaseIdToken.configure do |config|\n  config.redis = Redis.new\n  config.project_ids = ['your-firebase-project-id']\nend\n```\n\n- `redis` with a `Redis` instance must be supplied. You can configure your Redis details here. Example: `Redis.new(host: '10.0.1.1', port: 6380, db: 15)`.\n- `project_ids` must be an Array.\n\n*If you want to verify signatures from more than one Firebase project, just add more Project IDs to the list.*\n\n## Usage\n\nYou can get a glimpse of it by reading our RSpec output on your machine. It's\nreally helpful. But here is a complete guide:\n\n### Downloading Certificates\n\nBefore verifying tokens, you need to download Google's x509 certificates.\n\nTo do it, simply:\n```ruby\nFirebaseIdToken::Certificates.request\n```\n\nIt will download the certificates and save it in Redis, but only if the Redis certificates database is empty. To force download and override of the Redis database, use:\n```ruby\nFirebaseIdToken::Certificates.request!\n```\n\nGoogle give us information about the certificates' expiration time, it's used to set a Redis TTL (Time-To-Live) when saving it. By doing so, the certificates will be automatically deleted after its expiration.\n\n#### Certificates Info\n\nChecks the presence of certificates in the Redis database.\n```ruby\nFirebaseIdToken::Certificates.present?\n=\u003e true\n```\n\nHow many seconds until the certificate's expiration.\n```ruby\nFirebaseIdToken::Certificates.ttl\n=\u003e 22352\n```\n\nLists all certificates in the database.\n```ruby\nFirebaseIdToken::Certificates.all\n=\u003e [{\"ec8f292sd30224afac5c55540df66d1f999d\" =\u003e \u003cOpenSSL::X509::Certificate: [...]]\n```\n\nFinds the respective certificate of a given Key ID (`kid`).\n```ruby\nFirebaseIdToken::Certificates.find('ec8f292sd30224afac5c55540df66d1f999d')\n=\u003e \u003cOpenSSL::X509::Certificate: subject=\u003cOpenSSL::X509 [...]\u003e\n```\n\n#### Downloading in Rails\n\nIf you are using Rails, it's clever to download certificates in a cron task, you can use [whenever](https://github.com/javan/whenever).\n\n**Example**\n\n*Read whenever's guide on how to set it up.*\n\nCreate your task in `lib/tasks/firebase.rake`:\n```ruby\nnamespace :firebase do\n  namespace :certificates do\n    desc \"Request Google's x509 certificates when Redis is empty\"\n    task request: :environment do\n      FirebaseIdToken::Certificates.request\n    end\n\n    desc \"Request Google's x509 certificates and override Redis\"\n    task force_request: :environment do\n      FirebaseIdToken::Certificates.request!\n    end\n  end\nend\n```\n\nAnd in your `config/schedule.rb` you might have:\n```ruby\nevery 1.hour do\n  rake 'firebase:certificates:force_request'\nend\n```\n\nThen:\n```\n$ whenever --update-crontab\n```\n\nI recommend running it once every hour or every 30 minutes, it's up to you. Normally the certificates expiration time is around 4 to 6 hours, but it's good to perform it in a small fraction of this time.\n\nWhen developing, you should just run the task:\n```\n$ rake firebase:certificates:request\n```\n\n*You need Redis to be running.*\n\n### Verifying Tokens\n\nPass the Firebase ID Token to `FirebaseIdToken::Signature.verify` and it will return the token payload if everything is ok:\n\n```ruby\nFirebaseIdToken::Signature.verify(token)\n=\u003e {\"iss\"=\u003e\"https://securetoken.google.com/firebase-id-token\", \"name\"=\u003e\"Bob Test\", [...]}\n```\n\nWhen either the signature is false or the token is invalid, it will return `nil`:\n```ruby\nFirebaseIdToken::Signature.verify(fake_token)\n=\u003e nil\n\nFirebaseIdToken::Signature.verify('aaaaaa')\n=\u003e nil\n```\n\n#### WARNING!\n\n##### Expired tokens can point to long gone certificates\n\nNotice that often when the token have expired, the Firebase certificate can be already missing from the Firebase servers. In these cases, `verify` will return `nil`.\n\nIf you want to take specific actions in such cases, here's a solution suggested by the user [cfanpnk](https://github.com/fschuindt/firebase_id_token/issues/29#issuecomment-751137511):\n\n1. Use `verify!` to raise an exception.\n2. Rescue `FirebaseIdToken::Exceptions::CertificateNotFound` and return `401`.\n3. The client app will refresh the token if expired.\n\nMore details [here](https://github.com/fschuindt/firebase_id_token/issues/29).\n\n##### Trying to verify tokens without downloaded certificates will raise an error\n\nIf you try to verify a signature without any certificates in Redis database, it will raise a `FirebaseIdToken::Exceptions::NoCertificatesError`.\n\n##### \"I keep on getting `nil` on `verify`\"\n\nPoorly synchronized clocks will sometimes make the server think the token's `iat` is on the future, which will render the token as invalid. Make sure your server's or development system's clock is correctly set. On macOS, some people reported success by unchecking and checking the \"Set date and time automatically\" configuration checkbox. See [here](https://github.com/fschuindt/firebase_id_token/issues/21#issuecomment-623133926).\n\n#### Payload Structure\n\nIn case you need, here's a example of the payload structure from a Google login in JSON.\n```json\n{  \n   \"iss\":\"https://securetoken.google.com/{{YOUR_FIREBASE_APP_ID}}\",\n   \"name\":\"Ugly Bob\",\n   \"picture\":\"https://someurl.com/photo.jpg\",\n   \"aud\":\"{{YOUR_FIREBASE_APP_ID}}\",\n   \"auth_time\":1492981192,\n   \"user_id\":\"theUserID\",\n   \"sub\":\"theUserID\",\n   \"iat\":1492981200, // needs to be in the past\n   \"exp\":33029000017, // needs to be in the future\n   \"email\":\"uglybob@emailurl.com\",\n   \"email_verified\":true,\n   \"firebase\":{  \n      \"identities\":{  \n         \"google.com\":[  \n            \"1010101010101010101\"\n         ],\n         \"email\":[  \n            \"uglybob@emailurl.com\"\n         ]\n      },\n      \"sign_in_provider\":\"google.com\"\n   }\n}\n\n```\n\nIf you're using this snippet for testing, make sure you check the comments in it.\n\n## Testing\n\n```\nbundle exec rake rspec\n```\n\n### Testing Mode\n\nJust run:\n```\nFirebaseIdToken.test!\n```\n\nBy using this mode, the following methods become available.\n\n```ruby\n# RSA PRIVATE KEY\nFirebaseIdToken::Testing::Certificates.private_key\n\n# CERTIFICATE\nFirebaseIdToken::Testing::Certificates.certificate\n```\n\n`certificate` will always return the same value. No external HTTP call is performed.\n\n#### Example: Testing in Rails\n\nDescribes the following in `test_helper.rb`.\n\n```ruby\nclass ActiveSupport::TestCase\n  setup do\n    FirebaseIdToken.test!\n  end\nend\n```\n\nTest example:\n\n```ruby\nrequire 'test_helper'\n\nmodule Api\n  module V1\n    module UsersControllerTest \u003c ActionController::TestCase\n      setup do\n        @routes = Engine.routes\n        @user = users(:one)\n      end\n        \n      def create_token(sub: nil)\n        _payload = payload.merge({sub: sub})\n        JWT.encode _payload, OpenSSL::PKey::RSA.new(FirebaseIdToken::Testing::Certificates.private_key), 'RS256'\n      end\n\n      def payload\n        # payload.json\n      end\n\n      test 'should success get api v1 users ' do\n        get :show, headers: create_token(@user.id)\n        assert_response :success\n      end\n    end\n  end\nend\n```\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschuindt%2Ffirebase_id_token","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffschuindt%2Ffirebase_id_token","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffschuindt%2Ffirebase_id_token/lists"}