An ASP.NET Core 9 Razor Pages web application with Azure AD authentication, mutual TLS (mTLS), Azure Key Vault certificate management, Azure Cosmos DB, Azure Blob Storage, AWS Secrets Manager, Amazon DynamoDB, Google Cloud Secret Manager, Google Cloud Firestore, and a hardened HTTP security layer with nonce-based Content Security Policy.
This documentation is available in the following languages. Each language folder contains a README, Security Reviews, and translated guides.
| Language | Folder |
|---|---|
| English (en-US) | docs/en-US/ |
| Afrikaans (af-ZA) | docs/af-ZA/ |
| Amharic (am-ET) | docs/am-ET/ |
| Arabic (ar-SA) | docs/ar-SA/ |
| Bengali (bn-BD) | docs/bn-BD/ |
| German (de-DE) | docs/de-DE/ |
| Spanish (es-ES) | docs/es-ES/ |
| French (fr-FR) | docs/fr-FR/ |
| Irish (ga-IE) | docs/ga-IE/ |
| Hausa (ha-NG) | docs/ha-NG/ |
| Hawaiian (haw-US) | docs/haw-US/ |
| Hindi (hi-IN) | docs/hi-IN/ |
| Haitian Creole (ht-HT) | docs/ht-HT/ |
| Italian (it-IT) | docs/it-IT/ |
| Japanese (ja-JP) | docs/ja-JP/ |
| Korean (ko-KR) | docs/ko-KR/ |
| Māori (mi-NZ) | docs/mi-NZ/ |
| Dutch (nl-NL) | docs/nl-NL/ |
| Portuguese (pt-PT) | docs/pt-PT/ |
| Russian (ru-RU) | docs/ru-RU/ |
| Samoan (sm-WS) | docs/sm-WS/ |
| Swahili (sw-KE) | docs/sw-KE/ |
| Yoruba (yo-NG) | docs/yo-NG/ |
| Chinese Simplified (zh-CN) | docs/zh-CN/ |
| Chinese Traditional (zh-HK) | docs/zh-HK/ |
- Features
- Feature Flags
- Prerequisites
- Installation – Windows Azure (App Service)
- Installation – OpenBSD Server communicating with Azure Services
- Configuration Reference
- Supporting Scripts
- Security Notes
The application authenticates users through Microsoft Identity Platform using the OpenID Connect protocol (via Microsoft.Identity.Web). All routes under /Experimental require an authenticated Azure AD identity. The /Privacy, /Error, and /About pages are publicly accessible. The [Authorize] attribute on HomeController enforces authentication across all MVC actions.
When enabled, the application requires connecting clients to present a valid X.509 certificate. Settings in MtlsSettings control:
- Whether to allow chained certificates, self-signed certificates, or both
- Certificate revocation checking (X.509 CRL / online mode)
- Allowed certificate issuers (checked as a case-insensitive substring match against the certificate's
IssuerDN)
The Kestrel web server is configured with ClientCertificateMode.RequireCertificate when mTLS is on, and ClientCertificateMode.AllowCertificate in development or when mTLS is off.
The application retrieves the TLS server certificate from Azure Key Vault at startup. The Key Vault client uses Azure AD client credentials (client ID + client secret) from configuration. The loaded X509Certificate2 is injected directly into Kestrel's HTTPS defaults so no PFX file needs to exist on disk.
An OcspValidationService stub is included for validating client certificates against an OCSP (Online Certificate Status Protocol) server. The service supports configurable:
- Enable/disable per environment
- Request timeout and retry count
- In-memory caching of OCSP responses (configurable duration)
- Fail-closed, fail-open, or warn-only behavior when the OCSP server is unavailable
Note: The actual OCSP wire-protocol implementation (
PerformOcspValidationAsync) is a stub that rejects all certificates until a production implementation is supplied.
When enabled, every HTTP response carries a Content-Security-Policy header whose script-src directive includes a cryptographically random nonce generated per request. The nonce is:
- Generated/refreshed by
NonceRefresherServiceusing AES-CBC encryption with a configurable 32-byte key and 16-byte IV stored in configuration (or User Secrets). - Catalogued in a thread-safe
ConcurrentDictionarybyNonceCatalogService. - Injected into every response by
NonceMiddlewareand placed inHttpContext.Items["Nonce"]so Razor views can embed it in<script>tags.
The CSP also supports SHA-256 hash-based allow-listing of inline scripts via a flat text file (wwwroot/csp-hashes.txt) and an optional manually specified hash in configuration.
UseStandardSecurityHeaders appends the following headers to every response:
X-Frame-Options: DENYX-Content-Type-Options: nosniffStrict-Transport-Security: max-age=31536000; includeSubDomainsReferrer-Policy: strict-origin-when-cross-originCross-Origin-Opener-Policy: same-originCross-Origin-Resource-Policy: same-sitePermissions-Policydisabling geolocation, camera, microphone, and FLoC (interest-cohort)- Removal of
Server,X-Powered-By, andX-AspNetMvc-Versionresponse headers Cache-Control: no-cache, no-store, must-revalidate
When enabled, BlobSettingsService provides a scoped service backed by a connection string and a configurable maximum attachment count. The connection string is expected to be stored in User Secrets or Azure Key Vault, never in source control.
When enabled, the application verifies the Cosmos DB connection at startup by calling database.ReadAsync(). CosmosDbService wraps a CosmosClient singleton and is bound to a configurable database and container. The connection string and account key are secrets stored outside source control.
When enabled, AwsSecretsManagerOperationsService provides a template stub for fetching secrets and TLS certificates from AWS Secrets Manager (the AWS equivalent of Azure Key Vault). It mirrors the interface of AzureKeyVaultOperationsService:
FetchSecret(secretName)— retrieve any named secret by ARN or name.FetchCertificate()— retrieve the PFX server certificate.FetchSecretIVSecret()/FetchSecretNonceKeySecret()— retrieve nonce encryption material.
The underlying AwsSecretManagerOperations class logs a warning and returns empty values until a production implementation is supplied. AWS credentials (AccessKeyId, SecretAccessKey) must be stored in User Secrets or environment variables — never in source control.
When enabled, AwsDynamoDbService wraps an IAmazonDynamoDB client singleton (the AWS equivalent of Azure Cosmos DB). At startup the service verifies connectivity by calling DescribeTable. The service exposes GetTableAsync() and GetTableName() for downstream use. AWS credentials must be stored outside source control.
When enabled, GcpSecretManagerOperationsService provides a template stub for fetching secrets and TLS certificates from Google Cloud Secret Manager (the GCP equivalent of Azure Key Vault). It mirrors the interface of AzureKeyVaultOperationsService:
FetchSecret(secretId)— retrieve any named secret by ID.FetchCertificate()— retrieve the PFX server certificate.FetchSecretIVSecret()/FetchSecretNonceKeySecret()— retrieve nonce encryption material.
The underlying GcpSecretManagerOperations class logs a warning and returns empty values until a production implementation is supplied. Authentication uses Application Default Credentials (ADC) by default; a service-account JSON key file path can optionally be supplied via GcpSecretManager:CredentialFilePath.
When enabled, GcpFirestoreService wraps a FirestoreDb singleton (the GCP equivalent of Azure Cosmos DB). At startup the application builds a Firestore client bound to the configured project ID and collection name. The service exposes GetCollection(), GetCollectionName(), and GetDatabase() for downstream use. Authentication uses ADC or a service-account JSON key file.
When enabled, AddAwsCognitoAuthentication configures OpenID Connect authentication against an AWS Cognito User Pool — the AWS equivalent of Microsoft Entra ID / Azure AD. The middleware consumes Cognito's standards-compliant OIDC discovery endpoint:
https://cognito-idp.{Region}.amazonaws.com/{UserPoolId}/.well-known/openid-configuration
Configuration is under the AwsCognito section: Region, UserPoolId, AppClientId, AppClientSecret (store in User Secrets), and Domain (the Cognito hosted-UI domain). The callback path defaults to /signin-aws-cognito.
When enabled, AddGcpIdentityAuthentication configures OpenID Connect authentication using Google's OAuth 2.0 / OpenID Connect endpoint — the GCP equivalent of Microsoft Entra ID / Azure AD. The middleware consumes Google's standard OIDC discovery endpoint:
https://accounts.google.com/.well-known/openid-configuration
Configuration is under the GcpIdentity section: ClientId, ClientSecret (store in User Secrets), and optional ProjectId for logging. The callback path defaults to /signin-gcp. Obtain a client ID and secret from the Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client IDs.
Sessions use in-process distributed memory cache with a 30-minute idle timeout. Session cookies are configured as:
HttpOnly = trueSecure = Always(HTTPS-only)SameSite = Strict
The application supports 25 languages with per-view .resx resource files. The active culture is determined at the request pipeline level via RequestLocalizationOptions (Accept-Language header, query string, or cookie). Users can switch language at any time using the language picker in the navigation bar.
| Culture Tag | Language |
|---|---|
en-US |
English (United States) — default |
de-DE |
Deutsch (German) |
es-ES |
Español (Spanish) |
fr-FR |
Français (French) |
pt-PT |
Português (Portuguese) |
it-IT |
Italiano (Italian) |
zh-HK |
廣東話 (Cantonese — Hong Kong Traditional Chinese) |
ko-KR |
한국어 (Korean) |
hi-IN |
हिन्दी (Hindi) |
ru-RU |
Русский (Russian) |
ar-SA |
العربية (Arabic — right-to-left layout) |
sw-KE |
Kiswahili (Swahili) |
ja-JP |
日本語 (Japanese) |
ht-HT |
Kreyòl ayisyen (Haitian Creole) |
haw-US |
ʻŌlelo Hawaiʻi (Hawaiian) |
sm-WS |
Gagana Samoa (Samoan) |
mi-NZ |
Te Reo Māori (Māori) |
af-ZA |
Afrikaans |
nl-NL |
Nederlands (Dutch) |
ha-NG |
Hausa |
am-ET |
አማርኛ (Amharic) |
yo-NG |
Yorùbá (Yoruba) |
bn-BD |
বাংলা (Bengali) |
zh-CN |
普通话 (Mandarin Chinese — Simplified) |
ga-IE |
Gaeilge (Irish) |
Right-to-left (RTL) layout is activated automatically when Arabic is selected: the <html> element receives dir="rtl", and the lang attribute carries the full BCP-47 culture tag (e.g. ar-SA) for correct browser behaviour.
LoggingHelper hashes personally identifiable information in log output using HMAC-SHA256. A stable 32-byte key can be supplied via Logging:PiiHmacKey (stored in User Secrets). If the key is absent or invalid, a random key is generated at startup so PII is never logged in plaintext.
All major subsystems are controlled by boolean feature flags in appsettings.json. Each flag defaults to a safe state.
| Flag | Default | Description |
|---|---|---|
EnableSession |
true |
Server-side session and session cookie |
EnableLocalization |
true |
Multi-language support (en-US, de-DE, es-ES, fr-FR, pt-PT, it-IT, zh-HK, ko-KR, hi-IN, ru-RU, ar-SA, sw-KE, ja-JP, ht-HT, haw-US, sm-WS, mi-NZ, af-ZA, nl-NL, ha-NG, am-ET, yo-NG, bn-BD, zh-CN, ga-IE) |
EnableAzureAd |
true |
Azure AD / OpenID Connect authentication |
EnableAuthorization |
true |
Route-level authorization policies |
EnableKeyVault |
false |
Load TLS server cert from Azure Key Vault |
EnableNonceServices |
false |
Per-request CSP nonce generation |
EnableCSP |
false |
Attach Content-Security-Policy header |
EnableSecurityHeaders |
true |
Attach standard HTTP security headers |
EnableBlobStorage |
false |
Azure Blob Storage service |
EnableCosmosDb |
false |
Azure Cosmos DB service |
EnableMtls |
false |
Require client TLS certificates |
EnableOcspValidation |
false |
OCSP certificate revocation check (stub) |
EnableAwsSecretsManager |
false |
AWS Secrets Manager service (stub) |
EnableAwsDynamoDb |
false |
Amazon DynamoDB service |
EnableAwsCognito |
false |
AWS Cognito OpenID Connect identity management |
EnableGcpSecretManager |
false |
GCP Secret Manager service (stub) |
EnableGcpFirestore |
false |
Google Cloud Firestore service |
EnableGcpIdentity |
false |
GCP Identity Platform (Google OAuth 2.0 / OIDC) |
The following must be in place before deploying on either platform:
- Azure AD App Registration – with a redirect URI pointing to your hostname, a client secret or certificate credential, and (optionally) API permissions.
- Azure Key Vault – containing the PFX server certificate as a secret. The app registration must have
Getpermission on secrets. - Azure Cosmos DB account (optional) – with a database and container matching your configuration.
- Azure Blob Storage account (optional) – with a connection string.
- AWS credentials (optional) – an IAM user or role with
secretsmanager:GetSecretValueand/ordynamodb:DescribeTablepermissions for the AWS features. For AWS Cognito, create a User Pool, an App Client, and enable the hosted UI with the required OAuth 2.0 scopes. - GCP service account or ADC (optional) – a service account with
secretmanager.versions.accessand/ordatastore.databases.getIAM roles for the GCP features. For GCP Identity, create OAuth 2.0 credentials in the Google Cloud Console and enable the Google Identity API. - .NET 9 SDK / Runtime – version 9.0 or later.
# Log in
az login
# Create a resource group
az group create --name MyResourceGroup --location eastus
# Create an App Service plan (Linux or Windows)
az appservice plan create --name MyPlan --resource-group MyResourceGroup --sku B1 --is-linux
# Create the web app (.NET 9)
az webapp create --name MyWebApp26 --resource-group MyResourceGroup \
--plan MyPlan --runtime "DOTNETCORE:9.0"In the Azure Portal:
- Navigate to Microsoft Entra ID → App registrations → New registration.
- Set the redirect URI to
https://<your-app>.azurewebsites.net/signin-oidc. - Under Certificates & secrets, create a client secret and copy the value.
- Note the Tenant ID and Client ID from the Overview blade.
az keyvault create --name MyKeyVault26 --resource-group MyResourceGroup --location eastus
# Upload your PFX as a Key Vault secret (base64-encoded)
$pfxBase64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes("server.pfx"))
az keyvault secret set --vault-name MyKeyVault26 --name "ServerCert" --value $pfxBase64
# Grant the App Service Managed Identity access
az keyvault set-policy --name MyKeyVault26 \
--object-id <managed-identity-object-id> \
--secret-permissions get listCopy appsettings.template.json to appsettings.json and fill in the placeholder values. Secrets must not be stored in source control — set them as App Service Application Settings or via User Secrets locally:
# In Azure App Service, set secrets as app settings:
az webapp config appsettings set --name MyWebApp26 --resource-group MyResourceGroup --settings \
"AzureAd__TenantId=<TENANT_ID>" \
"AzureAd__ClientId=<CLIENT_ID>" \
"AzureAd__ClientSecret=<CLIENT_SECRET>" \
"AzureKeyVault__KeyVaultURL=https://MyKeyVault26.vault.azure.net/" \
"AzureKeyVault__KeyVaultSecret=<KV_SECRET>" \
"AzureKeyVault__KeyVaultPassName=ServerCert" \
"FeatureFlags__EnableKeyVault=true" \
"FeatureFlags__EnableAzureAd=true"dotnet publish -c Release -o ./publish
cd publish
zip -r ../app.zip .
az webapp deployment source config-zip \
--name MyWebApp26 --resource-group MyResourceGroup --src ../app.zip# Force HTTPS
az webapp update --name MyWebApp26 --resource-group MyResourceGroup --https-only true
# Bind a custom domain and managed TLS certificate
az webapp config hostname add --webapp-name MyWebApp26 --resource-group MyResourceGroup \
--hostname www.example.com
az webapp config ssl bind --certificate-thumbprint <THUMBPRINT> \
--name MyWebApp26 --resource-group MyResourceGroup --ssl-type SNIAzure App Service supports client certificates via the portal:
- Go to App Service → TLS/SSL settings → Client certificates.
- Set Incoming client certificates to Require.
Then set FeatureFlags__EnableMtls=true in Application Settings.
Important: .NET 9 does not have an official Microsoft build for OpenBSD. The instructions below use a Linux-compatible container (via Podman, which is available in OpenBSD's package tree) to run the ASP.NET Core 9 application on OpenBSD while communicating with Azure services over HTTPS.
# As root
pkg_add podman
pkg_add curl gitIf neither Podman nor Docker is available for your OpenBSD version, consider running the app in a Linux VM (e.g., vmm(4) with a Debian/Ubuntu guest) and following the standard Linux deployment path from within that guest.
podman pull mcr.microsoft.com/dotnet/aspnet:9.0On a machine with the .NET 9 SDK installed, publish a self-contained build targeting Linux x64:
dotnet publish WebAppExperimental26/WebAppExperimental26.csproj \
-c Release -r linux-x64 --self-contained true -o ./publishTransfer the publish/ directory to the OpenBSD host (e.g., via scp or a shared volume).
On the OpenBSD host, create /etc/webappexp26/appsettings.json with your production values (no secrets in the file; use environment variables instead):
{
"AllowedHosts": "your.hostname.example.com",
"FeatureFlags": {
"EnableAzureAd": true,
"EnableKeyVault": true,
"EnableSecurityHeaders": true,
"EnableMtls": false
},
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID",
"ClientId": "YOUR_CLIENT_ID",
"CallbackPath": "/signin-oidc"
},
"AzureKeyVault": {
"KeyVaultURL": "https://YOUR_KEYVAULT_NAME.vault.azure.net/",
"KeyVaultPassName": "ServerCert"
}
}Secrets are injected as environment variables in the next step.
podman run -d \
--name webappexp26 \
-p 443:8443 \
-v /etc/webappexp26:/app/config:ro \
-v /path/to/publish:/app:ro \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ASPNETCORE_URLS="https://+:8443" \
-e AzureAd__ClientSecret="YOUR_CLIENT_SECRET" \
-e AzureKeyVault__KeyVaultSecret="YOUR_KV_SECRET" \
-e Logging__PiiHmacKey="YOUR_32_BYTE_BASE64_KEY" \
mcr.microsoft.com/dotnet/aspnet:9.0 \
dotnet /app/WebAppExperimental26.dll \
--contentRoot /app \
--configDir /app/configAdd to /etc/pf.conf to allow inbound HTTPS and permit outbound connections to Azure endpoints:
# Allow inbound HTTPS
pass in on egress proto tcp to port 443
# Allow outbound to Azure AD, Key Vault, Cosmos DB, Blob Storage
pass out on egress proto tcp to port { 443 }
Reload the ruleset:
pfctl -f /etc/pf.confEnsure the hostname in AllowedHosts resolves to the OpenBSD server's public IP. Azure AD requires the redirect URI (/signin-oidc) to be reachable over HTTPS, so the server certificate must be trusted. Use a certificate from a public CA (e.g., Let's Encrypt via acme-client(1)) or upload a CA-signed certificate to Azure Key Vault and enable EnableKeyVault.
The following Azure service endpoints must be reachable from the OpenBSD host over TCP 443:
| Service | Endpoint |
|---|---|
| Azure AD / Microsoft Identity | login.microsoftonline.com |
| Azure Key Vault | <vault-name>.vault.azure.net |
| Azure Cosmos DB | <account>.documents.azure.com |
| Azure Blob Storage | <account>.blob.core.windows.net |
When AWS services are enabled, add the relevant regional endpoints (e.g. secretsmanager.us-east-1.amazonaws.com, dynamodb.us-east-1.amazonaws.com). When GCP services are enabled, add secretmanager.googleapis.com and firestore.googleapis.com.
Test connectivity before starting the container:
curl -I https://login.microsoftonline.com
curl -I https://YOUR_KEYVAULT_NAME.vault.azure.netCopy appsettings.template.json to appsettings.json and replace all {{PLACEHOLDER}} values.
| Section | Key | Description |
|---|---|---|
AzureAd |
TenantId, ClientId, ClientSecret |
Azure AD app registration |
AzureKeyVault |
KeyVaultURL, KeyVaultSecret, KeyVaultPassName |
Key Vault and certificate name |
MtlsSettings |
RequireClientCertificate, AllowedIssuers |
mTLS client cert policy |
NonceEncryption |
Key, IV |
32-byte key and 16-byte IV for nonce encryption (base64) |
BlobSettings |
BlobConnectionString, MaxAttachments |
Blob Storage connection |
CosmosDb |
CosmosConnectionString, DatabaseName, ContainerName |
Cosmos DB connection |
OcspSettings |
OcspServerUrl, CacheDurationMinutes |
OCSP validation (stub) |
Logging |
PiiHmacKey |
32-byte base64 HMAC key for PII hashing in logs |
AwsSecretsManager |
Region, CertificateSecretName, IVSecretName, NonceKeySecretName, AccessKeyId, SecretAccessKey |
AWS Secrets Manager (stub) |
AwsDynamoDb |
Region, TableName, AccessKeyId, SecretAccessKey |
Amazon DynamoDB |
AwsCognito |
Region, UserPoolId, AppClientId, AppClientSecret, Domain, CallbackPath |
AWS Cognito OIDC identity |
GcpSecretManager |
ProjectId, CertificateSecretId, IVSecretId, NonceKeySecretId, CredentialFilePath |
GCP Secret Manager (stub) |
GcpFirestore |
ProjectId, DatabaseId, CollectionName, CredentialFilePath |
Google Cloud Firestore |
GcpIdentity |
ClientId, ClientSecret, ProjectId, CallbackPath |
GCP Identity Platform (Google OAuth 2.0 / OIDC) |
Generate encryption keys and IVs using the included PowerShell script:
.\WebAppExperimental26\SupportingScripts\IVandKeySampleGenerator.ps1Store all secrets in .NET User Secrets for local development:
dotnet user-secrets set "AzureAd:ClientSecret" "YOUR_SECRET"
dotnet user-secrets set "AzureKeyVault:KeyVaultSecret" "YOUR_KV_SECRET"
dotnet user-secrets set "NonceEncryption:Key" "YOUR_BASE64_KEY"
dotnet user-secrets set "NonceEncryption:IV" "YOUR_BASE64_IV"For AWS services:
dotnet user-secrets set "AwsSecretsManager:AccessKeyId" "YOUR_AWS_ACCESS_KEY_ID"
dotnet user-secrets set "AwsSecretsManager:SecretAccessKey" "YOUR_AWS_SECRET_ACCESS_KEY"
dotnet user-secrets set "AwsDynamoDb:AccessKeyId" "YOUR_AWS_ACCESS_KEY_ID"
dotnet user-secrets set "AwsDynamoDb:SecretAccessKey" "YOUR_AWS_SECRET_ACCESS_KEY"
dotnet user-secrets set "AwsCognito:AppClientSecret" "YOUR_COGNITO_APP_CLIENT_SECRET"For GCP services, set the GOOGLE_APPLICATION_CREDENTIALS environment variable or use the CredentialFilePath setting to point to a service-account JSON key file.
dotnet user-secrets set "GcpIdentity:ClientSecret" "YOUR_GOOGLE_CLIENT_SECRET"The SupportingScripts/ directory contains PowerShell utilities:
| Script | Purpose |
|---|---|
IVandKeySampleGenerator.ps1 |
Generate a random 32-byte AES key and 16-byte IV (base64) |
HashInlineScriptPowerShell.ps1 |
Compute SHA-256 hashes for inline scripts (for CSP allow-listing) |
HashInlineScriptPowerShellBase64Output.ps1 |
Same as above, outputs hashes in base64 format |
CertificateUploaderToAzureExample.ps1 |
Upload a PFX certificate to Azure Key Vault |
CheckRoles.ps1 |
Verify Azure RBAC role assignments for the app |
ExportResourceGroups.ps1 |
Export Azure resource group configurations |
TroubleshootingCosmosDBInfo.ps1 |
Diagnose Cosmos DB connectivity |
SetupFromTemplate.ps1 |
Automate initial configuration from appsettings.template.json |
- Never commit secrets (
ClientSecret,KeyVaultSecret, connection strings, encryption keys, AWS/GCP credentials) to source control. Use .NET User Secrets locally and Azure App Settings / Key Vault references in production. - The OCSP validation implementation is a stub that rejects all certificates. Replace
PerformOcspValidationAsyncinOcspValidationService.csbefore enablingEnableOcspValidationin production. - The AWS Secrets Manager and GCP Secret Manager implementations are stubs that log a warning and return empty values. Replace the method bodies in
AwsSecretManagerOperationsandGcpSecretManagerOperationsbefore enabling those features in production. - Nonce values are never logged — logging a nonce in plaintext would allow an attacker with log access to inject arbitrary inline scripts.
- The
Serverresponse header is masked towebserverto avoid exposing platform information. - Review
AllowSelfSignedCertificates = false(default) before deploying mTLS; self-signed certificates should only be used in development. - AWS
AccessKeyIdandSecretAccessKeymust never appear inappsettings.json— use User Secrets, environment variables, or IAM instance roles. - For AWS Cognito, prefer IAM roles or Cognito Identity Pools over static credentials; never commit
AppClientSecretto source control. - GCP credentials should use Application Default Credentials (ADC) (e.g., Workload Identity on GKE, or
gcloud auth application-default loginlocally) rather than committing service-account JSON files. - For GCP Identity, the
ClientSecretmust be stored in User Secrets or environment variables — never inappsettings.json.