Filtri Menu 0 0.00

Delphi + Firebase Cloud Messaging nel 2026: what really changed

Delphi + Firebase Cloud Messaging nel 2026: what really changed

In 2022 I published an article about how to integrate Firebase Cloud Messaging (FCM) into a Delphi 11 iOS app, focusing mainly on the client-side implementation and the initial push-notification setup:

👉 https://synaptica.info/2022/03/24/delphi-11-firebase-cloud-messaging-fcm-ios-app-push-notification-1/

Since then, several things have happened, but it is worth clarifying this right away:

on the app side, everything we implemented continues to work and remains largely compatible.

Over time, Embarcadero released new versions of the Firebase libraries for iOS and Android, improving integration and updating the underlying SDKs, but the client-side conceptual model has not changed:

  • registering with the FCM service

  • obtaining the device token

  • receiving notifications (foreground/background)

  • handling notification / data payloads

If you already have a Delphi app in production that receives push notifications correctly, you are not required to rewrite anything on the app side.


The real change: everything happens on the server side

The important change – and in many cases a mandatory one – concerns the Google APIs used to send messages.

In recent years, Google has:

  • deprecated and then discontinued the old FCM Legacy HTTP APIs

  • introduced the FCM HTTP v1 APIs, based on:

    • OAuth2

    • Service Accounts

    • signed JWTs (RS256)

    • versioned REST endpoints

This means that:

  • it is no longer possible (or soon will not be) to send push notifications using the classic Server Key

  • message delivery moves from a simple static header to a structured authentication flow

  • the server-side integration must be updated, especially if the backend is written in Delphi


Purpose of this article

In this article we will therefore cover:

  • what really changes when moving from FCM Legacy to FCM HTTP v1

  • why existing Delphi apps continue to work

  • how to correctly implement push delivery from a Delphi backend

  • how to securely use a Firebase service account

  • and how to leverage existing libraries (such as Kastri) to avoid reinventing OAuth2 and JWT

FCM Legacy vs FCM HTTP v1: what really changes (and why it is more secure)

If on the app side the FCM integration has remained largely compatible with the past, on the backend side the story is different: the key change is the evolution of the sending APIs. Historically, many Delphi projects (including mine) used FCM’s Legacy HTTP APIs, based on a simple server key. Today, the correct approach is the FCM HTTP v1 API, which introduces a far more robust authentication and authorization model.

1) Endpoint and payload format

With the legacy APIs, notifications were sent to this endpoint:

POST https://fcm.googleapis.com/fcm/send

HTTP v1 uses a versioned endpoint that is explicitly tied to the Project ID:

POST https://fcm.googleapis.com/v1/projects/<PROJECT_ID>/messages:send

The payload structure changes as well: with legacy it was common to use a field like to (token or topic), while v1 uses a message object and clearer fields such as token or topic. In practice, the message becomes more structured and less ambiguous.

2) Authentication: from a “static key” to OAuth2 (JWT + Service Account)

The most critical part of the migration is authentication.

Legacy: Server Key (static)

With legacy, it was enough to add a static key in the header:

Authorization: key=AAAA....

It’s convenient, but it comes with a downside: the key is a single long-lived secret that often ends up in the wrong places (unprotected configs, logs, backups, private-but-shared repositories, etc.). Also, the key is not contextual: anyone who has it can potentially send notifications to everything tied to that project.

HTTP v1: OAuth2 Bearer Token (generated and expiring)

With HTTP v1, the backend no longer uses a static key. Instead, it obtains an OAuth2 access token using a Service Account (private key) and a signed JWT. Sending then happens with:

Authorization: Bearer ya29....

This token:

  • has a short lifetime (typically ~1 hour)
  • is revocable and easier to rotate
  • is not an “eternal” key that remains valid until someone notices
  • is obtained through a standard flow (OAuth2) controlled by Google

3) Security: why HTTP v1 is a real step forward

From a security perspective, HTTP v1 is simply better by design:

  • It reduces the impact of leaks: if an OAuth2 token ends up in a log or dump, it expires quickly. A legacy server key, on the other hand, remains valid until it is manually rotated (which often never happens).
  • It aligns push sending with IAM and project policies: by using service accounts and OAuth2, you enter the Google Cloud paradigm, where permissions can be managed more rigorously.
  • Clear separation between client and server: the service account and private key must live only on the backend; the app should not know anything about them (and must never include secrets).
  • Better auditing practices: it becomes natural to log and control token generation, rotation, and permissions, rather than relying on a single static key.

4) Practical impact on a Delphi backend

For those building backends in Delphi, the migration boils down to a few “building blocks”:

  • JWT generation (RS256)
  • JWT → access token exchange (OAuth2 token endpoint)
  • POST to /v1/projects/.../messages:send
  • access token caching (to avoid regenerating it for every send)

This is where two very helpful components come into play:

  • Kastri (Delphi Worlds) with DW.FCMSender: it implements the full FCM HTTP v1 flow (JWT + OAuth2 + sending) and also builds coherent payloads for Android and APNs.
  • DelphiOpenSsl (Grijjy): used by Kastri for RSA/SHA256 (RS256) signing via OpenSSL, avoiding the need to write crypto code by hand.

In other words: you can do everything “by hand” with Indy/OpenSSL, but using Kastri + DelphiOpenSsl saves a lot of time, reduces the bug surface area, and gets you closer to an implementation that has already been used in real-world contexts.

5) Operational best practices (quick tips)

  • Never commit the service account JSON into your repository or bundle it in the app: it must live only on the server (with restrictive file permissions).
  • Access token caching: generate the token only when needed and reuse it until it expires (Kastri handles this via TBearerToken).
  • Key rotation: if you have company policies, rotate the service-account key periodically and update the backend configuration.
  • Topic vs token: use topics for broadcast (e.g., “low stock” for a warehouse) and tokens for transactional notifications (e.g., “shipment sent” per user).

In the rest of this article we will see a practical Delphi example that sends a push to a single device via token, using Kastri (DW.FCMSender) and DelphiOpenSsl (Grijjy).

Firebase Service Account: what it is and why it has become essential

With the introduction of the FCM HTTP v1 APIs, Google significantly changed the authentication model for sending push notifications. The old mechanism based on a static server key was gradually phased out in favor of a more modern and secure approach, based on OAuth2 and Service Accounts.

In this new model, the backend (in our case written in Delphi) no longer uses a “fixed” key to authenticate to Firebase. Instead, it identifies itself through a service account, i.e. a technical account associated with the Google/Firebase project.

What is a Service Account?

A service account is a server-to-server identity used by backend services to authenticate against Google APIs. It does not represent a human user, but a service or application.

In the context of Firebase Cloud Messaging:

  • the service account identifies the authorized backend that can send push notifications
  • it is tied to the Firebase project (not to an individual iOS/Android app)
  • it allows obtaining temporary OAuth2 access tokens
  • it enables the use of the FCM HTTP v1 APIs

In practice, the service account is the new “official passport” for talking to Google when sending push notifications.

Why did Google introduce this mechanism?

The primary reason is security. The old server keys were:

  • static
  • often reused for years
  • hard to contain in case of leaks

With OAuth2 and service accounts instead:

  • tokens have a short lifetime
  • private keys remain server-side only
  • access fits into the Google Cloud IAM model
  • the overall attack surface is reduced

This approach is now the standard for modern Google APIs, and FCM is no exception.

How to obtain the firebase-service-account.json file

The service account JSON file is generated directly from the Firebase Console (or Google Cloud Console) and is created once.

  1. Open the Firebase Console and select the correct project
  2. Click the gear icon (Project settings)
  3. Open the Service accounts section
  4. Go to the Firebase Admin SDK tab
  5. Click Generate new private key
  6. Confirm and download the JSON file

Firebase will download a file with a name similar to:

my-project-firebase-adminsdk-xxxx.json

This file is what we will use on the backend as firebase-service-account.json.


What the JSON file contains

The file contains sensitive information, including:

  • project_id – the Firebase project identifier
  • client_email – the service account identity
  • private_key – the private key used to sign JWTs
  • token_uri – Google’s OAuth2 token endpoint

It is important to underline that:

  • this file must never be included in the app
  • it must never be committed to a Git repository
  • it must live only on the backend, with restrictive file permissions

How it is used by a Delphi backend

On a Delphi backend, the service account is used to:

  • generate a signed JWT (RS256)
  • exchange it for an OAuth2 access token
  • send authenticated requests to the FCM HTTP v1 APIs

Libraries such as Kastri (via DW.FCMSender) and DelphiOpenSsl (Grijjy) make this process dramatically easier by encapsulating the complexity of JWT, OAuth2, and cryptographic signing.

This keeps the Delphi code clean and readable, while the authentication follows Google’s recommended best practices.

Practical example: sending a push notification to a specific device with Delphi

Let’s conclude with a real example of sending a push notification to a specific device, using:

  • Delphi as the backend language
  • Kastri (TFCMSender / TFCMMessage)
  • Firebase Cloud Messaging HTTP v1
  • authentication through a Service Account

In this example, the service account is loaded directly from a JSON string (for example read from a file, environment variable, or vault), and the notification is sent to two distinct devices using their FCM registration tokens.



uses DW.FCMSender;
.
.
.
.
.

procedure TForm6.Button1Click(Sender: TObject);
var
  LSender: TFCMSender;
  Msg: TFCMMessage;
  Payload: string;
begin
  // Instance of the FCM sender (handles OAuth2, JWT, and HTTP v1 posting)
  LSender := TFCMSender.Create;
  try
    // Load the service account from JSON (string)
    // Alternative: LoadServiceAccount('path/file.json')
    if not LSender.ServiceAccount.Parse(c_service_account_xtumble) then
      raise Exception.Create('Invalid service account JSON');

    // Create the FCM message
    Msg := TFCMMessage.Create;
    try
      // Notification title and body
      Msg.Title := edTitle.Text;
      Msg.Body  := edBody.Text;

      // Additional data (MUST be valid JSON with string properties only)
      // It will be delivered under the "data" payload in the app
      Msg.Data := Format(
        '{"message_id":"%s","type":"shipment"}',
        ['xtumble']
      );

      // Build the payload for the first device (specific token)
      Payload := Msg.GetTokenPayload(device_token_simo);

      // Send the notification
      if LSender.Post(Payload) then
      begin
        // Push sent successfully to the first device
      end;

      // Send the same notification to a second device
      Payload := Msg.GetTokenPayload(device_token_ivan);

      if LSender.Post(Payload) then
      begin
        // Push sent successfully to the second device
      end;

    finally
      Msg.Free;
    end;
  finally
    LSender.Free;
  end;
end;
  

What happens behind the scenes

Behind just a few lines of code, TFCMSender automatically performs:

  • signed JWT generation (RS256)
  • JWT → OAuth2 access token exchange
  • token caching until expiration
  • calling the FCM HTTP v1 APIs

The FCM token passed to GetTokenPayload uniquely determines the recipient device: even if the Firebase project contains multiple apps (iOS / Android), the message will be delivered only to that specific device.

Final notes

This approach is the modern, recommended way to integrate Firebase Cloud Messaging into a Delphi backend:

  • no legacy static keys
  • standard Google OAuth2 authentication
  • maximum compatibility with existing client apps
  • clean and maintainable code

Once the service account is correctly set up, sending push notifications becomes an implementation detail, allowing you to focus on application logic (events, workflows, and targeted notifications).

Share:

Subscribe to newsletter

Subscribe to our newsletter to receive early discount offers, updates and new products info.
Top