diff --git a/README.md b/README.md index a3e8427..d1946ce 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # External Phone Provider Azure Function Sample This **External Phone Provider (EPP)** sample connects Microsoft Entra ID to a phone provider -for SMS and voice one-time passwords. -Guided setup deploys **one regional endpoint** using JavaScript by default, with C# and Python options. +for SMS and voice one-time passwords. Guided setup deploys **one regional endpoint** in JavaScript, +C#, or Python. JavaScript is the default. > [!IMPORTANT] -> Start in a separate nonproduction environment. Confirm provider access and Microsoft's approved -> test and activation process before deployment. +> Start in a separate nonproduction environment. Before deployment, confirm provider access and +> Microsoft's approved test and activation process. ## Table of contents @@ -23,12 +23,12 @@ Guided setup deploys **one regional endpoint** using JavaScript by default, with - JavaScript, C#, and Python implementations of one [HTTP and trust contract](docs/CONTRACT.md). - Guided Azure deployment with Easy Auth, Key Vault, and monitoring. - Evaluation mode checks authentication and decryption without sending a message. -- Built-in provider integrations and an [Add Twilio](docs/BRING-YOUR-OWN-PROVIDER.md) guide. +- Built-in provider integrations and a [BYOT provider guide](docs/BRING-YOUR-OWN-PROVIDER.md). ## Requirements -- Use a separate test environment with a Microsoft Entra ID tenant, Azure subscription, and - unique resource name prefix. Supply a dedicated app registration or create one during guided setup. +- Use a separate test environment with a Microsoft Entra ID tenant, Azure subscription, unique + resource prefix, and dedicated app registration. Guided setup can create the registration. - Check [roles, permissions, tools, regional availability, and quota](setup/docs/README.md#prerequisites-for-step-2). - For Telesign, complete the [OAuth profile placeholders](docs/ONBOARDING.md#telesign-oauth-configuration-and-handoff) before deployment. @@ -39,17 +39,15 @@ Guided setup deploys **one regional endpoint** using JavaScript by default, with ![Microsoft Entra ID sends an encrypted request through Easy Auth to a Function, which uses Key Vault and calls the phone provider](docs/images/single-region-architecture.png) -*Easy Auth checks the Microsoft caller before the Function decrypts the request. -Live success confirms provider acceptance. Confirm delivery separately. Evaluation validates the -request without sending a message. -The diagram is a topology overview; current packages read API-key secrets for each live request, -while Azure Identity owns OAuth token caching. -See [architecture details](TECHNICAL.md#single-region-architecture). -[Open the full-size diagram](docs/images/single-region-architecture.png).* +*Easy Auth checks the Microsoft caller before the Function decrypts the request. Evaluation checks +the request without sending. Live success confirms provider acceptance, not delivery. Current +packages read API-key secrets for each live request; Azure Identity caches OAuth tokens. See +[architecture details](TECHNICAL.md#single-region-architecture) or +[open the full-size diagram](docs/images/single-region-architecture.png).* ## Get started -Follow the **[five-step setup guide](docs/GETTING-STARTED.md)** in order: +Follow the **[five-step setup guide](docs/GETTING-STARTED.md)**: 1. [Get ready](docs/GETTING-STARTED.md#1-get-ready): confirm access, approvals, permissions, and budget. 2. [Choose the app](docs/GETTING-STARTED.md#2-register-your-application): use an existing registration or create one during setup. @@ -72,7 +70,7 @@ Before live use, review [production readiness](TECHNICAL.md#production-readiness | Resolve a problem | [Troubleshooting](setup/docs/Troubleshooting.md) | | Monitor the endpoint | [Monitoring](docs/MONITORING.md) | | Review the API | [Send OTP](docs/CONTRACT.md) | -| Add Twilio | [Twilio integration guide](docs/BRING-YOUR-OWN-PROVIDER.md) | +| Add a provider | [BYOT provider guide](docs/BRING-YOUR-OWN-PROVIDER.md) | | Work on a runtime | [JavaScript](javascript/README.md), [C#](dotnet/README.md), or [Python](python/README.md) | diff --git a/TECHNICAL.md b/TECHNICAL.md index 38a05ae..a41f75b 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -4,7 +4,7 @@ [Documentation](docs/README.md) | [First deployment](docs/GETTING-STARTED.md) | [HTTP contract](docs/CONTRACT.md) -This reference covers implementation details, app settings, packages, and built-in providers. +This reference covers implementation details, settings, packages, and built-in providers. | Task | Section | | --- | --- | @@ -22,9 +22,9 @@ This reference covers implementation details, app settings, packages, and built- See the [single-region diagram in the sample overview](README.md#architecture). -Microsoft Entra ID sends an authenticated, encrypted request. Easy Auth checks the caller, the -Function decrypts the request, and the selected adapter submits a live message. Success means -provider acceptance, not confirmed delivery. +Microsoft Entra ID sends an authenticated, encrypted request. Easy Auth checks the caller. The +Function decrypts the request, and the selected adapter sends the message. Success means provider +acceptance, not delivery. East US is illustrative. API-key integrations use provider secrets. OAuth integrations use managed-identity token exchange. See [provider settings and credential acquisition](#provider-settings-and-credential-acquisition) @@ -47,15 +47,16 @@ It covers capacity, shared keys, dependencies, readiness, regional recovery dril | Python (v2 model) | Available | [python/](python/) | Choose **one language and one provider per Function App**. All implementations share the -[HTTP, configuration, and trust contract](docs/CONTRACT.md). Runtime behavior can differ. +[HTTP and trust contract](docs/CONTRACT.md), but runtime behavior can differ. -For local work, use the selected runtime guide. To add Twilio, use -[Add Twilio](docs/BRING-YOUR-OWN-PROVIDER.md). +For local work, use the selected runtime guide. To add a provider, use the +[BYOT provider guide](docs/BRING-YOUR-OWN-PROVIDER.md). ## Guided EPP setup -Follow the [five-step guide](docs/GETTING-STARTED.md). `Setup-Epp.ps1` retrieves its support files -from one commit and deploys after plan approval. See the [setup reference](setup/docs/README.md#step-2---download-and-run-one-script). +Follow the [five-step guide](docs/GETTING-STARTED.md). `Setup-Epp.ps1` gets its support files from +one commit and deploys only after you approve the plan. See the +[setup reference](setup/docs/README.md#step-2---download-and-run-one-script). Review [application permissions](setup/docs/README.md#step-1---manually-create-the-application) and [certificate selection](setup/docs/README.md#encryption-certificate-selection). Setup pins one @@ -65,7 +66,7 @@ One deployment accepts SMS and voice. Each request's `channel` selects its confi ## Download a Function ZIP -Guided setup selects and publishes the language ZIP automatically. Use this section for manual deployment. +Guided setup selects and publishes the language ZIP. For a custom deployment: Open the [package releases](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases) and choose the newest stable tag ending in your language ID. Each language release contains @@ -102,8 +103,8 @@ pwsh -File ./package-dotnet.ps1 pwsh -File ./package-python.ps1 ``` -Choose one command. Each ZIP has `host.json` at its root and excludes local settings, credentials, -and tests. +Choose one command. Each ZIP contains root-level `host.json` and excludes local settings, +credentials, and tests. JavaScript installs production dependencies from the lockfile in a temporary folder. Review packaging for native dependencies or required lifecycle scripts. It does not modify `node_modules`. @@ -140,8 +141,7 @@ not in application environment settings or incoming request data. ## HTTP implementation details -Use [Send OTP](docs/CONTRACT.md) for the public request and response. This section covers runtime -implementation details. +See [Send OTP](docs/CONTRACT.md) for the public request and response. Runtime details follow. ### JWE validation and key handling @@ -212,8 +212,8 @@ The shared outcomes are `Continue`, `Block`, and `Fail`. | Missing or invalid endpoint, authentication-mode mismatch, credential or network failure, or unknown response | `502` | | Provider timeout | `504` | -Unknown or incomplete provider responses fail closed. Check a timed-out live request before -retrying because the provider might already have accepted it. +Unknown or incomplete provider responses fail closed. Before retrying a timeout, check whether the +provider accepted the request. HTTP `401` and `403` are reserved for inbound SAS-to-Function authentication, authorization, or platform access failures. HTTP `422` carries `error: "provider_blocked"` without a nonce. @@ -228,7 +228,7 @@ Provider adapters use these safe failure labels: `provider_http_error`, `provide ## Configure environment variables For guided deployment, inspect the [values and ownership table](docs/ONBOARDING.md#values-and-ownership). -The following settings are for local or manual deployment. +For local or custom deployment: Start from [sample settings](docs/local.settings.sample.json). Every `Values` entry is a string. @@ -353,12 +353,12 @@ Recognized success groups remain `ACCEPTED`, `PENDING`, and `DELIVERED` on succe unknown statuses fail closed. Set `EPP_PROVIDER_ACCOUNT_NAME` to an Infobip-approved sender/caller ID and confirm it works for -both intended channels. The adapter's existing `Verify` fallback is not proof of sender approval. +both channels. The adapter's default `Verify` value is not proof of sender approval. For an older API-key deployment, deploy a package containing this OAuth update and rerun setup with `-Provider infobip` to configure federation and the provider tenant/scope. Review `EPP_PROVIDER_AUTH_MODE=oauth` and the regional base URLs. Updating settings alone does not upgrade -an older adapter; this version has no API-key fallback. Complete +an older adapter; this version supports OAuth only. Complete [provider authorization](docs/ONBOARDING.md#complete-provider-authentication) before live use. ### Soprano diff --git a/docs/APPLICATION-INSIGHTS.md b/docs/APPLICATION-INSIGHTS.md index acdbc29..292da85 100644 --- a/docs/APPLICATION-INSIGHTS.md +++ b/docs/APPLICATION-INSIGHTS.md @@ -4,8 +4,8 @@ [Documentation](README.md) | [Monitoring](MONITORING.md) | [Log fields](LOGGING.md) -Start here when telemetry is missing or incomplete. For everyday queries and alerts, use -[monitoring](MONITORING.md). +For missing or incomplete telemetry, start here. For routine queries and alerts, see +[Monitor the endpoint](MONITORING.md). | Task | Go to | | --- | --- | @@ -17,7 +17,7 @@ Start here when telemetry is missing or incomplete. For everyday queries and ale | Review privacy and sampling | [Limits](#limits) | Function telemetry covers execution, timing, and logs. Microsoft Entra ID sign-ins and provider -delivery evidence are separate. +delivery use separate data. ## Collection @@ -38,7 +38,7 @@ requests, dependencies, exceptions, and traces when the active pipeline exports | [Python](../python/host.json) | Standard host Application Insights settings. Requests are excluded from sampling. | There is no Azure Monitor or OpenTelemetry exporter in requirements. Confirm structured properties and dependencies. | These are source defaults, not guarantees. Runtime versions, packages, settings, filters, and -sampling can change the collected data. +sampling affect collected data. Application events are logs, not guaranteed `customEvents`. Verify the [runtime format](MONITORING.md#runtime-log-formats) before saving queries. @@ -74,7 +74,7 @@ and [Functions monitoring reference](https://learn.microsoft.com/azure/azure-fun Guided setup connects one Function App to one Application Insights resource backed by a Log Analytics workspace. -Check the following: +Check these items: 1. **Destination:** On the Function App, confirm `APPLICATIONINSIGHTS_CONNECTION_STRING` points to the intended resource. Do not copy it into issues or screenshots. @@ -108,9 +108,9 @@ If you intentionally use a user-assigned ingestion identity, set `ClientId=;Authorization=AAD`, attach that identity to the app, and grant it access to the destination. Do not use the provider application ID. -Missing data can result from a wrong destination, missing role assignment, identity mismatch, -role propagation, an inactive exporter, filters, sampling, DNS, or network access. Query access is -separate from ingestion access. +Missing data can result from the wrong destination, missing roles, identity mismatch, role +propagation, an inactive exporter, filters, sampling, DNS, or network access. Query and ingestion +access are separate. See [Functions collection configuration](https://learn.microsoft.com/azure/azure-functions/configure-monitoring) and [Microsoft Entra-authenticated ingestion](https://learn.microsoft.com/azure/azure-monitor/app/azure-ad-authentication). @@ -171,8 +171,8 @@ AppTraces | order by Latest desc ``` -An absent table, query error, or empty result is not a healthy zero. Check scope, time range, -traffic, access, filters, sampling, and ingestion. `ItemCount` is a sampling weight when supplied. +An absent table, query error, or empty result does not mean zero failures. Check scope, time range, +traffic, access, filters, sampling, and ingestion. When present, `ItemCount` is a sampling weight. It does not recover discarded records. diff --git a/docs/BRING-YOUR-OWN-PROVIDER.md b/docs/BRING-YOUR-OWN-PROVIDER.md index a74f34a..75748e9 100644 --- a/docs/BRING-YOUR-OWN-PROVIDER.md +++ b/docs/BRING-YOUR-OWN-PROVIDER.md @@ -1,29 +1,58 @@ -# Add Twilio +# Bring your own telephony provider (BYOT) [Documentation](README.md) | [Send OTP API](CONTRACT.md) | [Technical details](../TECHNICAL.md) -Add a Twilio adapter to one runtime. +Add a provider adapter to one runtime. This guide uses Twilio as an example; use your provider's +approved API, credentials, endpoints, sender configuration, and response contract. > [!NOTE] -> Twilio is not included in the sample or configured by guided setup. Adding it requires code, -> tests, packaging, deployment settings, and your approved Twilio account configuration. +> BYOT providers are not included or configured by guided setup. Adding one requires code, tests, +> packaging, deployment settings, and an approved provider account. ## Before you start -Prepare: +Confirm these prerequisites before editing code: - The [Send OTP API contract](CONTRACT.md). -- An approved Twilio account and API design for SMS and voice. +- A separate nonproduction environment and controlled test recipients. +- An approved provider account with the required SMS and voice access. +- The provider's current API documentation, base URLs or operation URLs, authentication method, + credential names, sender or caller IDs, request limits, timeout guidance, and success responses. - One runtime: [JavaScript](../javascript/README.md), [C#](../dotnet/README.md), or [Python](../python/README.md). -- Test credentials and endpoints that do not send to production recipients. +- The selected runtime's local tools and dependencies. +- A local test encryption key and untracked `local.settings.json`. +- Test credentials and sandbox or test endpoints that do not reach production recipients. +- Azure access for the later deployment, including a Function App, Key Vault, managed identity, + Easy Auth configuration, and monitoring. + +Confirm the provider supports every channel, destination, sender, locale, and account feature you +plan to enable. Provider purchase or API access does not prove sender approval or recipient +delivery. Keep Easy Auth, envelope validation, JWE decryption, evaluation mode, and public responses in the -shared handler. The Twilio adapter should only build the outbound request and interpret the response. +shared handler. The provider adapter builds the outbound request and interprets the response. + +## Set up the runtime + +1. Clone the repository and open the selected runtime folder. +2. Follow its setup guide: + [JavaScript](../javascript/README.md#setup-and-deployment), + [C#](../dotnet/README.md#setup-and-deployment), or + [Python](../python/README.md#setup-and-deployment). +3. Install dependencies and run the existing offline tests before changing the adapter code. +4. Create an untracked `local.settings.json` from + [local.settings.sample.json](local.settings.sample.json). +5. Configure only a local test key for evaluation first. Add provider settings only when testing + the live adapter path with approved test credentials. +6. Keep Core Tools on loopback without tunnels. Local Core Tools does not provide Easy Auth. + +Do not copy production credentials or encryption keys to a workstation. Keep credentials out of +source, commands, test output, screenshots, and logs. @@ -38,20 +67,19 @@ shared handler. The Twilio adapter should only build the outbound request and in -1. Use a fixed lowercase provider ID such as `twilio`. +1. Use a fixed lowercase provider ID, such as `twilio`. 2. Define the authentication mode and credential names. -3. Map SMS and voice requests to the approved Twilio API. -4. Map recognized Twilio responses to `Continue`, `Block`, or `Fail`. +3. Map SMS and voice requests to the approved provider API. +4. Map recognized provider responses to `Continue`, `Block`, or `Fail`. 5. Return the shared `ProviderResult`. 6. Keep sending in the shared runtime transport. -Only a recognized accepted response can return success. A successful provider response does not -confirm recipient delivery. +Return success only for a recognized acceptance. Acceptance does not confirm delivery. -Do not forward the incoming Microsoft Entra ID bearer token or return raw Twilio JSON. +Do not forward the incoming Microsoft Entra ID bearer token or return raw provider JSON. -## Register it +## Register the adapter ### JavaScript @@ -83,7 +111,7 @@ Do not forward the incoming Microsoft Entra ID bearer token or return raw Twilio -## Configure it +## Configure the provider @@ -93,14 +121,19 @@ Set: - `EPP_PROVIDER_NAME=twilio` - `EPP_PROVIDER_AUTH_MODE` to the mode implemented by the adapter. - `EPP_PROVIDER_SMS_ENDPOINT` and `EPP_PROVIDER_VOICE_ENDPOINT` for supported channels. -- Twilio credential references and any additional adapter settings. +- Provider credential references and any additional adapter settings. + +Replace `twilio` with the fixed ID used by your adapter. Set each channel endpoint to the exact URL +form expected by the adapter. Some providers use a base URL and others use a complete operation +URL. Require HTTPS and reject credentials, user information, query strings, or fragments in +endpoint settings. -Keep credentials in Key Vault and read them through managed identity. Use the shared request-time -credential and timeout services instead of adding a credential cache, refresher, or send-retry loop. +Keep credentials in Key Vault and use managed identity to read them. Use the shared credential and +timeout services. Do not add a cache, refresher, or send-retry loop. See [provider settings and credential acquisition](../TECHNICAL.md#provider-settings-and-credential-acquisition). -## Test it +## Test the adapter Cover: @@ -111,6 +144,8 @@ Cover: - Network errors and timeouts. - Evaluation mode with no provider call. - Privacy-safe logs with no phone number, message, passcode, nonce, token, key, or raw response. +- Both configured channels and every supported provider region or route. +- Approved sender or caller IDs and test-recipient restrictions. Use fake credentials and service responses for offline tests. @@ -121,8 +156,10 @@ Use fake credentials and service responses for offline tests. 1. Run the selected runtime tests. 2. [Build a new package](../TECHNICAL.md#build-zips-locally). 3. Follow [manual deployment](ONBOARDING.md#optional-local-development-and-manual-deployment). -4. Configure Easy Auth, app settings, Key Vault access, and monitoring. -5. Run the [approved endpoint checks](ONBOARDING.md#validate-the-deployed-endpoint). +4. Set `EPP_PROVIDER_NAME` to the new adapter ID and configure both channel endpoints. +5. Add provider credentials to Key Vault and grant only the Function identity the required access. +6. Configure Easy Auth, app settings, monitoring, and any provider-side authorization. +7. Run the [approved endpoint checks](ONBOARDING.md#validate-the-deployed-endpoint). Do not deploy an unchanged public release after adding the adapter. diff --git a/docs/CONTRACT.md b/docs/CONTRACT.md index ea630f7..63ab8ce 100644 --- a/docs/CONTRACT.md +++ b/docs/CONTRACT.md @@ -4,9 +4,9 @@ -[Documentation](README.md) | [Technical details](../TECHNICAL.md) | [Add Twilio](BRING-YOUR-OWN-PROVIDER.md) +[Documentation](README.md) | [Technical details](../TECHNICAL.md) | [BYOT provider guide](BRING-YOUR-OWN-PROVIDER.md) -Receive an encrypted SMS or voice request from **Microsoft Entra ID**. +This endpoint receives encrypted SMS or voice requests from **Microsoft Entra ID**. | | | | --- | --- | @@ -21,7 +21,7 @@ Receive an encrypted SMS or voice request from **Microsoft Entra ID**. ## Authentication -Configure Easy Auth with: +Before testing the endpoint, configure Easy Auth with: - HTTPS required. - Unauthenticated requests returned as `401`. @@ -50,7 +50,7 @@ Authorization: Bearer | `x-ms-correlation-id` | Optional correlation value. | | `x-ms-client-request-id` | Optional request identifier. | -The Function does not forward the bearer token to the phone provider. +The Function never forwards the bearer token to the phone provider. ## Request body @@ -79,7 +79,7 @@ The Function does not forward the bearer token to the phone provider. | `correlationId` | No | Sign-in correlation value. | | `ttlSeconds` | No | Positive integer up to `2147483647`. The sample validates this value but does not enforce passcode expiry or a delivery deadline. | -Use the documented channel and mode values. When supplying `ttlSeconds`, use an integer. See +Use the documented channel and mode values. If you supply `ttlSeconds`, use an integer. See [request validation and runtime differences](../TECHNICAL.md#request-validation-and-runtime-differences). ### Encrypted delivery context @@ -112,9 +112,8 @@ The JWE uses `RSA-OAEP-256` and `A256GCM`. After decryption, the JSON contains: | `extension` | No | Legacy office-voice data. The shared handler does not forward it to built-in providers. | | `riskContext` | No | JSON key-value object containing risk signals; may be `null`. | -Microsoft Entra ID supplies `otp` for SMS and voice, with a generated test code in evaluation -mode. It belongs inside the encrypted delivery context, not the outer request body. Requests -that omit it or set it to `null` remain supported. +Microsoft Entra ID puts `otp` inside the encrypted delivery context, not the outer request body. +Evaluation uses a generated test code. `otp` can be omitted or `null`. `riskContext` has optional `scenario` and `ipAddress` strings; other fields retain arbitrary JSON values. Missing/null common fields are absent, and empty strings are preserved. Non-string common @@ -137,7 +136,7 @@ algorithms, key settings, and runtime behavior. - **Live:** sends through the configured provider and returns success after recognized provider acceptance. -Provider acceptance is not proof of recipient delivery. +Provider acceptance does not prove delivery. ## Response @@ -230,9 +229,8 @@ Error categories are: - `delivery_failed` for an unexpected handler failure. These JSON errors come from the SendOtp handler. Easy Auth can reject a request before the handler -runs. The .NET Functions host can also reject a request while binding its input, before the handler -runs. Those responses can have a different format. When `reason` is present, it is a fixed -validation message rather than an exception message. +runs. The .NET Functions host can reject input during binding. Those responses can use a different +format. When present, `reason` is a fixed validation message, not an exception. Unavailable provider credentials, for example, return `502 Bad Gateway`. The safe diagnostic detail is recorded as @@ -241,7 +239,7 @@ logs, not exposed in the response. ### Diagnose a credential failure -When a live request cannot obtain provider credentials, current packages emit the following events: +When a live request cannot obtain provider credentials, current packages emit these events: | Signal | Meaning | | --- | --- | @@ -296,6 +294,6 @@ for corrective steps. - [Technical implementation details](../TECHNICAL.md#http-implementation-details) - [Built-in providers](../TECHNICAL.md#built-in-providers) -- [Add Twilio](BRING-YOUR-OWN-PROVIDER.md) +- [BYOT provider guide](BRING-YOUR-OWN-PROVIDER.md) - [Application log reference](LOGGING.md) - [Deployment validation](ONBOARDING.md#validate-the-deployed-endpoint) diff --git a/docs/COST-CALCULATOR.md b/docs/COST-CALCULATOR.md index 4125833..a94c9b5 100644 --- a/docs/COST-CALCULATOR.md +++ b/docs/COST-CALCULATOR.md @@ -1,11 +1,10 @@ # Estimate telecom-provider and Azure hosting costs Estimate SMS and voice traffic before onboarding a provider. The report groups recent -authentication steps by calling code. Apply provider rates to estimate a telecom budget. -This guide also describes the Azure resources to include when -estimating the separate cost of hosting the provider integration. +authentication steps by calling code. Apply provider rates for a telecom estimate, then estimate +Azure hosting separately. -The guide separates the estimate into two parts: +Build the estimate in two parts: 1. Telecom-provider costs based on estimated SMS and voice requests. 2. Azure hosting costs for running and monitoring the provider integration. @@ -13,8 +12,8 @@ The guide separates the estimate into two parts: ## 1. Estimate telecom-provider costs > [!IMPORTANT] -> This is not a billing report. Confirm retries, secondary-provider attempts, message segments, -> destinations, taxes, fees, and pricing with the provider. +> This is not a bill. Confirm retries, secondary-provider attempts, message segments, +> destinations, taxes, fees, and prices with the provider. | Step | Task | | --- | --- | @@ -48,8 +47,8 @@ approved folder. ### Generate the activity report -The script installs the required modules, finds SMS and voice authentication steps, matches masked -details to registered phone methods, and groups the result. Set `$start` and `$end` first. +The script installs required modules, finds SMS and voice authentication steps, matches masked +details to registered phone methods, and groups the results. Set `$start` and `$end` first. ```powershell # 1. Install required modules if they are missing @@ -184,8 +183,8 @@ Microsoft Entra ID default retention period are available."** Selecting an earli ### Convert activity into a cost estimate -The exported report contains one row per calling code and authentication method. It counts -each SMS or voice authentication step as one estimated request: +The report has one row per calling code and authentication method. Each SMS or voice +authentication step counts as one estimated request: ```text Estimated SMS/voice requests = matching authentication-step count @@ -207,7 +206,7 @@ contingency for growth, retries, approved secondary-provider behavior, and seaso ### Confirm provider rates -Confirm the following with the provider: +Confirm these items with the provider: - How each international calling code maps to the provider's destination pricing. - Country and carrier coverage, sender registration, and route-specific rates. @@ -230,9 +229,7 @@ and service capabilities can change, so do not use an old estimate as an approve ### Get a quick planning estimate -If you need a rough estimate rather than a detailed calculation, select the deployment -profile that meets your availability requirements and use the closest monthly request -volume in the following table. +For a rough estimate, choose the required availability profile and closest monthly request volume. | Deployment profile | 50,000 requests/month | 1 million requests/month | 10 million requests/month | | --- | ---: | ---: | ---: | @@ -250,8 +247,8 @@ telecom-provider charges, taxes, support plans, and customer-specific discounts. costs can vary by region, agreement, traffic pattern, telemetry retention, and service pricing. -Use the table for early planning only. For an approved budget, enter your expected volume -and required deployment profile in the Azure Pricing Calculator. If your volume falls +Use the table only for early planning. For an approved budget, enter your expected volume and +deployment profile in the Azure Pricing Calculator. If your volume falls between columns, use the next higher column for a conservative estimate; do not scale the table values linearly because some profiles include fixed baseline capacity. diff --git a/docs/FRONTDOOR.md b/docs/FRONTDOOR.md index d29dbf2..7fb7326 100644 --- a/docs/FRONTDOOR.md +++ b/docs/FRONTDOOR.md @@ -5,7 +5,7 @@ [Documentation](README.md) | [Single-region setup](GETTING-STARTED.md) | [Routing troubleshooting](../setup/docs/Troubleshooting.md#optional-front-door-deployments) -Azure Front Door provides one public EPP URL across regional Function Apps. Start with a validated +Azure Front Door can provide one public EPP URL for regional Function Apps. First validate a [single-region deployment](GETTING-STARTED.md). | Step | Task | @@ -19,13 +19,12 @@ Azure Front Door provides one public EPP URL across regional Function Apps. Star ## When to use this design -Use this advanced manual design when one public endpoint must route across regional Function Apps. -The standard setup creates one regional endpoint. Released packages do not include the readiness -route required by Front Door health probes, so implement and validate that route before enabling -multi-region routing. +Choose this design only when one public endpoint must route across regional Function Apps. +Standard setup creates one regional endpoint. Released packages do not include the readiness route +required by Front Door probes. Add and validate that route before enabling multi-region routing. -Plan capacity, credentials, keys, monitoring, recovery tests, and rollback for the complete design. -Set measurable recovery, latency, capacity, and failure targets before activation. +Before activation, plan capacity, credentials, keys, monitoring, recovery tests, and rollback. +Define recovery, latency, capacity, and failure targets. ## Architecture @@ -49,8 +48,8 @@ priority, latency, and weight. Equal weights do not guarantee a 50/50 split. ### Regional origins Give every region its own Function App, capacity, storage, Key Vault, identities, telemetry, and -provider credentials. Use equal priority when the regions should all serve traffic, and verify -that every supported survivor combination can handle the required workload. Front Door, +provider credentials. Use equal priority when all regions should serve traffic. Verify that every +supported survivor combination can handle the workload. Front Door, Microsoft Entra ID authentication, and the provider remain shared dependencies. ## 1. Plan the regional deployment @@ -69,8 +68,8 @@ Build and test the regional design outside live traffic. Review the caller application ID, certificate/public-key identity, selected provider route, and regional owners. - Plan rollout, validation, renewal, monitoring, recovery tests, and rollback before activation. -Follow this regional procedure rather than rerunning `Setup-Epp.ps1` against the same -application. The setup script creates one region. The steps below coordinate Front Door, shared +Do not rerun `Setup-Epp.ps1` against the same application. It creates one region. The steps below +coordinate Front Door, shared key material, regional registrations, and recovery routing without replacing an existing endpoint's application configuration or certificate unexpectedly. @@ -156,8 +155,8 @@ certificate is separate from this JWE encryption certificate. ## 3. Provide a non-delivering readiness endpoint -Front Door's health probes do not carry the Microsoft Entra ID caller token. Probing the protected `SendOtp` -endpoint would test authentication failure rather than application readiness. +Front Door probes do not carry the Microsoft Entra ID caller token. Probing the protected +`SendOtp` endpoint tests authentication failure, not readiness. Implement a separate route such as **`/api/health/ready`** with this contract: diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md index 82eb04e..9114759 100644 --- a/docs/GETTING-STARTED.md +++ b/docs/GETTING-STARTED.md @@ -5,7 +5,7 @@ [Documentation](README.md) | [Setup reference](../setup/docs/README.md) | [Troubleshooting](../setup/docs/Troubleshooting.md) **External Phone Provider (EPP)** connects Microsoft Entra ID to your phone provider for SMS -and voice one-time passwords. This guide deploys one regional endpoint for that integration. +and voice one-time passwords. Deploy one regional endpoint in five steps. | Step | Task | | --- | --- | @@ -15,13 +15,13 @@ and voice one-time passwords. This guide deploys one regional endpoint for that | 4 | [Connect the provider and test](#4-connect-your-provider-and-test) | | 5 | [Activate and hand over](#5-activate-and-hand-over) | -Complete the steps in order. Portal labels can differ from the screenshots. +Complete the steps in order in nonproduction. Portal labels can differ from the screenshots. ## 1. Get ready -Use a separate test environment with its own application registration and resource prefix. +Use a separate test environment with its own app registration and resource prefix. -Before purchasing or deploying, confirm provider EPP access in +Before spending or deploying, confirm provider EPP access in [Microsoft Security Store](https://securitystore.microsoft.com/private-solutions) and arrange Microsoft's approved test and activation process. See [access and support](ONBOARDING.md#before-purchasing-or-deploying). @@ -51,19 +51,16 @@ Assign an **onboarding owner** and review [production readiness](../TECHNICAL.md ## 2. Register your application -You can create the dedicated application during guided setup or register it manually. +Use a dedicated app registration. Create it during setup or before you begin. -**Create during setup:** in Step 3, press Enter to choose **Create a new application**, then Enter to use -**EPP Application** or enter your own display name, and choose a provider by name from the -alphabetized list (**Infobip, Soprano, Telesign**). Blank provider-name input prompts again instead of -canceling or selecting a default. -Setup creates the registration only after you approve the final deployment plan. -Canceling before approval creates no application. The signed-in user must be allowed to register -applications in the customer tenant. +**Create during setup:** in Step 3, press Enter to choose **Create a new application**. Press Enter +again to use **EPP Application**, or enter another display name. Then choose **Infobip**, +**Soprano**, or **Telesign** by name. There is no default provider; blank input prompts again. +Setup creates the registration only after you approve the final deployment plan. Canceling before +approval creates nothing. The signed-in user must be allowed to register applications. -**Register manually or use an existing dedicated app:** keep its tenant and application client -IDs handy. To register manually, open the customer tenant's -**Microsoft Entra admin center > App registrations > New registration**: +**Use or create a dedicated app:** keep its tenant ID and application client ID handy. To create +one, open **Microsoft Entra admin center > App registrations > New registration**: 1. Give the application a recognizable name. 2. Select **Accounts in this organizational directory only**. Leave the redirect URI blank, @@ -120,11 +117,10 @@ On a shared workstation, use `.\Setup-Epp.ps1 -ForceAuthentication` for a fresh JavaScript is used by default, without a language prompt. To deploy another language, run `.\Setup-Epp.ps1 -Language csharp` or `.\Setup-Epp.ps1 -Language python`. -Select the tenant and subscription, then select an existing application, enter its **client ID**, -or choose **Create a new application** and accept **EPP Application** or enter your own display name. -Enter accepts the displayed menu default rather than canceling: the current CLI tenant/subscription -when eligible (otherwise the first eligible option), and **Create a new application** for the app. -Use **0** or **Cancel** where offered to stop. +Select the tenant and subscription. Then select an existing application, enter its **client ID**, +or choose **Create a new application**. Enter accepts the displayed default: the current eligible +CLI tenant or subscription (otherwise the first eligible option), and **Create a new application** +for the app. Use **0** or **Cancel** where offered to stop. Setup detects the tenant's replication scope and selects the corresponding provider route; it does not ask you to choose EU or Global. Select the Azure region, then complete the remaining settings: @@ -173,7 +169,7 @@ Azure resource deployment and Function publication can each take several minutes open; a rotating indicator identifies the operation in progress, without a percentage or estimated completion time. Unattended or redirected output uses static status messages instead. -Wait for **Deployment completed successfully** and the saved summary before Step 4. Setup creates or updates +Before Step 4, wait for **Deployment completed successfully** and save the summary. Setup creates or updates the Function App, hosting plan, storage, Key Vault, identities, Log Analytics, Application Insights, diagnostics, Easy Auth, and scoped role assignments. Do not precreate these resources. **Save the output: the endpoint URL and Application (client) ID are required for policy updates.** @@ -202,7 +198,7 @@ listed in your deployment summary. ## 4. Connect your provider and test -Next, complete [provider authentication](ONBOARDING.md#complete-provider-authentication). +First, complete [provider authentication](ONBOARDING.md#complete-provider-authentication). API-key integrations use the [setup-created vault and exact secret names](ONBOARDING.md#where-to-enter-api-key-credentials-in-key-vault). OAuth uses the provider-administrator handoff, not API-key secrets. @@ -217,7 +213,7 @@ In your Function App, open **Settings > Authentication**. Check that authenticat *The Function and identity-provider labels remain visible. Only the client ID is hidden. The **Microsoft Entra ID** identity provider validates the caller; it is not the SMS or voice provider.* -Complete the [approved endpoint checks](ONBOARDING.md#validate-the-deployed-endpoint): +Then complete the [approved endpoint checks](ONBOARDING.md#validate-the-deployed-endpoint): 1. Confirm that missing or unauthorized caller credentials are rejected. 2. Run an authorized **evaluation** to check authentication and decryption without a provider send. @@ -225,8 +221,8 @@ Complete the [approved endpoint checks](ONBOARDING.md#validate-the-deployed-endp channel you intend to activate** and confirm provider acceptance and recipient delivery separately. 4. Check that the request and relevant logs appear in Application Insights. -Use Microsoft's approved caller and test procedure for these checks. An ordinary Azure CLI token -cannot represent that caller. Activate the endpoint after every required check passes. If a live +Use Microsoft's approved caller and test procedure. An ordinary Azure CLI token cannot represent +that caller. Activate the endpoint after every required check passes. If a live send times out, investigate its outcome before trying again because the provider may already have accepted it. Retain only safe evidence: resource IDs, UTC times, status, latency, correlation IDs, and sanitized outcomes. diff --git a/docs/LOGGING.md b/docs/LOGGING.md index 4468ce6..40cbbeb 100644 --- a/docs/LOGGING.md +++ b/docs/LOGGING.md @@ -2,7 +2,7 @@ [Documentation](README.md) | [Find deployed logs](MONITORING.md#runtime-specific-log-discovery) | [HTTP contract](CONTRACT.md) -This reference defines safe event fields, request flow, and runtime differences. +This reference covers safe event fields, request flow, and runtime differences. | Need | Section | | --- | --- | @@ -23,8 +23,8 @@ responses.** Keep body tracing off. | Python | Standard `logging`. `otp_log.py` defines fixed event IDs, names, levels, and fields. An immutable `LoggerAdapter` supplies Function and Microsoft trace identifiers. | | .NET | Standard `ILogger`. `OtpLog` defines source-generated IDs/names. `ILogger.BeginScope` supplies `FunctionName`, `FunctionRequestId`, `FunctionInvocationId`, `MsClientRequestId`, `MsCorrelationId`, and `MsCorrelationIdSource`. | -Each invocation ends with `request_completed`. Exporters control formatting and property -preservation. Field names differ by runtime, so verify the +Each handler invocation ends with `request_completed`. Exporters control formatting and preserve +properties differently. Field names differ by runtime, so verify the [deployed schema](MONITORING.md#runtime-specific-log-discovery). ## Event sequence @@ -87,12 +87,12 @@ examples below. Raw error text, responses and credentials are never logged. Pyth | [`AADSTS70021`](https://login.microsoftonline.com/error?code=70021) | No matching federation; check issuer, subject and audience. | | [`AADSTS700016`](https://login.microsoftonline.com/error?code=700016) | Application not found in the requested directory; check app and tenant IDs. This alone does not prove missing provider consent. | -The event does not identify which acquisition step failed or infer a root cause. A null value -does not establish that authentication succeeded or that the failure was unrelated to Entra. +The event does not identify the failed acquisition step or root cause. A null value does not prove +that authentication succeeded or that the failure was unrelated to Microsoft Entra ID. ## Safe fields and identifiers -Build metadata from validated allowlisted fields. Exclude unknown, tenant, locale, risk, and +Use only validated, allowlisted fields for metadata. Exclude unknown, tenant, locale, risk, and decrypted values. `providerCredentialSource` is `managed_identity_client_assertion` for app tokens and `key_vault` diff --git a/docs/MONITORING.md b/docs/MONITORING.md index 4dc0e0e..bae7084 100644 --- a/docs/MONITORING.md +++ b/docs/MONITORING.md @@ -4,8 +4,8 @@ [Documentation](README.md) | [Application Insights](APPLICATION-INSIGHTS.md) | [Log fields](LOGGING.md) | [Troubleshooting](../setup/docs/Troubleshooting.md#after-deployment-triage) -Guided setup creates Application Insights and Log Analytics. Start here to confirm telemetry, -review failures and latency, and create alerts. +Guided setup creates Application Insights and Log Analytics. Confirm telemetry, review failures +and latency, and add alerts. | Task | Start here | | --- | --- | @@ -18,7 +18,7 @@ review failures and latency, and create alerts. | Diagnose missing data | [Collection and data quality](#collection-and-data-quality) | | Monitor Microsoft Entra ID sign-ins | [Sign-in monitoring](SIGN-IN-MONITORING.md) | -Run queries only after an [authorized test](ONBOARDING.md#validate-the-deployed-endpoint). +Run these queries only after an [authorized test](ONBOARDING.md#validate-the-deployed-endpoint). Keep phone numbers, OTPs, request bodies, credentials, tokens, nonces, and raw provider responses out of queries, alerts, exports, and screenshots. @@ -40,7 +40,7 @@ out of queries, alerts, exports, and screenshots. application IDs, the instrumentation key, and the connection string remain hidden.* Record the Function App, Application Insights resource, workspace, runtime, region, deployment -version, owner, and expected traffic or test schedule. For several regions, keep one row per origin. +version, owner, and expected traffic or test schedule. Use one row per regional origin. Connection strings and resource identifiers are private operational data. Grant read access only to the required workspace. Application telemetry uses `AppRequests`, `AppTraces`, and related @@ -59,7 +59,7 @@ AppRequests | order by LastSeen desc ``` -Use the observed `AppRoleName`, `_ResourceId`, and request `Name` in later queries. Do not assume +Copy the observed `AppRoleName`, `_ResourceId`, and request `Name` into later queries. Do not assume that every deployment records the operation as `SendOtp`. @@ -76,8 +76,7 @@ AppRequests ![CYOT-CONSTO workspace Logs in KQL mode with a query scoped to the consto Function App and one aggregate result row](images/onboarding/logs-query-results.png) `StoredRows` counts stored telemetry, not delivered messages. Sampling can change the count. -Evaluation and live calls can both return `200`. No rows means no matching data in this scope and -time range, not proof that the endpoint is healthy. +Evaluation and live calls can both return `200`. No rows means no matching data in this scope and time range. It does not prove health. ## Function requests @@ -117,7 +116,7 @@ AppRequests | order by TimeGenerated asc ``` -Investigate application `4xx` separately from provider or host `5xx`. Easy Auth can reject a +Investigate application `4xx` separately from provider or host `5xx`. Easy Auth can reject request before the Function runs, so some `401` or `403` responses may not create an `AppRequests` row or application event. @@ -260,8 +259,8 @@ Expected ``` For an alert, append `| where MissingTelemetry` and trigger when table rows are greater than zero. -Use this only when traffic or an authorized scheduled test is expected. Idle or standby origins can -legitimately have no request rows. +Run this query only when traffic or an authorized scheduled test is expected. Idle or standby +origins can have no request rows. ## Alerts @@ -298,7 +297,7 @@ contacts. Cost budgets notify but do not stop spending. -Create a Workbook only after the queries are verified. Include: +Create a Workbook only after verifying the queries. Include: - request volume, failure percentage, and p95/p99 latency - expected origins and last-seen status diff --git a/docs/MULTI-PROVIDER-IMPLEMENTATION.md b/docs/MULTI-PROVIDER-IMPLEMENTATION.md index fca9a89..36b918a 100644 --- a/docs/MULTI-PROVIDER-IMPLEMENTATION.md +++ b/docs/MULTI-PROVIDER-IMPLEMENTATION.md @@ -4,12 +4,11 @@ [Documentation](README.md) | [Normal deployment](GETTING-STARTED.md) | [Provider interface](../TECHNICAL.md#provider-result-mapping) -The sample does not implement automatic secondary-provider attempts. This advanced design shows -how a customer-owned implementation can add one controlled primary-to-secondary attempt around -`/api/SendOtp`. +The sample does not implement automatic secondary-provider attempts. This advanced design +describes one controlled primary-to-secondary attempt around `/api/SendOtp`. -Use the secondary **only after proof that the primary did not accept the request**. Timeouts, -lost responses, and generic errors are uncertain and must not trigger an automatic resend. +Use the secondary **only with proof that the primary did not accept the request**. Timeouts, lost +responses, and generic errors are uncertain and must not trigger an automatic resend. | Step | Task | | --- | --- | @@ -37,7 +36,7 @@ const providerOrder = Object.freeze({ Reject unsupported channels and request-supplied provider configuration. Do not use round-robin, fan-out, recursive provider switching, or primary retries. Attempt the secondary at most once. -Keep **routing order** separate from **secondary-attempt eligibility**. Use this policy: +Keep **routing order** separate from **secondary-attempt eligibility**: | Primary result | Action | | --- | --- | @@ -55,8 +54,7 @@ authentication and provider controls still apply. [config.js](../javascript/src/functions/config.js) accepts `readConfig(settings)`, so each account can use its own immutable settings without changing `process.env` per request. -The following customer-owned format is not loaded by the sample. Replace each -`` with a registered adapter. +The sample does not load this format. Replace each `` with a registered adapter. ```json [ @@ -106,7 +104,7 @@ identity to the Function App. Grant the vault-reading identity scoped secret-rea as Key Vault Secrets User under RBAC or the approved access-policy equivalent. If using the system-assigned identity, grant that identity instead. Client-ID settings do not attach identities or grant access. Complete provider account, recipient, sender, and consent prerequisites. -see [provider onboarding](ONBOARDING.md#complete-provider-authentication). +See [provider onboarding](ONBOARDING.md#complete-provider-authentication). ## 3. Credential isolation @@ -159,8 +157,8 @@ for another account. ## 4. Operation state -Add a **durable attempt store and idempotency contract** before allowing a second submission. -Agree on a stable, authorized operation key with the caller and test owner. Generated invocation +Add a **durable attempt store and idempotency contract** before a second submission. Agree on a +stable, authorized operation key with the caller and test owner. Generated invocation IDs, correlation GUIDs, and nonces serve other purposes. Bind the operation key to the authorized caller, request, and fixed policy so another request cannot reuse it for a different delivery. @@ -280,7 +278,7 @@ even when the primary result would otherwise allow it. ## 7. Validation and deployment -Before editing, run the existing JavaScript tests from the repository root. Restore missing +Before editing code, run the existing JavaScript tests from the repository root. Restore missing dependencies from the existing lockfile only when needed: ```powershell diff --git a/docs/ONBOARDING.md b/docs/ONBOARDING.md index b84e087..0482afc 100644 --- a/docs/ONBOARDING.md +++ b/docs/ONBOARDING.md @@ -2,7 +2,7 @@ [Documentation](README.md) | [Deployment guide](GETTING-STARTED.md) | [Troubleshooting](../setup/docs/Troubleshooting.md) -After deployment, connect the provider, validate the endpoint, and assign operational owners. +After deployment, connect the provider, validate the endpoint, and assign owners. | Task | Go to | | --- | --- | @@ -16,16 +16,17 @@ After deployment, connect the provider, validate the endpoint, and assign operat ## Before deployment -In [Microsoft Security Store](https://securitystore.microsoft.com/private-solutions), confirm the +Before spending or deploying, use +[Microsoft Security Store](https://securitystore.microsoft.com/private-solutions) to confirm the provider account, sender registration, countries, channels, pricing, and EPP access. ![Cropped examples of phone-provider offer cards in Microsoft Security Store](images/onboarding/security-store-providers.png) *Sample cards only. Check the current offer and [guided provider support](#provider-credential-names).* -Before spending or deploying, confirm: +Also confirm: -- eligibility, licensing, and preview access +- eligibility, licensing, and feature access - the authorized Microsoft test caller and procedure - the activation process - an onboarding owner for Microsoft Entra ID, Azure, provider access, and support @@ -75,9 +76,9 @@ and [certificate selection](../setup/docs/README.md#encryption-certificate-selec ### Values setup configures -Save the deployment output: the endpoint URL and Application (client) ID are required for policy -updates. Keep `deployment-*.json` and the public certificate in private storage. Compare the summary with -the approved plan. `policyChanged: false` is expected because setup does not activate policy. +Save the deployment output. Policy updates need the endpoint URL and Application (client) ID. +Keep `deployment-*.json` and the public certificate in private storage. Compare the summary with +the approved plan. `policyChanged: false` is expected: deployment does not activate policy. | Setting | Purpose | Check | | --- | --- | --- | @@ -93,7 +94,7 @@ the approved plan. `policyChanged: false` is expected because setup does not act | `EPP_PROVIDER_TIMEOUT_MS`, `EPP_PROVIDER_RETRY_INTERVAL_MS` | Provider timing values | Retry interval metadata does not enable retries | | Easy Auth issuer, audience, and caller allowlist | Inbound Microsoft caller validation | Read back the platform configuration | -The trust paths are separate: +The trust paths are independent: - Easy Auth validates the caller. - The Key Vault certificate decrypts the request. @@ -113,8 +114,8 @@ Guided setup supports **Infobip, Soprano, and Telesign**. | Soprano | OAuth client assertion through managed identity federation | Complete provider-side authorization and confirm tenant, app, scope, account, and channels | | Telesign | OAuth client assertion through managed identity federation | Complete [OAuth configuration and handoff](#telesign-oauth-configuration-and-handoff) | -Sinch is bundled but does not have a guided setup profile. Twilio requires the -[Add Twilio](BRING-YOUR-OWN-PROVIDER.md) development process. +Sinch is bundled but does not have a guided setup profile. Other providers require the +[BYOT provider guide](BRING-YOUR-OWN-PROVIDER.md) development process. Infobip retains the previously tested SMS and advanced voice API contracts, using OAuth rather than API-key authentication. Its channel settings are HTTPS regional base URLs, not the complete @@ -162,8 +163,8 @@ test before retiring the old credentials. Evaluation does not test provider cred ### Complete provider OAuth authorization -Setup creates the customer-side federation. The provider administrator still needs to authorize the -customer application and account in the configured access-token tenant. +Setup creates customer-side federation. The provider administrator must still authorize the +customer application and account in the access-token tenant. Send identifiers only through the approved onboarding route: @@ -189,14 +190,14 @@ prove provider-side authorization. Verify the integration with an approved contr For Infobip, set the Function App's `EPP_PROVIDER_ACCOUNT_NAME` to the approved sender/caller ID and confirm account access to SMS, voice, and the selected regional host. Do not assume the existing -`Verify` fallback is an approved voice caller ID. No API key or client secret is required. +default `Verify` value is an approved voice caller ID. No API key or client secret is required. ## Validate the endpoint -Use Microsoft's approved caller and test procedure. A customer test application or ordinary Azure -CLI token is a different caller. +Use Microsoft's approved caller and test procedure. A customer test app or Azure CLI token cannot +stand in for that caller. Provide the authorized operator: @@ -216,7 +217,7 @@ Provide the authorized operator: | 5 | Send one approved live SMS and voice request | Provider acceptance plus separate recipient confirmation | | 6 | Confirm logs and notifications | Saved query, resource scope, and tested action group | -The following check sends no token and no OTP: +This check sends no token and no OTP: ```powershell $endpoint = 'https:///api/SendOtp' @@ -264,7 +265,7 @@ evidence, package version, and renewal dates in restricted operational storage. ## Custom deployment -Skip this section after guided setup unless you are changing code or deploying a custom package. +For custom code or package deployment only. For local work: diff --git a/docs/README.md b/docs/README.md index 9078356..046619f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ [Sample overview](../README.md) | [Get started](GETTING-STARTED.md) -Use the guide that matches your task. +Choose the guide for your task. @@ -27,7 +27,7 @@ Use the guide that matches your task. - [JavaScript](../javascript/README.md) - [C#](../dotnet/README.md) - [Python](../python/README.md) -- [Add Twilio](BRING-YOUR-OWN-PROVIDER.md) +- [Bring your own telephony provider (BYOT)](BRING-YOUR-OWN-PROVIDER.md) ## Operate diff --git a/docs/SIGN-IN-MONITORING.md b/docs/SIGN-IN-MONITORING.md index 7ec7682..713e386 100644 --- a/docs/SIGN-IN-MONITORING.md +++ b/docs/SIGN-IN-MONITORING.md @@ -2,8 +2,8 @@ [Documentation](README.md) | [Function monitoring](MONITORING.md) -Sign-in records explain identity activity. Monitor Function execution, provider acceptance, and -delivery through their own data sources. +Sign-in records explain identity activity. Function execution, provider acceptance, and delivery +use separate data sources. | Task | Start here | | --- | --- | @@ -15,8 +15,8 @@ delivery through their own data sources. ## Recommended monitors -Choose exact tenant, application, service-principal, managed-identity, and resource IDs. Display -names can change and are not reliable alert filters. +Filter on exact tenant, application, service-principal, managed-identity, and resource IDs. +Display names can change and are not reliable alert filters. ### Integration sign-in failures @@ -42,8 +42,8 @@ names can change and are not reliable alert filters. - **Notify:** recurring errors or failures above the normal baseline - **Check first:** overall result, recent app or policy changes, and later recovery records -Keep the streams separate. A successful authentication step does not make the complete sign-in -successful. Set thresholds from representative traffic, not a universal sample value. +Keep the streams separate. A successful authentication step does not make the whole sign-in +successful. Set thresholds from representative traffic, not a universal sample. ## Find a sign-in @@ -119,8 +119,8 @@ differ.* | `ManagedIdentitySignInLogs` | `AADManagedIdentitySignInLogs` | | `AuditLogs` | `AuditLogs` | -Initial export is not immediate and does not backfill all portal history. Record the observed -arrival delay before setting alert windows. +Initial export is delayed and does not backfill all portal history. Measure the delay before +setting alert windows. ### 4. Confirm the data @@ -134,8 +134,8 @@ arrival delay before setting alert windows. 6. Add tenant and application filters before aggregation. 7. Run the query across normal, peak, and quiet periods, then save it with its scope and baseline. -No matching rows is not a healthy result. Check the destination, category, time range, access, -source activity, and ingestion delay. +No matching rows does not prove health. Check destination, category, time range, access, source +activity, and ingestion delay. For the first alert, the official **Failed Signin reasons** query is a useful starting point. `ResultType` is a string in `SigninLogs`. Treat empty results separately and do not confuse @@ -154,8 +154,8 @@ authentication codes with provider HTTP status codes. 7. Create the rule disabled when access must be granted after creation, then enable it after role propagation and testing. -Creating alerts requires workspace read access, rule write access, and access to the action group. -The operator's successful interactive query does not prove that the alert identity can read data. +Creating alerts requires workspace read access, rule write access, and action-group access. A +successful interactive query does not prove that the alert identity can read data. ### 6. Test and hand over diff --git a/dotnet/README.md b/dotnet/README.md index f019b43..4d1628f 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -4,23 +4,23 @@ [Documentation](../docs/README.md) | [Settings](#environment-configuration) | [Request behavior](#request-behavior) | [Source](#source) -Implements the shared [contract](../docs/CONTRACT.md) with one provider per deployment. -Target: .NET 8 isolated worker, Azure Functions v4. +This Azure Function implements the shared [contract](../docs/CONTRACT.md) with one provider per +deployment. It uses .NET 8 isolated worker and Azure Functions v4. ## Setup and deployment **Sample deployment:** follow the [five-step guide](../docs/GETTING-STARTED.md), then complete [provider authentication and validation](../docs/ONBOARDING.md#complete-provider-authentication). -**Custom package development:** follow these steps from `dotnet/`: +**Custom development:** from `dotnet/`: 1. Create the [local settings below](#environment-configuration). Select an adapter's fixed `Name` from [Providers/](Src/Providers/), configure it, and complete [provider authentication](../docs/ONBOARDING.md#complete-provider-authentication). 2. Build [dotnet.csproj](dotnet.csproj), run [xUnit tests](tests/Epp.Otp.Tests.csproj), and start Core Tools here, **on loopback only, without tunnels**. It has no Easy Auth. -3. For manual deployment, publish to a compatible .NET isolated Function App. Inspect the output - for private settings and complete the +3. Publish to a compatible .NET isolated Function App and inspect the output for private settings. + Complete the [Easy Auth trust checklist](../docs/ONBOARDING.md#manual-deployment-trust-checklist), then run [authorized deployed checks](../docs/ONBOARDING.md#validate-the-deployed-endpoint) to verify platform authentication. @@ -46,7 +46,7 @@ For local evaluation, start Azurite and replace the test-key placeholder in this ``` For live delivery, use the [shared settings table](../TECHNICAL.md#configure-environment-variables) -for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter-specific options. +for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter options. Keep `Values` as strings, including `EPP_PROVIDER_TIMEOUT_MS`, for example `"1500"`. Replace the placeholders. API-key adapters store credentials in Key Vault. Each selected channel requires a valid endpoint; a missing or invalid endpoint never uses the other channel. @@ -79,7 +79,7 @@ For built-in message and voice mapping, see ## Source -To add Twilio, follow the [Twilio integration guide](../docs/BRING-YOUR-OWN-PROVIDER.md). +To add a provider, follow the [BYOT provider guide](../docs/BRING-YOUR-OWN-PROVIDER.md). Keep caller authentication and public responses in the shared handler. For deployed diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and @@ -92,8 +92,8 @@ provider. `PhoneProviderBase.SendJsonAsync` owns bounded HTTP transpo mapping. Each provider maps its statuses to `ProviderResult`. Request-aware routing or multiple accounts require a separate trust, configuration, credential, and test design. -`CredentialTokenService` invokes the selected provider's credential retrieval for each live -request with a 2.5-second cancellation budget. API-key secrets are read from Key Vault each time. +`CredentialTokenService` gets the selected provider's credentials for each live request with a +2.5-second cancellation budget. API-key secrets are read from Key Vault each time. The OAuth providers share `OAuthCredentialSource` and reuse Azure Identity SDK credential instances, leaving token caching and renewal to the SDK. There is no custom credential cache, hosted warmup service, polling, or retry cooldown. Evaluation does no provider credential work. See diff --git a/javascript/README.md b/javascript/README.md index e273807..7bce432 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -4,8 +4,8 @@ [Documentation](../docs/README.md) | [Settings](#environment-configuration) | [Request behavior](#request-behavior) | [Source](#source) -A Node.js Azure Function implementing the shared [HTTP contract](../docs/CONTRACT.md) with one -provider per deployment. +A Node.js Azure Function for the shared [HTTP contract](../docs/CONTRACT.md), with one provider per +deployment. ## Setup and deployment @@ -13,16 +13,17 @@ provider per deployment. **Sample deployment:** follow the [five-step guide](../docs/GETTING-STARTED.md), then complete [provider authentication and validation](../docs/ONBOARDING.md#complete-provider-authentication). -**Custom package development:** follow these steps from `javascript/`: +**Custom development:** from `javascript/`: 1. Create the [local settings below](#environment-configuration). Select a fixed provider ID from [providers/](src/functions/providers/), configure it, and complete [provider authentication](../docs/ONBOARDING.md#complete-provider-authentication). 2. Install [package.json](package.json) dependencies, run its offline test script, and start Core Tools from this folder. **Bind only to loopback, without tunnels:** Core Tools has no Easy Auth. -3. For manual deployment, [package](../TECHNICAL.md#build-zips-locally) this app, inspect it for - secrets, and complete the [Easy Auth trust checklist](../docs/ONBOARDING.md#manual-deployment-trust-checklist). - Keep Easy Auth enabled for SendOtp, then run +3. [Package](../TECHNICAL.md#build-zips-locally) the app for deployment and inspect it for secrets. + Complete the + [Easy Auth trust checklist](../docs/ONBOARDING.md#manual-deployment-trust-checklist), keep Easy + Auth enabled for SendOtp, then run [authorized deployed checks](../docs/ONBOARDING.md#validate-the-deployed-endpoint) to verify platform authentication. @@ -47,7 +48,7 @@ the test-key placeholder in this minimal setup: ``` For live delivery, use the [shared settings table](../TECHNICAL.md#configure-environment-variables) -for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter-specific options. +for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter options. Keep `Values` as strings, including `EPP_PROVIDER_TIMEOUT_MS`, for example `"1500"`. Replace the placeholders. API-key adapters store credentials in Key Vault. Each selected channel requires a valid endpoint; a missing or invalid endpoint never uses the other channel. @@ -75,14 +76,14 @@ For built-in message and voice mapping, see ## Source -To add Twilio, follow the [Twilio integration guide](../docs/BRING-YOUR-OWN-PROVIDER.md). +To add a provider, follow the [BYOT provider guide](../docs/BRING-YOUR-OWN-PROVIDER.md). Keep caller authentication and public responses in the shared handler. For diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and the [JavaScript event queries](../docs/MONITORING.md#4-javascript-service-event-queries). Each live request reads API-key secrets from Key Vault or calls the reusable Azure Identity -credential's token API. Only the SDK caches tokens and renews them as needed; there is no custom +credential's token API. Only the SDK caches and renews tokens; there is no custom credential cache, startup warmup, polling, or retry cooldown. Evaluation does no provider credential work. Acquisition is bounded to 2.5 seconds, and shutdown aborts pending requests. See [credential acquisition](../TECHNICAL.md#provider-settings-and-credential-acquisition). diff --git a/python/README.md b/python/README.md index 81815ed..9574d90 100644 --- a/python/README.md +++ b/python/README.md @@ -4,23 +4,23 @@ [Documentation](../docs/README.md) | [Settings](#environment-configuration) | [Request behavior](#request-behavior) | [Source](#source) -Implements the shared [contract](../docs/CONTRACT.md) with one provider per deployment. -Target: Python 3.11, Azure Functions v4, Python v2 programming model. +This Azure Function implements the shared [contract](../docs/CONTRACT.md) with one provider per +deployment. It uses Python 3.11, Azure Functions v4, and the Python v2 programming model. ## Setup and deployment **Sample deployment:** follow the [five-step guide](../docs/GETTING-STARTED.md), then complete [provider authentication and validation](../docs/ONBOARDING.md#complete-provider-authentication). -**Custom package development:** follow these steps from `python/`: +**Custom development:** from `python/`: 1. Create the [local settings below](#environment-configuration). Select a fixed provider ID from [providers/](src/providers/), configure it, and complete [provider authentication](../docs/ONBOARDING.md#complete-provider-authentication). 2. Use a virtual environment, install [requirements.txt](requirements.txt) and pytest, and run [tests/](tests/). Start Core Tools here, **on loopback only, without tunnels**. It has no Easy Auth. -3. For manual deployment, publish to a compatible Linux Python Function with dependencies or - remote build. Inspect the package for private settings, complete the +3. Publish to a compatible Linux Python Function with dependencies or remote build. Inspect the + package for private settings, then complete the [Easy Auth trust checklist](../docs/ONBOARDING.md#manual-deployment-trust-checklist), and run [authorized deployed checks](../docs/ONBOARDING.md#validate-the-deployed-endpoint) to verify platform authentication. @@ -46,7 +46,7 @@ For local evaluation, start Azurite and replace the test-key placeholder in this ``` For live delivery, use the [shared settings table](../TECHNICAL.md#configure-environment-variables) -for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter-specific options. +for `EPP_PROVIDER_NAME`, both channel endpoints, authentication, and adapter options. Keep `Values` as strings, including `EPP_PROVIDER_TIMEOUT_MS`, for example `"1500"`. Replace the placeholders. API-key adapters store credentials in Key Vault. Each selected channel requires a valid endpoint; a missing or invalid endpoint never uses the other channel. @@ -73,7 +73,7 @@ For built-in message and voice mapping, see ## Source -To add Twilio, follow the [Twilio integration guide](../docs/BRING-YOUR-OWN-PROVIDER.md). +To add a provider, follow the [BYOT provider guide](../docs/BRING-YOUR-OWN-PROVIDER.md). Keep caller authentication and public responses in the shared handler. For diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and @@ -81,7 +81,7 @@ For diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and Verify which Python logging properties the host/export pipeline preserves. Each live request reads API-key secrets from Key Vault or calls the reusable Azure Identity -credential's token API. Only the SDK caches tokens and renews them as needed; there is no custom +credential's token API. Only the SDK caches and renews tokens; there is no custom credential cache, startup warmup, polling, or retry cooldown. Evaluation does no provider credential work. Request-scoped daemon threads bound the caller's acquisition wait to 2.5 seconds without blocking process shutdown. Timed-out results are discarded; SDK network timeouts bound the diff --git a/setup/docs/README.md b/setup/docs/README.md index cbf1998..151eed1 100644 --- a/setup/docs/README.md +++ b/setup/docs/README.md @@ -2,9 +2,10 @@ [Documentation](../../docs/README.md) | [First deployment](../../docs/GETTING-STARTED.md) | [Troubleshooting](Troubleshooting.md) -**EPP** stands for **External Phone Provider**. This reference covers setup choices, reruns, and -maintenance. For a first deployment, follow the linked [five-step deployment guide](../../docs/GETTING-STARTED.md); it keeps preparation, -deployment, provider validation, policy activation, and handoff in order. +**EPP** means **External Phone Provider**. This reference covers setup choices, reruns, and +maintenance. For a first deployment, follow the +[five-step deployment guide](../../docs/GETTING-STARTED.md). It keeps preparation, deployment, +provider validation, policy activation, and handoff in order. | Task | Section | | --- | --- | @@ -16,15 +17,16 @@ deployment, provider validation, policy activation, and handoff in order. | Validate, activate, and maintain | [Step 3](#step-3---manually-validate-and-activate-policy), [renewal](#encryption-certificate-lifecycle), [rollback](#rollback-and-decommissioning) | | Develop setup changes | [Offline checks](#offline-setup-checks) | -Confirm [provider access and Microsoft's approved procedure](../../docs/ONBOARDING.md#before-purchasing-or-deploying) -before deployment. +Before deployment, confirm +[provider access and Microsoft's approved procedure](../../docs/ONBOARDING.md#before-purchasing-or-deploying). -Setup creates **one regional endpoint**. See [Front Door](../../docs/FRONTDOOR.md) for regional continuity. +Setup creates **one regional endpoint**. For regional continuity, see +[Front Door](../../docs/FRONTDOOR.md). ## Availability -Choose **Infobip, Soprano, or Telesign** and **FC1/EP1** hosting. Setup detects the tenant's replication scope -and selects the **EU** provider route for EU tenants or **Global** otherwise, without a scope picker. +Choose **Infobip, Soprano, or Telesign** and **FC1/EP1** hosting. Setup detects the tenant's +replication scope and selects **EU** routing for EU tenants or **Global** routing otherwise. **JavaScript** is the default language, without a prompt. To use another language, run `.\Setup-Epp.ps1 -Language csharp` or `.\Setup-Epp.ps1 -Language python`. The legacy `-Language dotnet` value remains accepted for C#. @@ -32,7 +34,7 @@ Setup selects `released-package.zip` from the matching language's stable release against that release's `SHA256SUMS.txt` before publishing. See [source/package selection](#source-versioning). -Both channels are configured. Profile objects `deployment.routes.global` and +Setup configures both channels. Profile objects `deployment.routes.global` and `deployment.routes.eu` define `smsEndpoint`, `voiceEndpoint`, tenant, authentication, and timing values. Setup writes the endpoints to `EPP_PROVIDER_SMS_ENDPOINT` and `EPP_PROVIDER_VOICE_ENDPOINT`; OAuth profiles also supply their API app ID and scope. @@ -93,8 +95,8 @@ still apply. These are deployment recommendations, not enforcement or a GDPR com Setup offers these two Linux hosting plans: -Review the [Azure hosting and provider cost estimate](../../docs/COST-CALCULATOR.md), also linked -directly from the service-plan prompt. +Before choosing a plan, review the +[Azure hosting and provider cost estimate](../../docs/COST-CALCULATOR.md), also linked from the prompt. | Option | Hosting | | --- | --- | @@ -133,17 +135,17 @@ For the first deployment, use a nonproduction tenant and subscription. press Enter to accept **EPP Application** or enter a display name (1-256 characters), and choose a provider by name from the alphabetized list (**Infobip, Soprano, Telesign**). Blank provider-name input prompts again; there is no default provider. An explicit `-Provider` skips that provider prompt. -The new registration is only planned at this point: setup creates it after final deployment -approval, not when you choose the menu option. Canceling before approval creates nothing. +Setup creates the planned registration only after final deployment approval, not when you choose +the menu option. Canceling before approval creates nothing. An existing app with the same display name is never adopted automatically. -**Use an existing dedicated registration:** select/search for it or supply its client ID. -To create it manually before setup: +**Use or create a dedicated registration:** select/search for an existing app or supply its client +ID. To create one before setup: 1. In the customer tenant's **Microsoft Entra admin center > App registrations**, register a new application for this endpoint with **Accounts in this organizational directory only** as its initial supported account type; setup changes it to organizational multi-tenant after approval. - No redirect URI, client secret, API permission, or app role is required manually. + Do not add a redirect URI, client secret, API permission, or app role. 2. Record the **Directory (tenant) ID** and **Application (client) ID**. For an existing app, supply its client ID, not the application's object ID. Setup never silently creates a replacement if an existing ID is invalid. @@ -160,7 +162,7 @@ Creation is an interactive choice. Unattended runs still require an existing `-A Setup configures: -- Organizational multitenancy and the Microsoft Entra ID allowed-tenants preview: home tenant, +- Organizational multitenancy and the Microsoft Entra ID allowed-tenants configuration: home tenant, selected provider's Security Store and OAuth token tenants, and preserved existing allowed tenants. - The app's enterprise application with assignment required, `Epp.Invoke`, and its assignment to the Microsoft phone-provider service principal. @@ -250,7 +252,7 @@ Invoke-WebRequest ` -OutFile .\Setup-Epp.ps1 ``` -Review the downloaded script before running it: +Review the script before running it: ```powershell .\Setup-Epp.ps1 @@ -411,8 +413,8 @@ at `Proceed with deployment? Enter Yes or Y to deploy, or No or N to cancel [No] Do not supply `-ApproveDeployment`: that switch authorizes the deployment phase without asking again. -This is a cancellation at the approval gate, not an offline simulation. You can select an existing -registration or plan a new one; neither is created or changed before approval. +This cancels at the approval gate; it is not an offline simulation. Select an existing registration +or plan a new one. Neither changes before approval. Before the plan appears, setup can download tools and packages, request required installation, and sign in through Azure CLI. It also checks permissions and regions, verifies package checksums and contents, and compiles Bicep. @@ -568,8 +570,8 @@ already registered. ### Read the deployment summary Open the timestamped JSON under `epp-output` in a private editor. It contains identifiers and -configuration metadata, not provider credentials or private-key bytes. Keep it access-controlled. -**Save the output: the endpoint URL and Application (client) ID are required for policy updates.** +configuration metadata, not provider credentials or private-key bytes. Restrict access. +**Save it: policy updates require the endpoint URL and Application (client) ID.** | Summary field | How to use it | | --- | --- | diff --git a/setup/docs/Troubleshooting.md b/setup/docs/Troubleshooting.md index 50e137c..12c7ebf 100644 --- a/setup/docs/Troubleshooting.md +++ b/setup/docs/Troubleshooting.md @@ -2,8 +2,9 @@ [Documentation](../../docs/README.md) | [Setup reference](README.md) | [Resume onboarding](../../docs/GETTING-STARTED.md) -Select the failed stage and keep existing resources. Before rerunning, use -[safe recovery](#recover-without-destructive-cleanup). Check provider outcomes before retrying timeouts. +Find the failed stage and keep existing resources. Before rerunning, use +[safe recovery](#recover-without-destructive-cleanup). Check the provider outcome before retrying +a timeout. ## Find your symptom @@ -47,7 +48,7 @@ Keep nonce-match **booleans**, statuses, latency, safe IDs, and delivery outcome 1. **Locate the failure.** Before approval, only sign-in or Bicep installation can change the environment. After approval, partial Azure or Microsoft Entra ID changes can remain. Inspect the error and **resource group > Deployments**. -2. **Fix, then rerun with the same inputs:** tenant, subscription, app, runtime, plan, prefix, +2. **Fix the problem, then rerun with the same inputs:** tenant, subscription, app, runtime, plan, prefix, provider/route, and source/package versions. Active endpoints need a change window; setup has no checkpoint resume or automatic undo. If setup created the app, find its client ID in the printed output or `application-.json` and rerun with `-ApplicationId` and the same @@ -97,7 +98,7 @@ Follow the [Front Door guide](../../docs/FRONTDOOR.md), including readiness and ## Deployment fails with SubscriptionIsOverQuotaForSku **Check:** the selected subscription's regional **EP1 VMs** quota, not authentication or resource -registration. A supported Linux EP1 region can still have zero quota for your subscription. +registration. A supported Linux EP1 region can still have zero quota. 1. Confirm the tenant, subscription, region, and SKU in the plan and Azure error. 2. In **Quotas > App Service**, filter to that subscription/region and inspect **EP1 VMs** usage @@ -321,7 +322,7 @@ do not create another app or delete partially created resources. Azure roles do not grant Graph permissions; the user needs application/service-principal management and app-role assignment permissions, including active Privileged Role Administrator for the approved `Application.Read.All` grant. -4. Confirm beta `signInAudienceRestrictions` availability. If tenant policy/preview access blocks it, +4. Confirm beta `signInAudienceRestrictions` availability. If tenant policy or feature availability blocks it, stop; setup does not silently allow all tenants. Unattended runs still need the user session and separate `-ApproveDeployment`. @@ -394,8 +395,8 @@ delete, purge/recover vaults, activate policy, or roll back. Follow For **Create a new application**, a successful registration can remain even if later steps fail. Its `application-.json` record is not proof of deployment success. If the creation request itself fails or times out, inspect the selected tenant before retrying; setup does not automatically -repeat that non-idempotent request. A permission error requires app-registration rights in addition -to the [other setup permissions](README.md#prerequisites-for-step-2). +repeat that non-idempotent request. A permission error requires app-registration rights plus the +[other setup permissions](README.md#prerequisites-for-step-2). Recognized storage/Key Vault RBAC propagation errors are retried for at most twelve attempts. Transient Function startup errors also have bounded retries. This includes the specific ARM @@ -407,7 +408,7 @@ registration fails. **Verify:** inspect [host/worker logs](../../docs/APPLICATION-INSIGHTS.md#single-region-configuration) for startup/discovery failures. Expected messages include `Worker process started and initialized`, -`Found the following functions: Host.Functions.SendOtp`, and `Job host started`. +`Found functions: Host.Functions.SendOtp`, and `Job host started`. Setup closes public ingress after persistent publication failure. ## The endpoint returns 401 or live delivery fails