Security & Permissions
The webhook URL is the only secret. The module validates it before every request, pins TLS, refuses redirects and redacts it from every log entry.
Microsoft Teams Notifications does not create database tables, does not register a bot and does not request Microsoft Graph permissions. Delivery uses a single HTTPS POST to the URL generated by Teams. That makes the URL the entire security boundary: anyone who holds it can post to the channel, and anyone who can read it in a log has the same power.
Webhook URL validation
Every request — including the connection test — validates the URL before cURL is called. The provider webhook and any per-rule Webhook Override go through the same checks:
- HTTPS only. Plain HTTP is rejected.
- No credentials. A URL containing
user:pass@is rejected. - No fragment. A URL containing
#is rejected. - Length and shape. The URL must parse and be 2,048 characters or fewer.
- Host allowlist. Teams connector hosts and Power Automate Workflows hosts are accepted. Anything else requires Allow Custom Host.
- Path shape. A legacy URL must contain
/webhook/or/webhookb2/and/IncomingWebhook/. A Workflows URL must contain/workflows/,/triggers/and/paths/invoke. - Signature rules. A legacy URL must not carry a query string. A Workflows URL must carry its signature query string.
- Port. Only 443 is accepted unless a custom host is allowed.
The optional Card Icon URL is validated separately: it must be a valid HTTPS URL of 512 characters or fewer. Teams fetches the image from the client, so the URL must be publicly reachable; the module does not proxy or cache it.
| Host pattern | Purpose |
|---|---|
outlook.office.com, outlook.office365.com | Legacy Office 365 Incoming Webhook. |
*.webhook.office.com, *.webhook.office365.com | Legacy tenant-scoped connectors. |
*.logic.azure.com | Power Automate Workflows trigger. |
*.api.powerplatform.com | Power Platform Workflows trigger. |
| Any HTTPS host | Only when Allow Custom Host is ticked, for national clouds or a trusted proxy. |
Transport hardening
| cURL option | Value | Why |
|---|---|---|
CURLOPT_FOLLOWLOCATION | false | A redirect cannot send the token or signature to another host. |
CURLOPT_SSL_VERIFYPEER | true | The server certificate must chain to a trusted CA. |
CURLOPT_SSL_VERIFYHOST | 2 | The certificate must match the webhook host. |
CURLOPT_PROTOCOLS | CURLPROTO_HTTPS | No protocol downgrade. |
CURLOPT_CONNECTTIMEOUT | min(5, timeout) | A dead host cannot hold a PHP worker for the full timeout. |
CURLOPT_TIMEOUT | 1–30 seconds | Bounds the whole request. |
Log redaction
The module log records the request URL, payload, HTTP status and an excerpt of the response. The URL is rewritten before it reaches the log:
| Real URL | Logged URL |
|---|---|
https://contoso.webhook.office.com/webhookb2/abc@def/IncomingWebhook/123/SECRET | https://contoso.webhook.office.com/webhookb2/abc@def/IncomingWebhook/123/[redacted] |
https://prod-12.westeurope.logic.azure.com/workflows/abc/triggers/manual/paths/invoke?api-version=1&sig=SECRET | https://prod-12.westeurope.logic.azure.com/workflows/abc/triggers/manual/paths/[redacted]?[redacted] |
The payload is logged so you can see what was sent, but the token and signature never are. Error text returned by Teams is truncated to 200 characters before logging or display.
Data handling
- No module tables. Provider settings live in
tblnotificationproviders.settings; rules live intblnotificationrules. - No client data is stored by the module. Notification content is built in memory for each delivery.
- No telemetry. The only outbound request is the HTTPS POST to the configured webhook.
- No theme or core edits. Everything ships inside
modules/notifications/MicrosoftTeams/. - Payload logging is opt-in. Module logging is controlled by WHMCS and should be off outside troubleshooting.
Teams-side permissions
The module needs no Microsoft Graph permission, no app registration and no admin consent. Access is governed by the webhook itself:
- Workflows — the workflow is owned by the user who created it. Keep at least one co-owner so it is not orphaned when that user leaves.
- Legacy connector — any member who can configure connectors for the channel can create or remove the webhook.
- Tenant policy — an administrator can disable connectors or Workflows across the tenant. The module then receives HTTP 401 or 403 and reports it clearly.
- Channel membership — the webhook posts as the workflow or connector identity, not as the administrator.
When to allow a custom host
Allow Custom Host is off by default. Enable it only when:
- Your tenant uses a national or sovereign cloud domain that is not in the standard allowlist.
- Outbound traffic passes through a trusted reverse proxy that rewrites the webhook host.
HTTPS, certificate verification and the no-redirect policy still apply. Do not enable it to make an unrelated URL work.
Security checklist
- Use a dedicated Teams channel for WHMCS alerts, not a general chat.
- Restrict who can edit the provider settings in WHMCS.
- Keep module logging off except while diagnosing.
- Rotate the workflow or webhook if a staff member with access leaves.
- Review the module log for HTTP 401 and 403 responses, which can indicate a revoked URL.
- Keep WHMCS, PHP and the CA bundle up to date.