Skip to content

Delivery providers & Web Push

This document covers two gNotify capabilities added on top of the core notification pipeline:

  1. Delivery providers (Furnizori) — configurable per-channel backends for sending email and SMS, chosen individually for each channel of a notification.
  2. Web Push — real browser push notifications over the open Web Push protocol (VAPID), with no Firebase / FCM account required.

A Provider is a persisted, admin-managed delivery backend. Instead of hard-wiring a single SMTP server or one SMS gateway, operators register any number of providers and pick one per channel when sending a notification (“this email via the first SMTP, that email via the second; this SMS via AWS SNS, that one via the third-party gateway”).

Each provider has:

FieldMeaning
cheieunique key, referenced at send time (e.g. gstackgov, aws-sns)
numehuman label shown in the UI
tipprovider type — one of the values below
configJSON configuration (shape depends on tip)
activonly active providers are selectable when sending
tipChannelconfig keys
EMAIL_SMTPemailfrom (required). Optional host, port, username, password, starttls for a dedicated SMTP server. If host is omitted, the shared spring.mail.* transport (e.g. Amazon SES) is reused and only the From identity differs.
SMS_SNSsmsregion (default eu-north-1), accessKey, secretKey, optional senderId, smsType (Transactional/Promotional). Sends via Amazon SNS Publish.
SMS_HTTPsmsurl (required), method (default POST), contentType (form/json), headers (JSON object), params (JSON object; values may contain {to} and {message} placeholders). Optional successField/idField/errorField to parse a JSON response (so gateways that return HTTP 200 on failure — like TextBelt — are handled correctly). Generic third-party HTTP gateway.

On the Trimite notificare (notify console) screen, each channel row has a Furnizor dropdown:

  • email channels list the active EMAIL_SMTP providers;
  • sms channels list the active SMS_SNS + SMS_HTTP providers;
  • push / telegram have no provider (single built-in transport).

Leaving the dropdown on (implicit) keeps the previous behaviour:

  • email → default SES From (gnotify.mail.default-sender);
  • sms → the fallback provider from gnotify.sms.provider (stub/twilio/textbelt).

The chosen provider key travels end-to-end: ChannelSpec.provider → NotificationJob.provider → resolved at delivery by ProviderRegistry inside EmailChannelSender / SmsChannelSender.

Administrare → Furnizori (/providers, ADMIN only) is a type-aware CRUD screen: selecting a type shows the right config fields, SMS_HTTP exposes free-form headers/params JSON, and the config is serialized to the provider’s config column on save.

Backend endpoint: /api/providers (standard JHipster resource, locked to ROLE_ADMIN).

On startup, ProviderSeeder materializes the delivery backends that were previously configured only through properties, so they appear on the Furnizori page and in the per-channel selector. Upsert by key — existing rows (including your UI edits) are never overwritten.

KeySeeded fromTypeActiveNotes
gstackgovgnotify.mail.senders.gstackgovEMAIL_SMTPyesSES From identity; shares the SES transport
gstackdevgnotify.mail.senders.gstackdevEMAIL_SMTPyesSES From identity; shares the SES transport
textbeltgnotify.sms.textbeltSMS_HTTPyesthe “third-party server”; success-field parsing wired
aws-sns(template)SMS_SNSnoplaceholder — fill in credentials in the UI and enable

The two SES senders differ only by From; they are two identities on one SES server, not two separate SMTP servers. To add a genuinely separate second SMTP server, create a new EMAIL_SMTP provider on the Furnizori page and fill in its own host/port/username/password.

  • AWS SNS: edit the seeded aws-sns row → set region, accessKey, secretKey → tick Activ. The AWS SDK v2 (software.amazon.awssdk:sns, url-connection HTTP client) is already bundled.
  • A second/other SMTP: add an EMAIL_SMTP provider with host/port/username/password and a from.
  • Another HTTP SMS gateway: add an SMS_HTTP provider with the gateway’s url, params (using {to}/{message}), and — if it returns a JSON body — successField.

How it works — and why there is no Firebase

Section titled “How it works — and why there is no Firebase”

gNotify sends browser notifications with the standard Web Push protocol using VAPID (Voluntary Application Server Identification), via the nl.martijndwars:web-push Java library. There is no Firebase project, no FCM app, and no server key:

  • Each browser subscribes to its own push service. gNotify posts the encrypted payload directly there.
  • Firefox subscriptions terminate on Mozilla’s push service (updates.push.services.mozilla.com); Chrome/Edge subscriptions terminate on Google’s endpoints (fcm.googleapis.com/wp/…). The latter is just the standardized Web Push transport — it does not require a Firebase account.
  • The only server-side secret is the VAPID keypair, owned by the deployment.

Set on the backend (env / gnotify.push.vapid.*):

GNOTIFY_PUSH_VAPID_PUBLIC_KEY=<base64url public key>
GNOTIFY_PUSH_VAPID_PRIVATE_KEY=<base64url private key>
GNOTIFY_PUSH_VAPID_SUBJECT=mailto:admin@example

Generate a keypair with npx web-push generate-vapid-keys --json. If the keys are unset, the PUSH channel silently falls back to a stub (logs “sent”, delivers nothing) — the provider_message_id will read fake-push-… instead of webpush-…. On startup the log prints Web Push (VAPID) not configured … when the keys are missing.

PUSH is keyed by the recipient’s identifier (IDNP/IDNO), not by an address — a person can have several devices. When a citizen enables push from the portal (Contul meu), the browser subscription is stored as a PushSubscription under their identifier, and a PUSH CanalContact (value = identifier) is auto-created so the send-by-IDNP flow can route to it.

  • Public key for the browser: GET /api/registry/push-public-key (authenticated).
  • Subscribe: POST /api/registry/me/push-subscription.
  • Sending to PUSH fans out to all the identifier’s subscriptions; a 404/410 from the push service means the subscription expired and it is auto-removed.
  1. Subscribe a browser — open the portal (https://portalgnotify.gstack.esempla.systems), log in as a directory user, go to Contul meu, enable push and Allow the browser prompt. Note the user’s IDNP.
  2. Send — from the admin UI (https://gnotify.gstack.esempla.systems):
    • Trimite după ID / grup → enter the IDNP → send; or
    • Trimite notificare → Fără șablon → add a push channel with recipient = the IDNP.
  3. Verify — the OS notification appears (even with the portal tab closed, as long as the browser runs). Server-side, the Livrare row flips to PUSH / SENT with a provider_message_id starting webpush- (a real send; fake-push- would mean the stub).

Requirements: HTTPS, the user must click Allow, desktop Chrome/Edge/Firefox (Safari/iOS is restricted).


All of the above lives in separately-named, hand-written classes so jhipster jdl never clobbers it: service/channel/{ProviderRegistry,ProviderSeeder,EmailChannelSender,SmsChannelSender,WebPushService,PushChannelSender}, repository/lookup/*, web/rest/RegistryPushResource, and the standalone app/providers/ Angular screen. The Provider entity itself is generated from registry-providers.jdl (@skipClient, backend-only) — re-run jhipster jdl registry-providers.jdl --force after JDL changes, then keep the security matcher for /api/providers/**.