Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security Videosheise & c't: #heiseshow: EU Kids Act, Claude Opus 5.5, IT-Arbeitsmarkt(23.09.2026 um 15:45 Uhr)
YouTube Security VideosHow to quickly blur faces in video | Intel(23.09.2026 um 16:05 Uhr)
YouTube Security VideosGoogle Cloud Tech: Join Developers Building with Gemini(23.09.2026 um 15:00 Uhr)
YouTube Security VideosGoogle Workspace: Micro habits 🤏Macro results 🚀 #Shorts(23.09.2026 um 15:15 Uhr)
Windows Tipps & SecurityLenovo ThinkPad X9 15p Aura Edition Review ⭐(23.09.2026 um 15:58 Uhr)
YouTube Security Videosheise & c't: #heiseshow: EU Kids Act, Claude Opus 5.5, IT-Arbeitsmarkt(23.09.2026 um 15:45 Uhr)
YouTube Security VideosHow to quickly blur faces in video | Intel(23.09.2026 um 16:05 Uhr)
YouTube Security VideosGoogle Cloud Tech: Join Developers Building with Gemini(23.09.2026 um 15:00 Uhr)
YouTube Security VideosGoogle Workspace: Micro habits 🤏Macro results 🚀 #Shorts(23.09.2026 um 15:15 Uhr)
Windows Tipps & SecurityLenovo ThinkPad X9 15p Aura Edition Review ⭐(23.09.2026 um 15:58 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

One LINE Official Account, Multiple Tools: Webhook and Token Architecture

A completed WhatsApp Embedded Signup dialog does not mean that a customer is ready to send and receive messages. The browser flow may have finished while the backend still has one of these problems: The result was attached to the wrong…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

A completed WhatsApp Embedded Signup dialog does not mean that a customer is ready to send and receive messages.



The browser flow may have finished while the backend still has one of these problems:




  • The result was attached to the wrong tenant

  • The access token belongs to another app

  • The wrong WhatsApp Business Account was selected

  • The system user lacks the required access

  • The phone number is not ready

  • The app is not subscribed to WABA webhooks

  • The first webhook cannot be routed to the customer



A SaaS application should display Connected only after every backend gate has passed.






Model onboarding as a state machine



Avoid representing onboarding with one Boolean field such as:




connected: true






Use explicit states instead:




type WhatsAppOnboardingStatus =
| "started"
| "browser_finished"
| "token_validated"
| "waba_resolved"
| "access_verified"
| "phone_ready"
| "app_subscribed"
| "webhook_pending"
| "active"
| "verification_required"
| "failed";






A safe transition path is:




started

browser_finished

token_validated

waba_resolved

access_verified

phone_ready

app_subscribed

webhook_pending

active






Each transition should store evidence, not just a timestamp.






Define the acceptance gates



Meta's official Embedded Signup collection separates the browser flow from the Graph API work required afterward.
















































Gate Evidence Failure risk
Session correlation Tenant ID, state, configuration ID Assets attached to the wrong customer
Token validation App ID, scopes, expiry metadata Token exists but cannot manage the WABA
WABA resolution Exact WABA ID and business mapping First list result belongs to another customer
System-user access System-user ID and required task Later API operations fail
Phone readiness Phone-number ID and onboarding path WABA exists but messaging is unavailable
App subscription App appears in subscribed_apps
Meta receives messages but sends no webhook
Delivery proof One correctly routed webhook Configuration passes without real delivery


A tenant should remain unavailable until all required gates pass.






1. Correlate the browser result with a server session



Create the onboarding session on your server before opening Embedded Signup.



Store:




type OnboardingSession = {
id: string;
tenantId: string;
startedByUserId: string;
state: string;
configurationId: string;
onboardingPath: "cloud_api" | "coexistence";
expiresAt: string;
consumedAt?: string;
};






The state value should be:




  • Random

  • Single-use

  • Bound to one tenant

  • Bound to the initiating user

  • Short-lived

  • Validated on the server



When the browser reports completion:




  1. Look up the server session

  2. Verify the state

  3. Reject expired sessions

  4. Reject already-consumed sessions

  5. Verify that the current user can modify the tenant

  6. Mark the result as consumed atomically



Do not trust a tenant ID submitted by the browser if it can be derived from the authenticated server session.



If the Coexistence flow emits FINISH_WHATSAPP_BUSINESS_APP_ONBOARDING, treat it as evidence that the dialog finished—not evidence that the backend is active.






2. Keep credentials out of the browser



Credential exchange and Graph API verification belong on the server.



Do not place access tokens in:




  • Browser storage

  • Query strings

  • Client-side analytics

  • Error-reporting breadcrumbs

  • Support screenshots

  • Application logs

  • Database fields returned by public APIs



Store the credential in a secret manager or encrypted server-side store.



Persist only the audit metadata your application needs:




type TokenAuditMetadata = {
tenantId: string;
appId: string;
tokenType: string;
grantedScopes: string[];
issuedAt?: string;
expiresAt?: string;
lastValidatedAt: string;
};






A non-empty token is not proof that it belongs to the expected Meta app or has the permissions required for the selected WABA.






3. Resolve the intended WABA deterministically



The official collection exposes this endpoint for retrieving client WABAs:




GET /{business-id}/client_whatsapp_business_accounts






A business can return multiple WABAs. Never select the first array element:




// Unsafe
const waba = response.data[0];






Match against information captured for the current onboarding session:




function resolveWaba(
availableWabas: Array<{ id: string; name?: string }>,
expectedWabaId: string,
) {
const matches = availableWabas.filter(
(candidate) => candidate.id === expectedWabaId,
);

if (matches.length !== 1) {
throw new Error("expected_waba_not_resolved");
}

return matches[0];
}






Store the relationship explicitly:




type TenantWabaMapping = {
tenantId: string;
metaBusinessId: string;
wabaId: string;
sourceSessionId: string;
verifiedAt: string;
};






If no exact match exists, move the tenant to verification_required. Do not silently attach another WABA.






4. Verify system-user access



Meta documents the following endpoint for checking assigned users:




GET /{waba-id}/assigned_users?business={business-id}






Verify:




  • The expected system user is present

  • It belongs to the expected business

  • It has the task required by your integration

  • Your backend credential can perform the required WABA operations



Do not treat any returned system user as sufficient.



A system user added for a different operational role may not have the access your message or template workflow requires.






5. Branch by onboarding path



Standard Cloud API onboarding and WhatsApp Business App Coexistence are not the same phone-number path.






Standard Cloud API



Depending on the current onboarding contract, the backend may need to register the phone number and verify that its status permits messaging.






Coexistence



Coexistence uses a number already connected to the WhatsApp Business app.



Do not automatically repeat the standard registration operation. Instead, validate:




  • The number returned by the Coexistence flow

  • Its current status

  • Its WABA association

  • The intended synchronization behavior

  • The supported message-history boundary

  • Which surface owns each business operation



Keep the path in your tenant configuration:




type WhatsAppConnection = {
tenantId: string;
wabaId: string;
phoneNumberId: string;
mode: "cloud_api" | "coexistence";
status: "verification_required" | "active" | "failed";
};






This prevents later jobs from applying Cloud API assumptions to a Coexistence number.






6. Subscribe the app to the WABA



A valid WABA and phone number do not prove that webhook delivery is configured.



Meta's official collection uses:




POST /{waba-id}/subscribed_apps






Verify the result afterward:




GET /{waba-id}/subscribed_apps






The expected Meta app must appear in the returned subscription list.



Treat subscription as a separate gate:




async function verifyAppSubscription(
subscriptions: Array<{
whatsapp_business_api_data?: { id?: string };
}>,
expectedAppId: string,
) {
const subscribed = subscriptions.some(
(item) =>
item.whatsapp_business_api_data?.id === expectedAppId,
);

if (!subscribed) {
throw new Error("waba_app_subscription_missing");
}
}






Registration can succeed while subscription fails. Subscription can also exist while the selected phone number is not operational.



Do not combine these conditions into one status.






7. Prove one real webhook delivery



Configuration reads are necessary but not sufficient.



Before marking the tenant active, require one controlled webhook to reach a production-equivalent receiver.



The acceptance path should be:




Controlled WhatsApp message

Meta webhook delivery

Signature verification

WABA and phone-number lookup

Tenant resolution

Idempotency check

Durable event storage

Successful acknowledgement






For a Meta webhook, verify the signature against the raw request body before parsing or routing it.



An illustrative Node.js helper:




import { createHmac, timingSafeEqual } from "node:crypto";

function verifyMetaSignature(
rawBody: Buffer,
signatureHeader: string,
appSecret: string,
): boolean {
const prefix = "sha256=";

if (!signatureHeader.startsWith(prefix)) {
return false;
}

const expected = createHmac("sha256", appSecret)
.update(rawBody)
.digest();

const actual = Buffer.from(
signatureHeader.slice(prefix.length),
"hex",
);

return (
actual.length === expected.length &&
timingSafeEqual(actual, expected)
);
}






Never log the app secret, token, or complete sensitive webhook payload while debugging verification.






8. Route by provider assets, not browser state



The webhook receiver must resolve the tenant from trusted provider identifiers.



Relevant identifiers can include:




  • WABA ID

  • Phone-number ID

  • Meta app ID

  • Internal connection ID



Do not route a production webhook using a tenant ID previously stored in browser state without verifying its asset mapping.



A routing table can look like:




CREATE TABLE whatsapp_connections (
id VARCHAR(64) PRIMARY KEY,
tenant_id VARCHAR(64) NOT NULL,
meta_app_id VARCHAR(64) NOT NULL,
waba_id VARCHAR(64) NOT NULL,
phone_number_id VARCHAR(64) NOT NULL,
onboarding_mode VARCHAR(32) NOT NULL,
status VARCHAR(32) NOT NULL,
UNIQUE (meta_app_id, waba_id, phone_number_id)
);






If the mapping is missing or ambiguous:




  • Do not guess

  • Do not assign the event to the most recently onboarded tenant

  • Store it in a restricted reconciliation queue

  • Alert the integration owner

  • Keep the tenant out of active






9. Make webhook processing idempotent



Webhook delivery can be retried.



Choose an idempotency key from stable provider fields appropriate to the event type. For inbound messages, the provider message ID is usually part of that identity.



Store the event before running slow business logic:




Verify signature

Resolve tenant

Insert event if absent

Return success

Process asynchronously






A duplicate should not create:




  • Two conversations

  • Two automated replies

  • Two billing records

  • Two workflow executions

  • Two customer notifications



Operational readiness means that both the first delivery and a repeated delivery are safe.






10. Treat credit-line attachment as conditional



The official Embedded Signup collection includes credit-line sharing for provider-paid arrangements.



That does not make credit-line attachment a universal readiness gate.



Apply it only when:




  • Your business owns the billing relationship with Meta

  • Your partner model requires credit sharing

  • The customer WABA is expected to use that credit line



For customer-paid or other supported billing arrangements, document the appropriate billing check separately.



Do not block every tenant on a credit-line operation that does not belong to its commercial model.






11. Separate platform approval from operational readiness



This checklist does not prove:




  • Meta business eligibility

  • App Review approval

  • Advanced Access approval

  • Display-name approval

  • Number quality

  • Template approval

  • Messaging-limit tier

  • Policy compliance



Those are separate platform states.



Maintain them independently:




type WhatsAppReadiness = {
embeddedSignup: "pending" | "completed";
appReview: "unknown" | "approved" | "rejected";
wabaAccess: "unknown" | "verified" | "failed";
phoneStatus: "unknown" | "ready" | "failed";
webhookSubscription: "unknown" | "verified" | "failed";
deliveryTest: "pending" | "passed" | "failed";
};






A successful test tenant also does not prove that every customer configuration will work. Keep the verification process repeatable for every onboarding session.






Recommended failure states



Return actionable states instead of a generic connection error.





















































State Meaning Next action
session_mismatch Browser result cannot be correlated Restart onboarding
token_invalid Credential validation failed Repeat credential exchange
waba_not_resolved Expected WABA was not found Review business selection
system_user_missing Required access is absent Assign or repair system user
phone_not_ready Phone path is incomplete Follow path-specific recovery
subscription_missing App is not subscribed Subscribe and verify again
webhook_not_received No delivery proof exists Inspect webhook and routing
routing_ambiguous Asset mapping matches multiple tenants Stop and reconcile mappings


These states make support, retry behavior, and audit logs much easier to reason about.






Final post-onboarding checklist




  • [ ] Server-side onboarding session exists

  • [ ] State is valid, single-use, and tenant-bound

  • [ ] Browser result has been consumed exactly once

  • [ ] Credentials remain server-side

  • [ ] Token app, permissions, and expiry were validated

  • [ ] Exact WABA was resolved

  • [ ] WABA was not selected by array position

  • [ ] System-user access was verified

  • [ ] Standard and Coexistence paths are separated

  • [ ] Phone-number ID is stored

  • [ ] Phone status is ready for the selected path

  • [ ] Expected app appears in subscribed_apps

  • [ ] Billing checks match the commercial model

  • [ ] One real webhook reached the receiver

  • [ ] Webhook signature was verified

  • [ ] WABA and phone number resolved to one tenant

  • [ ] Duplicate delivery is safe

  • [ ] Failure states are recoverable

  • [ ] Only then is the tenant marked active



The browser dialog finishing is a user-interface milestone.



Operational readiness requires a verified chain from the server-side onboarding session to the correct WABA, system user, phone number, app subscription, tenant mapping, and real webhook delivery.






Official references








Originally published on UnifyPort.



This article was prepared with AI assistance for language and structure, then technically reviewed and verified by the author.

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-5695 | Arbitrary file upload vulnerability due to a lack of proper validation in…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick