Skip to content

Latest commit

 

History

History

dotnet

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

C# implementation

Documentation | Settings | Request behavior | Source

Implements the shared contract with one provider per deployment. Target: .NET 8 isolated worker, Azure Functions v4.

Setup and deployment

Sample deployment: follow the five-step guide, then complete provider authentication and validation.

Custom package development: follow these steps from dotnet/:

  1. Create the local settings below. Select an adapter's fixed Name from Providers/, configure it, and complete provider authentication.
  2. Build dotnet.csproj, run xUnit tests, 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 Easy Auth trust checklist, then run authorized deployed checks to verify platform authentication.

Regional continuity: use the Front Door guide and include .NET-specific package, startup, dependency, and telemetry checks.

Environment configuration

Run Core Tools from dotnet/. Create an untracked local.settings.json beside host.json, starting from the shared sample. For local evaluation, start Azurite and replace the test-key placeholder in this minimal setup:

{
	"IsEncrypted": false,
	"Values": {
		"AzureWebJobsStorage": "UseDevelopmentStorage=true",
		"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
		"EPP_DECRYPTION_KEY_PEM": "<base64 of your local test private PEM>"
	}
}

For live delivery, use the shared settings table for EPP_PROVIDER_NAME, both channel endpoints, authentication, and adapter-specific options. Keep values as strings, including EPP_PROVIDER_TIMEOUT_MS, for example "1500". Replace placeholders and store credentials in the adapter's Key Vault secrets. Each selected channel requires a valid endpoint. An invalid or missing endpoint returns an error without using the other channel.

Core Tools loads Values into environment variables. Direct worker execution and tests do not. Restart after edits. Use a local test PEM because Core Tools cannot resolve Key Vault references. The project excludes local settings from publish output.

In Azure, configure the same app settings and use a Key Vault reference for the private PEM. Provider credentials use managed identity, not a developer CLI login. Keep local evaluation on loopback.

Request behavior

POST /api/SendOtp follows the shared request/response contract. Easy Auth authenticates the caller. The handler then validates and decrypts the payload. It never forwards or returns the incoming Authorization header.

.NET binding runs first: [FromBody] creates EntraSendOtpPayload. Malformed JSON, invalid enum tokens, or other deserialization errors can fail before SendOtp, its ILogger events, or its custom error response. Successfully bound requests retain application validation, privacy-safe logging, and response behavior. See binding differences.

Use mode: 2 or "evaluation" to validate authentication and decryption without a provider send. Live success confirms acceptance, not handset receipt. Check a timeout before retrying.

For built-in message and voice mapping, see Soprano and Telesign.

Source

To add Twilio, follow the Twilio integration guide. Keep caller authentication and public responses in the shared handler.

For deployed diagnostics, use Application Insights and runtime-specific log discovery. The host's OpenTelemetry setting does not by itself initialize a direct worker exporter. verify actual ILogger collection before depending on event-based alerts.

SendOtp validates and decrypts, selects EPP_PROVIDER_NAME, resolves credentials, and calls the provider. PhoneProviderBase.SendJsonAsync<TResponse> owns bounded HTTP transport and typed response 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. The OAuth provider reuses 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 credential acquisition.

Source Purpose
Program.cs Host, provider, credential service and HTTP client registration
Functions/SendOtp.cs HTTP handler, provider selection, credential resolution and delivery orchestration
Src/AppConfig.cs Shared deployment settings
Src/EntraSendOtpPayload.cs Bound request model, strict channel/mode converters and semantic validation
Src/JweDeliveryContext.cs Pinned JWE decryption and decrypted delivery context
Src/CredentialTokenService.cs Bounded provider-supplied credential retrieval for each live request
Src/OtpLog.cs Source-generated, strongly typed structured logging events
Src/PhoneProviderBase.cs Provider extension contract and shared typed JSON/HTTP transport
Src/Providers/ Provider identity, credential delegation and API-specific request/response protocols
Src/SecretResolver.cs Key Vault transport. ISecretResolver.ResolveAsync accepts cancellation.
Src/Models.cs Outcomes and shared records

Derive from PhoneProviderBase, register the provider in Program.cs, and give it the configured name used by the simple SelectProvider policy in SendOtp. Providers own authentication declaration, credential resolution, request construction and private typed response DTOs. Their public SendOtpAsync method delegates to the base SendJsonAsync<TResponse> transport and maps the typed response into ProviderResult. Provider mapping code never handles HttpResponseMessage or raw JSON DOM types. Each provider keeps its own response rules. Common async HTTP transport lives in PhoneProviderBase. See production readiness when planning a live service.