5
votes

I have a firebase project which Im trying to authenticate from my rails server creating a custom token with the library ruby-jwt as it says on the docs, but i keep getting the same error:

auth/invalid-custom-token, The custom token format is incorrect. Please check the documentation.

The credentials.json is from the service account I made in google console, uid is sent from the front end to the api.

def generate_auth_token(uid)
  now_seconds = Time.now.to_i
  credentials = JSON.parse(File.read("credentials.json"))
  private_key = OpenSSL::PKey::RSA.new credentials["private_key"]
  payload = {
      :iss => credentials["client_email"],
      :sub => credentials["client_email"],
      :aud => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
      :iat => now_seconds,
      :exp => now_seconds+(60*60), # Maximum expiration time is one hour
      :uid => uid.to_s,
      :claims => {:premium_account => true}
    }
  JWT.encode(payload, private_key, 'RS256')
end

it looks like this in jwt.io

{
  "iss": "[email protected]",
  "sub": "[email protected]",
  "aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
  "iat": 1486824545,
  "exp": 1486828145,
  "uid": "4",
  "claims": {
     "premium_account": true
   }
}
3
Please, take a look at my firebase_id_token gem. It does exactly what you want.fschuindt

3 Answers

2
votes

It looks like the accepted answer found a way to link authentication from Firebase to Rails, but the original question seems to be asking how to link Rails authentication to Firebase (which is what I was trying to do).

To keep your authentication logic in Rails (ex: from Devise) and share it with Firebase, first get a Firebase server key as a .json file from your Service Accounts page in your project's settings.

You'll only need the private_key and client_id from this file, which I recommend storing as environment variables so they're not potentially leaked in source code.

Next, make a Plain ol' Ruby object (PORO) that will take in a User and spit out a JSON Web Token (JWT) that Firebase can understand:

class FirebaseToken
  def self.create_from_user(user)
    service_account_email = ENV["FIREBASE_CLIENT_EMAIL"]
    private_key = OpenSSL::PKey::RSA.new ENV["FIREBASE_PRIVATE_KEY"]

    claims = {
      isCool: "oh yeah"
    }

    now_seconds = Time.now.to_i
    payload = {
      iss: service_account_email,
      sub: service_account_email,
      aud: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
      iat: now_seconds,
      exp: now_seconds + (60*60), # Maximum expiration time is one hour
      uid: user.id,
      # a hash to pass to the client as JSON
      claims: claims
    }
    JWT.encode payload, private_key, "RS256"
  end
end

Now send this JWT to authenticated users through javascript in your application layout:

window.firebaseJWT = "#{FirebaseToken.create_from_user(current_user)}";

In your frontend code, you can now use this token to authenticate users:

firebase
  .auth()
  .signInWithCustomToken(window.firebaseJWT)
  .catch(error => {
    console.error(error);
  });

Remember to sign them out of firebase when they sign out of your application:

firebase
  .auth()
  .signOut()
  .then(() => {
    // Sign-out successful.
  })
  .catch(error => {
    console.error(error);
  });
1
votes

I found a better way to authenticate, I'm just sending the token that firebase gives you and verifying it on rails with the information I need and that's it.

0
votes

Check if your secret key is wrapped in double quotes and not single as they contain '\n' escape sequences. An auth/invalid-custom-token error is thrown if the secret key is not as specified in the documentation.