Skip to content

Commit cafd8a8

Browse files
SciencePotatoHou Chi Chan
andauthored
docs: add bring-your-own-provider Twilio adapter guide (#47)
Co-authored-by: Hou Chi Chan <houchichan@microsoft.com> Copilot-Session: af0a60fe-cd74-4bfa-89a7-426b1a9b4549
1 parent 96b0e21 commit cafd8a8

6 files changed

Lines changed: 167 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,5 @@ Deleting Azure resources does not undo an authentication policy change.
250250
| Understand the architecture, settings, or packages | [Technical reference](TECHNICAL.md) and [HTTP contract](docs/CONTRACT.md) |
251251
| Explore multiple Azure regions | [Manual Front Door guide](docs/FRONTDOOR.md); not included in guided setup |
252252
| Build a custom implementation | [JavaScript](javascript/README.md), [C#](dotnet/README.md), or [Python](python/README.md) |
253+
| Extend the code for a customer-owned phone provider | [Bring your own provider guide](docs/BRING-YOUR-OWN-PROVIDER.md); customer-owned code extension, not guided setup support |
253254
| Design primary-to-secondary provider fallback | [Developer walkthrough](docs/MULTI-PROVIDER-IMPLEMENTATION.md); not a built-in feature |

‎TECHNICAL.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ by default. Deploy each language separately, not all three to the same Function
5252
For local development, manual configuration, and deployment checks, see the
5353
[detailed configuration and validation guide](docs/ONBOARDING.md).
5454

55+
To extend these implementations for a customer-owned provider, follow the
56+
[bring your own provider guide](docs/BRING-YOUR-OWN-PROVIDER.md). Its repository checklist
57+
uses Twilio as the context and points to the adapter, registration, settings and packaging files.
58+
It is not a bundled adapter, guided setup option, or statement of tenant activation eligibility.
59+
5560
## Guided EPP setup
5661

5762
Use **[setup](setup/docs/README.md)** for **Step 2: endpoint deployment**. Download only

‎docs/BRING-YOUR-OWN-PROVIDER.md‎

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Bring your own phone provider: repository checklist
2+
3+
To connect your own provider, such as Twilio, add an adapter to **one** language
4+
implementation. Your team owns the provider integration and maintains that code.
5+
This checklist covers the repository changes, not the provider's implementation.
6+
Twilio isn't included in the sample or guided setup.
7+
8+
## 1. Choose one language
9+
10+
1. Start with the [JavaScript](../javascript/README.md), [C#](../dotnet/README.md)
11+
or [Python](../python/README.md) guide and use your own source checkout.
12+
2. Read the [shared contract](CONTRACT.md). Keep Entra's message and code intact,
13+
preserve caller authentication and decryption, and leave evaluation mode
14+
ahead of provider work so it never sends a message.
15+
3. Before purchasing or deploying, confirm eligibility and the testing/activation
16+
process with your tenant administrator and Microsoft using the
17+
[access guidance](ONBOARDING.md#before-purchasing-or-deploying).
18+
Until then, work offline with fake credentials and HTTP responses.
19+
20+
## 2. Create and register your provider
21+
22+
Follow only the section for your language. Add a new file rather than changing an
23+
existing provider. Use the existing implementation for the repository's structure;
24+
your team supplies the new provider's send and response handling.
25+
26+
### JavaScript
27+
28+
1. Add a module in [src/functions/providers](../javascript/src/functions/providers/),
29+
using [telesign.js](../javascript/src/functions/providers/telesign.js) as a reference.
30+
Implement `name`, `authenticationMode`, `credentialSpec`, `createRequest` and
31+
`interpretResponse`. Return the existing
32+
[ProviderResult](../javascript/src/functions/providerResult.js) and use
33+
[providerTransport.js](../javascript/src/functions/providerTransport.js) for sending.
34+
2. Import it in [providers/index.js](../javascript/src/functions/providers/index.js),
35+
add its fixed ID to `selectProvider`, and include it in the exports.
36+
3. Use [credentials.js](../javascript/src/functions/credentials.js) for credential
37+
lookup. [config.js](../javascript/src/functions/config.js) preserves `env`, which
38+
[SendOtp.js](../javascript/src/functions/SendOtp.js) passes to your adapter.
39+
40+
<a id="c-net"></a>
41+
42+
### C# (.NET)
43+
44+
1. Add a class in [Src/Providers](../dotnet/Src/Providers/), deriving from
45+
[PhoneProviderBase](../dotnet/Src/PhoneProviderBase.cs). Use
46+
[TelesignProvider.cs](../dotnet/Src/Providers/TelesignProvider.cs) as a reference
47+
for `Name`, `AuthenticationMode`, `FetchCredentialsAsync` and `SendOtpAsync`.
48+
Keep the shared transport and [ProviderResult](../dotnet/Src/Models.cs).
49+
2. Register the class as another singleton `PhoneProviderBase` in
50+
[Program.cs](../dotnet/Program.cs).
51+
[SendOtp.cs](../dotnet/Functions/SendOtp.cs) already selects registered providers by name.
52+
3. Use the injected [ISecretResolver](../dotnet/Src/ISecretResolver.cs) and
53+
[CredentialTokenService](../dotnet/Src/CredentialTokenService.cs) for credentials.
54+
Read adapter settings through the supplied `IEnv`.
55+
56+
### Python
57+
58+
1. Add a module in [src/providers](../python/src/providers/), deriving from
59+
[PhoneProviderBase](../python/src/provider.py). Use
60+
[telesign.py](../python/src/providers/telesign.py) as a reference for `name`,
61+
`authentication_mode`, `credential_spec`, `build_request` and `map_response`.
62+
Keep the shared transport and [ProviderResult](../python/src/models.py).
63+
2. Import the class in [function_app.py](../python/function_app.py) and add an
64+
instance to the tuple that builds `_providers`.
65+
[providers/__init__.py](../python/src/providers/__init__.py) needs no registration.
66+
3. Use [credentials.py](../python/src/credentials.py) and
67+
[secrets.py](../python/src/secrets.py) for credential lookup.
68+
[config.py](../python/src/config.py) preserves the `env` passed to your adapter.
69+
70+
## 3. Set up credentials and app settings
71+
72+
1. Choose the adapter's credential mode and secret names in its code, and keep
73+
them consistent with your deployment settings. Store provider secrets in
74+
Key Vault; use the sample's managed-identity lookup and grant the Function's
75+
reader identity access. Keep the existing
76+
[credential cache behavior](CONTRACT.md#credential-caching-and-refresh).
77+
2. Start local settings from
78+
[docs/local.settings.sample.json](local.settings.sample.json). Copy it to an
79+
untracked `local.settings.json` beside your runtime's `host.json`, set the
80+
runtime named in its language guide, and keep all `Values` entries as strings.
81+
Use a local test decryption key and fake credentials for offline development.
82+
3. Set `EPP_PROVIDER_NAME` to the ID you registered, match
83+
`EPP_PROVIDER_AUTH_MODE` to the adapter, and add the channel settings and any
84+
extra settings your code actually reads. See the
85+
[configuration reference](CONTRACT.md#4-configuration-app-settings--env).
86+
For local evaluation-only runs, omit provider settings to avoid credential warmup.
87+
88+
The local JSON supplies environment variables to the Functions host. It doesn't
89+
register an adapter, configure Azure, or load itself into unit tests. Apply cloud
90+
settings separately when deploying. Provider profile JSON has a different purpose,
91+
described in the optional setup section below.
92+
93+
<a id="step-7-check-endpoints-timeouts-and-privacy"></a>
94+
95+
## 4. Test the adapter
96+
97+
1. Add tests beside the existing [JavaScript tests](../javascript/test/),
98+
[C# tests](../dotnet/tests/) or [Python tests](../python/tests/).
99+
Check registration, credential failures, settings, both supported channels,
100+
successful and failed responses, timeouts, and evaluation without a send.
101+
2. Keep sends on the shared bounded transport, restrict destinations, and fail
102+
closed on unknown responses. Don't add blind retries: a timed-out send may
103+
already have been accepted. Never log codes, secrets, message bodies or full
104+
phone numbers. Use the [contract](CONTRACT.md#production-limitations) for the
105+
existing safety limits.
106+
3. Run your language's test runner and build before packaging. Keep the handler
107+
response contract unchanged: live acceptance echoes the nonce; failure omits it.
108+
109+
## 5. Package and deploy your changed source
110+
111+
1. Build your package using the
112+
[custom packaging instructions](../TECHNICAL.md#build-zips-locally).
113+
Public release packages won't contain your new adapter. C# source needs publish;
114+
Python source needs the documented Linux remote build.
115+
2. Have your Azure operator deploy that package through your team's deployment
116+
process and apply the settings and vault permissions from step 3.
117+
Follow the [manual trust checklist](ONBOARDING.md#manual-deployment-trust-checklist)
118+
before opening public access. Easy Auth must remain enabled; the handler has
119+
no backup caller-authentication check.
120+
3. Arrange the [deployed-endpoint checks](ONBOARDING.md#validate-the-deployed-endpoint)
121+
with your tenant administrator, including evaluation and receipt on each channel
122+
you intend to use. Tenant activation is a separate supported administrator/Microsoft
123+
process, not something adding a provider file or deploying a package enables.
124+
125+
## 6. Optional: extend the guided setup
126+
127+
Skip this for the custom-package route above. If your team also wants to maintain
128+
its own setup integration, review these files together:
129+
130+
1. [setup/providers/telesign.json](../setup/providers/telesign.json) shows an existing
131+
deployment profile; [setup/providers/catalog.json](../setup/providers/catalog.json)
132+
lists the profiles offered by setup. These files describe setup choices and
133+
deployment values. **The Function doesn't read them to create or register providers.**
134+
2. [Epp.Setup.psm1](../setup/support/Epp.Setup.psm1) validates profiles and maps their
135+
values into settings; [resources.bicep](../setup/infra/resources.bicep) applies
136+
them. Review the required provider-tenant, authentication and route fields,
137+
trust configuration, extra adapter settings and provider-specific setup behavior.
138+
Don't reuse another provider's tenant or assume a new JSON file is sufficient.
139+
3. [setup/packages/catalog.json](../setup/packages/catalog.json) and
140+
[Epp.Packages.ps1](../setup/support/Epp.Packages.ps1) select and verify release
141+
assets. Those assets must include your adapter.
142+
[Setup-Epp.ps1](../setup/Setup-Epp.ps1) downloads supporting files from a selected,
143+
commit-pinned source; editing local JSON doesn't change what it downloads.
144+
Test your customized setup with [Providers.Tests.ps1](../setup/tests/Providers.Tests.ps1)
145+
and the other [setup tests](../setup/tests/), alongside the modified package,
146+
before offering it to customers.
147+
148+
The current guided catalog has no Twilio profile. This optional work is a customer
149+
setup extension, not built-in provider support.

‎dotnet/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ six-digit numeric run that is not part of a longer number and repeats the comple
112112

113113
## Source
114114

115+
To add a customer-owned provider, follow the
116+
[repository checklist](../docs/BRING-YOUR-OWN-PROVIDER.md#c-net).
117+
It shows the files to create or update, not built-in provider or guided setup support.
118+
115119
For deployed diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and
116120
[runtime-specific log discovery](../docs/MONITORING.md#runtime-specific-log-discovery).
117121
The host's OpenTelemetry setting does not by itself initialize a direct worker exporter;

‎javascript/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ retries. The shared contract defines validation, HTTP outcomes and privacy-safe
116116

117117
## Source and extension points
118118

119+
To add a customer-owned provider, follow the
120+
[repository checklist](../docs/BRING-YOUR-OWN-PROVIDER.md#javascript).
121+
It shows the files to create or update, not built-in provider or guided setup support.
122+
119123
For deployed diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and the
120124
[JavaScript service-event queries](../docs/MONITORING.md#4-javascript-service-event-queries).
121125
Logs are JSON `service` events ending in `request_completed`, not a `logType: "request"` summary.

‎python/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ six-digit numeric run that is not part of a longer number and repeats the comple
106106

107107
## Source
108108

109+
To add a customer-owned provider, follow the
110+
[repository checklist](../docs/BRING-YOUR-OWN-PROVIDER.md#python).
111+
It shows the files to create or update, not built-in provider or guided setup support.
112+
109113
For deployed diagnostics, use [Application Insights](../docs/APPLICATION-INSIGHTS.md) and
110114
[runtime-specific log discovery](../docs/MONITORING.md#runtime-specific-log-discovery).
111115
Python emits fixed messages with `event_name` and other logging extras, not a universal JSON

0 commit comments

Comments
 (0)