|
| 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. |
0 commit comments