Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
416 changes: 179 additions & 237 deletions README.md

Large diffs are not rendered by default.

27 changes: 23 additions & 4 deletions TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,29 @@ Azure Front Door is an [optional manual multi-region design](docs/FRONTDOOR.md),
enabled by the single-region setup script. Its additional readiness endpoint is not shipped in
the release packages, and regional failover does not change the SendOtp contract.

A provider-agnostic **OTP-delivery Azure Function** sample, implemented across multiple languages.
Each language folder is a self-contained implementation of the **same design and the same
[contract](docs/CONTRACT.md)**: one engine, drop-in provider adapters, env-provisioned config, and
secrets in Key Vault.
Use this reference when developing or customizing the endpoint. Each language folder implements
the same [HTTP contract](docs/CONTRACT.md), with provider-specific adapters, environment settings,
and managed-identity access to credentials.

## Single-region architecture

![Single-region External Phone Provider architecture](docs/images/single-region-architecture.png)

Microsoft Entra ID sends an authenticated, encrypted request. Easy Auth checks the caller,
then the Function decrypts the request using its Key Vault-backed key. For a live request, the
selected adapter submits the message and returns success after provider acceptance, not after
confirmed recipient delivery. Application Insights collects the available request and log telemetry.

The diagram is an overview, not a portal screenshot. East US is illustrative; select a region
with capacity and quota for your hosting plan. The provider-secret path applies to API-key
integrations; OAuth integrations use managed-identity token exchange instead. Credential caching
depends on the [selected cache settings](docs/CONTRACT.md#credential-caching-and-refresh), and
[log formats vary by runtime](docs/MONITORING.md#runtime-specific-log-discovery).

An authorized, valid encrypted **evaluation request (`mode: 2`)** returns the matching nonce
without submitting a message to the provider. When caching is enabled, credential preparation
can run independently; it never sends an OTP. Authentication failures can return **401 or 403**,
neither of which is a successful evaluation.

## Implementations

Expand Down
32 changes: 15 additions & 17 deletions docs/APPLICATION-INSIGHTS.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
# Application Insights for the External Phone Provider

Application Insights is the application-performance feature of Azure Monitor. It helps explain
which Function invocations ran, how long they took, and what the application reported while
processing them. It is not a delivery receipt service, an audit ledger, or a replacement for
Azure resource health and platform metrics.

This guide covers telemetry collection and interpretation for the repository's **JavaScript,
.NET isolated, and Python** implementations, in one region or multiple regions. It does not
deploy resources, enable additional instrumentation, or configure alert rules, action groups,
or availability tests. Use the [setup guide](../setup/docs/README.md) for deployment.
Azure Front Door is optional; the Function telemetry described here also applies without it.

**First-time operator:** locate `resources.applicationInsights` and `resources.logAnalytics` in
the [saved deployment summary](../setup/docs/README.md#read-the-deployment-summary). Verify collection
here, then follow the [runtime-specific queries](MONITORING.md#runtime-specific-log-discovery)
and [alert setup](MONITORING.md#5-set-up-notifications-and-alert-rules). Authorized evaluation/live
testing must use the [customer test handoff](ONBOARDING.md#validate-the-deployed-endpoint);
deployment does not provide a customer token for the Microsoft caller.
Application Insights helps you see which requests ran, how long they took, and what the Function
reported along the way. It does **not** confirm that an SMS or voice call reached the recipient.

**Looking for your logs?** Start with the [monitoring guide](MONITORING.md). It shows how to find
the resources from your deployment summary, run queries, and create alerts.

This reference explains how collection works for **JavaScript, .NET isolated, and Python**,
what each signal means, and why data may be missing. Start with
[single-region configuration](#single-region-configuration) to check a guided deployment;
use the remaining sections for runtime differences or a multi-region design.
You do not need Azure Front Door for Function telemetry.

These instructions do not deploy resources or enable extra instrumentation, alerts, or availability
tests. Use the [setup guide](../setup/docs/README.md) for deployment and the
[approved test handoff](ONBOARDING.md#validate-the-deployed-endpoint) to generate test traffic.

## How telemetry reaches Application Insights

Expand Down
74 changes: 63 additions & 11 deletions docs/MONITORING.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
# Monitoring setup and sample queries

Use this guide to operate a **single-region Azure Function** or **multiple regional
Functions**. Azure Front Door is optional; it adds an edge monitoring layer and does
not replace Function, credential, or provider monitoring.
Use this guide to find your endpoint's logs and set up alerts. Guided setup creates Application
Insights and a Log Analytics workspace, but **you still need to configure and test notifications**.

These are operator-run examples, not an automatic deployment of monitors. Review
permissions, privacy, charges, and alert routing before creating anything. All
thresholds and intervals below are **starting points, not SLAs**. This guide does not
change deployed diagnostics, sampling, retention, or alert settings.
For your first single-region deployment:

For the first deployment, finish the [customer acceptance checks](ONBOARDING.md#validate-the-deployed-endpoint)
with the authorized test operator. Read [Application Insights](APPLICATION-INSIGHTS.md) for
collection/identity details, then use this guide to establish queries and alerts. You do not
need Front Door, a Workbook, or a new exporter merely to locate your existing single-region logs.
1. [Find the monitoring resources and confirm data is arriving](#1-record-the-deployment-and-confirm-telemetry)
after an [authorized test](ONBOARDING.md#validate-the-deployed-endpoint).
2. Check [requests, failures, and latency](#2-single-region-function-queries), then use
[the log format for your runtime](#runtime-specific-log-discovery).
3. [Set up and test alerts](#5-set-up-notifications-and-alert-rules), including certificate
reminders and cost ownership.

The multi-region queries, shared Workbook, and Front Door sections are optional.
You do not need them to find single-region logs. For collection and identity details, see
[Application Insights](APPLICATION-INSIGHTS.md).

These examples do not create monitors automatically. Review permissions, privacy, and charges
before applying them. Thresholds and intervals are **starting points, not SLAs**; tune them to
your deployment.

## 1. Record the deployment and confirm telemetry

Expand All @@ -37,6 +43,23 @@ methods can differ. The current setup template requests 30-day retention; confir
effective workspace/table retention and organizational policy before changing it.
It does not create the action groups, Workbooks, or alert rules described here.

### Find the linked resources in the portal

1. Open the Function App named in your deployment summary.
2. Select **Monitoring > Application Insights**. Follow the linked resource name; do not
select **Change your resource** or **Apply** just to view your logs.

![Function App Monitoring menu with Application Insights selected and its connected resource link](images/onboarding/monitoring-link.png)

3. On that Application Insights resource's **Overview**, find **Logs workspace** and open
the linked workspace. Then select **Logs** in the workspace to use the queries below.

![Application Insights Overview showing the Logs workspace link with account and connection details redacted](images/onboarding/monitoring-workspace.png)

These are real portal views of an existing test deployment, with identifying information and
connection details permanently hidden. Resource names, regions, and portal layouts may differ.
The links show where to navigate; they do not prove that test traffic or delivery succeeded.

In the Azure portal, inspect each Function's Application Insights association,
monitoring settings, managed-identity ingestion authorization, and diagnostic
destinations. Do not expose connection strings or credential values. Application
Expand Down Expand Up @@ -69,6 +92,35 @@ necessary resource/workspace access; saving a Workbook does not grant its reader
access to its data. Cross-workspace alert evaluation also needs access to each
workspace under the rule's configured identity.

### Run a first query and read the results

1. In your workspace's **Logs**, close any welcome or Queries hub dialog. If the portal opens
in **Agent** mode, switch it off for this walkthrough, then choose **KQL mode**.
2. Paste the query below. Replace `<function-role-name>` with the `AppRoleName` you verified
using the discovery query above. If a workspace contains multiple Application Insights
components with that same role name, also filter by your component's `_ResourceId`.
3. Select **Run**, then read the **Results** grid. Adjust `ago(48h)` to include the time of
your authorized test; this query sets its own time range.

```kusto
AppRequests
| where TimeGenerated >= ago(48h)
| where AppRoleName == "<function-role-name>" and Name == "SendOtp"
| summarize StoredRows = count() by ResultCode, Success
| order by ResultCode asc
```

![Workspace Logs in KQL mode with a scoped request-status query, Run button, and one aggregate result row](images/onboarding/logs-query-results.png)

*This real query returned 11 stored rows with result code 200 and `Success=true` in the example
window. The role and workspace names are hidden. The query shows counts only: no phone numbers,
message bodies, credentials, or raw trace messages. Your result counts will differ.*

`StoredRows` counts stored telemetry records, not delivered messages or necessarily all requests.
Sampling can change the count, and evaluation and live calls can both return HTTP 200. No rows
means no matching records in the selected scope/time range; it is not proof that nothing failed.
Use the scoped and sampling-aware queries below for ongoing monitoring.

## 2. Single-region Function queries

### Requests, failures, and latency
Expand Down
16 changes: 12 additions & 4 deletions docs/MULTI-PROVIDER-IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ The sample still ships with one provider per deployment and **no provider fallba
describes customer code you must implement and review. A timeout, lost response or generic
provider error is not proof of nonacceptance: in those cases, do not automatically send again.

For a normal deployment, follow the [main setup guide](../README.md) instead. This is an advanced
developer walkthrough: it requires custom code, durable operation state, provider-specific
approval, and separate testing. It is not another step in guided onboarding.

## 1. Set a fixed provider order and a deny-by-default policy

Keep the existing HTTP registration in [SendOtp.js](../javascript/src/functions/SendOtp.js).
Expand Down Expand Up @@ -103,8 +107,8 @@ see [provider onboarding](ONBOARDING.md#complete-provider-authentication).

## 3. Give each context its own credential service and lifecycle

Open [credentials.js](../javascript/src/functions/credentials.js). Its singleton caches the
first selected configuration; passing another account to it does not switch its cache.
Open [credentials.js](../javascript/src/functions/credentials.js). When caching is enabled, its
singleton retains the first selected configuration; passing another account does not switch that cache.
Create a long-lived service for each account instead, with an immutable configuration:

```javascript
Expand Down Expand Up @@ -145,8 +149,12 @@ app.hook.appTerminate(stopProviderCredentialRefresh);
module.exports = { stopProviderCredentialRefresh };
```

Remove the singleton import only after replacing its request/lifecycle uses. Each service begins
periodic refresh when first used. An evaluation request must not start credential acquisition;
Remove the singleton import only after replacing its request/lifecycle uses. With its selected
cache enabled, each service begins periodic refresh when first used. With caching disabled,
each live attempt acquires credentials without a poller or cross-request reuse. Preserve
the [cache switches](CONTRACT.md#credential-caching-and-refresh) in each account's configuration;
setup sets them to `false` for FC1 and `true` for EP1, while omitted values default to `true`.
An evaluation request must not start credential acquisition;
already-running refresh is independent. Rebuild contexts on controlled restart when configuration
changes; never repurpose a live service for another account.

Expand Down
62 changes: 52 additions & 10 deletions docs/ONBOARDING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# Customer configuration and validation

Start with the [root onboarding checklist](../README.md). This is the detailed customer runbook
for values, provider access, and acceptance checks; [the setup guide](../setup/docs/README.md)
covers workstation preparation and the deployment command.
Start with the [five-step setup guide](../README.md). Come here when you need exact IDs,
provider credential names, or the checks to complete before activation.

**The normal path is: confirm access, register an app, run setup once, complete provider
authentication, validate, then activate policy.** Setup already builds/publishes the Function and
configures its Azure settings. Local settings, Core Tools, and a second manual deployment are
**not** required. [Optional developer work](#optional-local-development-and-manual-deployment)
is separate.
**After successful guided setup, go straight to [provider authentication](#complete-provider-authentication),
then [validate the endpoint](#validate-the-deployed-endpoint).** The script has already published
the code and configured Azure. You do not need local settings, Core Tools, or a second deployment.

For earlier steps, use [access requirements](#before-purchasing-or-deploying),
the [values worksheet](#values-and-ownership), or the [setup reference](../setup/docs/README.md).
[Local development and manual deployment](#optional-local-development-and-manual-deployment)
are separate, optional paths.

## Before purchasing or deploying

Expand All @@ -18,6 +20,12 @@ channel, pricing, and access to the provider's **EPP integration**, not just its
An offer purchase does not deploy Azure resources, enable a Microsoft tenant feature, or install
a missing adapter.

![Cropped examples of phone-provider offer cards in Microsoft Security Store](images/onboarding/security-store-providers.png)

*These are sample cards, not recommendations or a complete provider list. Offers, preview
labels, pricing, and access can change. A store listing does not mean the setup script supports
that integration; check [guided provider support](#provider-credential-names).*

Have your tenant administrator confirm the supported EPP onboarding/activation procedure with
Microsoft and the provider **before incurring deployment costs**. This repository does not define
tenant eligibility, licensing, preview enrollment, or a self-service activation entitlement.
Expand Down Expand Up @@ -118,6 +126,39 @@ developer integrations, not additional guided provider offers:
| `infobip` | `infobip-api-key`; `Authorization: App ...` | Adapter only; no guided profile. Validate its account/options separately. |
| `sinch` | `sinch-api-token`; static token | Adapter only; no guided profile. Validate its account/options separately. |

### Where to enter API-key credentials in Key Vault

Use the vault created for **your endpoint**, not a vault chosen from a screenshot:

1. Open the saved deployment summary and find `resources.keyVault`.
2. In **Azure portal > Key vaults**, open that vault.
3. Select **Objects > Secrets**. This list shows secret names and status, not their values.
Select **Generate/Import** to enter a credential.

![Key Vault Objects menu with Secrets selected and the Generate/Import action visible](images/onboarding/key-vault-secrets.png)

*This existing test vault contains examples for more than one adapter. Create only the secrets
required by your chosen integration in the table above. The screenshot does not mean guided
setup configures multiple providers or supports every adapter.*

4. Choose **Manual**. For **Name**, use the exact name in the provider table above.
For **Secret value**, paste the credential obtained through your provider's secure process.
Leave **Enabled** set to **Yes** and set activation/expiry dates to match your provider agreement.
5. Select **Create** only after checking the vault, name, and value. Repeat for any additional
required credential, such as an account/customer ID.

![Unsaved Create a secret form showing a sample name, masked placeholder value, and Enabled set to Yes](images/onboarding/key-vault-create-secret.png)

*The screenshot uses `example-provider-api-key` and a fake masked value to show the form.
Neither is a working configuration. The form was cancelled without saving. Use the exact
provider-specific secret names, not this example name, and never reveal a real key for a screenshot.*

After saving, verify the name, enabled state, and version metadata without selecting
**Show Secret Value**. Do not edit or replace the setup-created encryption certificate secret.
If the vault denies access, ask the approved credential administrator for help; do not disable
vault protection or copy credentials into Function settings. See the permission and runtime
checks in the provider-specific steps below.

### Telesign: enter and verify the two vault secrets

1. Obtain the raw API key and matching Customer ID from your Telesign account's approved secure
Expand All @@ -137,8 +178,9 @@ developer integrations, not additional guided provider offers:
the encryption certificate secret, grant broad access, or put credentials into Function
settings to bypass a vault failure.
5. Follow the authorized evaluation and controlled live checks below. Evaluation cannot validate
these secrets. Workers cache credentials; use the [runtime cache behavior](CONTRACT.md#credential-caching-and-refresh)
to plan first-use/rotation checks. Absence of a warning does not prove account authorization.
these secrets. Credential caching depends on your settings: setup disables it for FC1 and
enables it for EP1. Use the [runtime cache behavior](CONTRACT.md#credential-caching-and-refresh)
to plan first-use and rotation checks. Absence of a warning does not prove account authorization.

Use portal secret entry rather than command-line literal values, transcripts, source files,
screenshots, or chat. During rotation, coordinate the matching pair and provider validity window;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/app-registration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/authentication.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/key-vault-secrets.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/logs-query-results.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/monitoring-link.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/monitoring-workspace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/resource-group.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/setup-choices.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/onboarding/setup-identifiers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading