Does anybody have a experience connecting elixir with Firebase?

Hello community,

My goal is to send a message from elixir back end server to an android application. I came to the conclusion that google’s firebase is currently the best option.
My problem is related to authorization and sending a message to the console Firebase.
Having downloaded my secret json from console.firebase.google.com I tried to link elixir with the platform. The problem here is that I struggle dealing with that jwt token - (FunctionClauseError) no function clause matching in :jose_jwk.from/1

defmodule App.Firebase do
  @moduledoc """
  Module for sending messages through Firebase Cloud Messaging.
  """

  @fcm_url "https://fcm.googleapis.com/v1/projects/censored/messages:send"
  @service_account_path "priv/firebase/serviceAccountKey.json"

  alias JOSE.{JWK, JWS, JWT}

  def send_message(token, message) do
    service_account = Jason.decode!(File.read!(@service_account_path))
    jwt = generate_jwt(service_account)

    IO.puts("JWT generated successfully.")

    headers = [
      {"Authorization", "Bearer #{jwt}"},
      {"Content-Type", "application/json"}
    ]

    body = %{
      message: %{
        token: token,
        notification: %{
          title: "Notification Title",
          body: message
        }
      }
    }
    |> Jason.encode!()

    IO.puts("Sending message to FCM...")

    case HTTPoison.post(@fcm_url, body, headers) do
      {:ok, %HTTPoison.Response{status_code: 200, body: response_body}} ->
        IO.puts("Message sent successfully!")
        IO.inspect(response_body)

      {:ok, %HTTPoison.Response{status_code: status_code, body: response_body}} ->
        IO.puts("Failed to send message. Status code: #{status_code}")
        IO.inspect(re my problem is withsponse_body)

      {:error, %HTTPoison.Error{reason: reason}} ->
        IO.puts("Error occurred while sending message")
        IO.inspect(reason)
    end
  end

  defp generate_jwt(service_account) do
    iat = :os.system_time(:second)
    exp = iat + 3600 # 1 hour expiration

    claims = %{
      "iss" => service_account["client_email"],
      "sub" => service_account["client_email"],
      "aud" => "https://oauth2.googleapis.com/token",
      "iat" => iat,
      "exp" => exp,
      "scope" => "https://www.googleapis.com/auth/firebase.messaging"
    }

    private_key = service_account["private_key"]
    jwk = JWK.from_pem(private_key)
    {_, jwt} = claims |> JWT.sign(jwk) |> JWS.compact

    jwt
  end
end

Have someone encountered with this before? Is this the right way anyway or I am at a dead end

I’m also trying to connect to Firebase, but I’m trying to use Pigeon V2, which uses Goth for authentication. In addition to using the latest Pigeon I also picked up PR 235 to get some Goth related improvements. My authentication seems to be going fine, my problem is getting my sender_id to match. But you might find that Goth is a useful way to get your token working, if your struggles with JOSE don’t get you anywhere.

And if you get to having a SENDER_ID_MISMATCH and figure out a meaningful way to debug it, please share!

1 Like

I managed to connect the apps via One Signal - for my purpose it works great.

defmodule App.OneSignal do
  @moduledoc """
  Module for sending messages through OneSignal.
  """

  @onesignal_url "https://onesignal.com/api/v1/notifications"
  @api_key "your_api_key"
  @app_id "your_app_id"

  def send_message(player_id, message) do
    headers = [
      {"Content-Type", "application/json"},
      {"Authorization", "Basic #{@api_key}"}
    ]

    body = %{
      app_id: @app_id,
      contents: %{en: message},
      include_player_ids: [player_id]
    }
    |> Jason.encode!()

    case HTTPoison.post(@onesignal_url, body, headers) do
      {:ok, %HTTPoison.Response{status_code: 200, body: response_body}} ->
        IO.puts("Message sent successfully!")
        IO.inspect(response_body)

      {:ok, %HTTPoison.Response{status_code: status_code, body: response_body}} ->
        IO.puts("Failed to send message. Status code: #{status_code}")
        IO.inspect(response_body)

      {:error, %HTTPoison.Error{reason: reason}} ->
        IO.puts("Error occurred while sending message")
        IO.inspect(reason)
    end
  end
end

Your authorization is still set to Basic. Is that going to work with the new FCM V1 API, or is your code going to stop working when the Legacy FCM notifications stop working on July 22nd?

Yes, the Basic authorization will not work anymore soon.
Did you manage to connect Firebase with Pigeon V2? Over the past few days I’ve tried using Goth, along with google_api_fcm (API Reference — google_api_fcm v0.12.0), however I still get errors that I can’t get past.

With Pigeon V2 and it’s PR 235 for some Goth improvements I have now been able to communicate with my app using API V1. I’m now in the process of updating the app to accept the new V1 format.

Perhaps if you post your errors I might recognize them.

My biggest problem turned out to be that the app was using a project id from a former developer and not the one from the current firebase owner. Once I got those two synchronized I got past my sender_id_mismatch and got a response.