namespace NpgsqlRestClient;
public static partial class ConfigSchemaGenerator
{
///
/// Full JSONC template for --config output.
/// This is the exact content of appsettings.json with all comments preserved.
///
internal const string DefaultConfigJsonc = """
{
//
// The application name used to set the application name property in connection string by "NpgsqlRest.SetApplicationNameInConnection" or the "NpgsqlRest.UseJsonApplicationName" settings.
// It is the name of the top-level directory if set to null.
//
"ApplicationName": null,
//
// Production or Development
//
"EnvironmentName": "Production",
//
// Specify the urls the web host will listen on. See https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.hostingabstractionswebhostbuilderextensions.useurls?view=aspnetcore-8.0
//
"Urls": "http://localhost:8080",
//
// Logs at startup, format placeholders:
// {time} - startup time
// {urls} - listening on urls
// {version} - current version
// {environment} - EnvironmentName
// {application} - ApplicationName
//
// Note: This message is logged at Information level. To disable this message, set to empty string.
//
"StartupMessage": "Started in {time}, listening on {urls}, version {version}",
//
// Configuration settings
//
"Config": {
//
// Add the environment variables to configuration.
// When enabled, environment variables will override the settings in this configuration file but can be overridden by command line arguments.
// Complex hierarchical keys can be defined using double underscore as a separator.
// For example, "ConnectionStrings__Default" environment variable will override the "ConnectionStrings.Default" setting in this configuration file.
//
"AddEnvironmentVariables": false,
//
// When set, configuration values will be parsed for environment variables in the format {ENV_VAR_NAME}
// and replaced with the value of the environment variable when available.
//
"ParseEnvironmentVariables": true,
//
// Path to a .env file containing environment variables.
// When AddEnvironmentVariables or ParseEnvironmentVariables is true and this file exists,
// variables from this file will be loaded and made available for configuration parsing.
// Format: KEY=VALUE (one per line)
//
"EnvFile": null,
//
// Validate configuration keys against known defaults at startup.
// "Ignore" - no validation
// "Warning" - log warnings for unknown keys, continue startup (default)
// "Error" - log errors for unknown keys and exit
//
"ValidateConfigKeys": "Warning"
},
//
// List of named connection strings to PostgreSQL databases.
// The "Default" connection string is used when no connection name is specified.
// For connection string definition see https://www.npgsql.org/doc/connection-string-parameters.html
//
"ConnectionStrings": {
"Default": "Host={PGHOST};Port=5432;Database={PGDATABASE};Username={PGUSER};Password={PGPASSWORD}"
},
//
// Additional connection settings and options.
//
"ConnectionSettings": {
//
// Sets the ApplicationName connection property in the connection string to the value of the ApplicationName configuration.
// Note: This option is ignored if the UseJsonApplicationName option is enabled.
//
"SetApplicationNameInConnection": true,
//
// Sets the ApplicationName connection property dynamically on every request in the following format:
// {"app":"","uid":"","id":""}
// Note: The ApplicationName connection property is limited to 64 characters.
//
"UseJsonApplicationName": false,
//
// Test any connection string before initializing the application and using it. The connection string is tested by opening and closing the connection.
//
"TestConnectionStrings": true,
//
// Connection open retry options.
//
"RetryOptions": {
"Enabled": true,
//
// Retry sequence in seconds. Accepts decimal numbers (0.25 is quarter of a second). The length of the array determines the maximum number of retries.
//
"RetrySequenceSeconds": [1, 3, 6, 12],
//
// Error codes that will trigger a retry when opening a connection. See https://www.postgresql.org/docs/current/errcodes-appendix.html
//
"ErrorCodes": [
"08000", "08003", "08006", "08001", "08004", // Connection failure codes
"55P03", // Lock not available
"55006", // Object in use
"53300", // Too many connections
"57P03", // Cannot connect now
"40001" // Serialization failure (can be retried)
]
},
//
// The connection name in ConnectionStrings configuration that will be used to execute the metadata query. If this value is null, the default connection string will be used.
//
"MetadataQueryConnectionName": null,
//
// Set the search path to this schema before executing the metadata query function.
// When null (default), no search path is set and the server's default search path is used.
//
// This is needed when using non superuser connection roles with limited schema access and mapping the metadata function to a specific schema.
// If the connection string contains the same "Search Path=" it will be skipped.
//
"MetadataQuerySchema": null,
// Any: Any successful connection is acceptable.
// Primary: Server must not be in hot standby mode (pg_is_in_recovery() must return false).
// Standby: Server must be in hot standby mode (pg_is_in_recovery() must return true).
// PreferPrimary: First try to find a primary server, but if none of the listed hosts is a primary server, try again in Any mode.
// PreferStandby: First try to find a standby server, but if none of the listed hosts is a standby server, try again in Any mode.
// ReadWrite: Session must accept read-write transactions by default (that is, the server must not be in hot standby mode and the default_transaction_read_only parameter must be off).
// ReadOnly: Session must not accept read-write transactions by default (the converse).
// see https://www.npgsql.org/doc/failover-and-load-balancing.html
"MultiHostConnectionTargets": {
// all connections use the same target mode
"Default": "Any",
// per connection overrides { "name": "Primary|Standby|Any|PreferPrimary|PreferStandby|ReadWrite|ReadOnly" }
"ByConnectionName": { }
}
},
//
// Enable to invoke UseKestrelHttpsConfiguration. See https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.hosting.webhostbuilderkestrelextensions.usekestrelhttpsconfiguration?view=aspnetcore-8.0
//
"Ssl": {
"Enabled": false,
//
// Adds middleware for redirecting HTTP Requests to HTTPS. See https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.httpspolicybuilderextensions.usehttpsredirection?view=aspnetcore-8.0
//
"UseHttpsRedirection": true,
//
// Adds middleware for using HSTS, which adds the Strict-Transport-Security header. See https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.hstsbuilderextensions.usehsts?view=aspnetcore-2.1
//
"UseHsts": true
},
//
// Data protection settings. Encryption/decryption settings for Auth Cookies, Antiforgery tokens and custom data protection needs.
//
"DataProtection": {
"Enabled": false,
//
// Set to null to use the current "ApplicationName" value.
// This value determines encryption type or class. Meaning, different application names will not be able to decrypt each other's data.
//
"CustomApplicationName": null,
//
// Sets the default lifetime in days of keys created by the data protection system.
// Represents a number of days how long before keys are rotated.
//
"DefaultKeyLifetimeDays": 90,
//
// Data protection location: "Default", "FileSystem" or "Database"
//
// Note: When running on Linux, using Default location means keys will not be persisted.
// When keys are lost on restart, encrypted tokens (auth) will also not work on restart.
// Linux users should use FileSystem or Database storage.
//
"Storage": "Default",
//
// FileSystem storage path. Set to a valid path when using FileSystem.
// Note: When running in Docker environment, the path must be a Docker volume path to persist the keys.
//
"FileSystemPath": "./data-protection-keys",
//
// GetAllElements database command. Expected to return rows with a single column of type text.
//
"GetAllElementsCommand": "select get_data_protection_keys()",
//
// StoreElement database command. Receives two parameters: name and data of type text. Doesn't return anything.
//
"StoreElementCommand": "call store_data_protection_keys($1,$2)",
//
// Configure encryption algorithms for data protection keys or null to use the default algorithm.
// Values: AES_128_CBC, AES_192_CBC, AES_256_CBC, AES_128_GCM, AES_192_GCM, AES_256_GCM
//
"EncryptionAlgorithm": null,
//
// Configure validation algorithms for data protection keys or null to use the default algorithm.
// Values: HMACSHA256, HMACSHA512
//
"ValidationAlgorithm": null,
//
// Key encryption method: "None", "Certificate", or "Dpapi" (Windows only)
// None: Keys are not encrypted at rest (default)
// Certificate: Keys are encrypted using an X.509 certificate
// Dpapi: Keys are encrypted using Windows Data Protection API (Windows only)
//
"KeyEncryption": "None",
//
// Path to the X.509 certificate file (.pfx) when using Certificate key encryption.
//
"CertificatePath": null,
//
// Password for the certificate file. Can be null for certificates without password.
// For security, consider using environment variable reference: "${CERT_PASSWORD}"
//
"CertificatePassword": null,
//
// When using Dpapi key encryption, set to true to protect keys to the local machine.
// If false (default), keys are protected to the current user account.
//
"DpapiLocalMachine": false
},
//
// Uncomment to configure Kestrel web server and to add certificates
// See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-9.0
//
"Kestrel": {
// "Endpoints": {
// "Http": {
// "Url": "http://localhost:5000"
// },
// "HttpsInlineCertFile": {
// "Url": "https://localhost:5001",
// "Certificate": {
// "Path": "",
// "Password": "$CREDENTIAL_PLACEHOLDER$"
// }
// },
// "HttpsInlineCertAndKeyFile": {
// "Url": "https://localhost:5002",
// "Certificate": {
// "Path": "",
// "KeyPath": "",
// "Password": "$CREDENTIAL_PLACEHOLDER$"
// }
// },
// "HttpsInlineCertStore": {
// "Url": "https://localhost:5003",
// "Certificate": {
// "Subject": "",
// "Store": "",
// "Location": "",
// "AllowInvalid": ""
// }
// },
// "HttpsDefaultCert": {
// "Url": "https://localhost:5004"
// }
// },
// "Certificates": {
// "Default": {
// "Path": "",
// "Password": "$CREDENTIAL_PLACEHOLDER$"
// }
// },
// "Limits": {
// "MaxConcurrentConnections": 100,
// "MaxConcurrentUpgradedConnections": 100,
// "MaxRequestBodySize": 30000000,
// "MaxRequestBufferSize": 1048576,
// "MaxRequestHeaderCount": 100,
// "MaxRequestHeadersTotalSize": 32768,
// "MaxRequestLineSize": 8192,
// "MaxResponseBufferSize": 65536,
// "KeepAliveTimeout": "00:02:00",
// "RequestHeadersTimeout": "00:00:30",
// "Http2": {
// "MaxStreamsPerConnection": 100,
// "HeaderTableSize": 4096,
// "MaxFrameSize": 16384,
// "MaxRequestHeaderFieldSize": 8192,
// "InitialConnectionWindowSize": 65535,
// "InitialStreamWindowSize": 65535,
// "MaxReadFrameSize": 16384,
// "KeepAlivePingDelay": "00:00:30",
// "KeepAlivePingTimeout": "00:01:00",
// "KeepAlivePingPolicy": "WithActiveRequests"
// },
// "Http3": {
// "MaxRequestHeaderFieldSize": 8192
// }
// },
// "DisableStringReuse": false,
// "AllowAlternateSchemes": false,
// "AllowSynchronousIO": false,
// "AllowResponseHeaderCompression": true,
// "AddServerHeader": true,
// "AllowHostHeaderOverride": false
},
//
// Thread pool configuration settings for optimizing application performance
//
"ThreadPool": {
//
// Minimum number of worker threads in the thread pool. Set to null to use system defaults.
//
"MinWorkerThreads": null,
//
// Minimum number of completion port threads. Set to null to use system defaults.
//
"MinCompletionPortThreads": null,
//
// Maximum number of worker threads in the thread pool. Set to null to use system defaults.
//
"MaxWorkerThreads": null,
//
// Maximum number of completion port threads. Set to null to use system defaults.
//
"MaxCompletionPortThreads": null
},
//
// Authentication and Authorization settings
//
"Auth": {
//
// Enable Cookie Auth
//
"CookieAuth": false,
//
// Authentication scheme name for cookie authentication. Set to null to use default.
//
"CookieAuthScheme": "Cookies",
//
// Cookie validity duration in Postgres interval syntax: e.g. "14 days", "12 hours", "30 minutes".
// Set to null to fall back to the framework default (14 days).
//
"CookieValid": "14 days",
//
// Custom name for the authentication cookie. Set to null to use default.
//
"CookieName": null,
//
// Path scope for the authentication cookie. Set to null to use default.
//
"CookiePath": null,
//
// Domain scope for the authentication cookie. Set to null to use default.
//
"CookieDomain": null,
//
// Allow multiple concurrent sessions for the same user.
//
"CookieMultiSessions": true,
//
// Make cookie accessible only via HTTP (not JavaScript).
//
"CookieHttpOnly": true,
//
// Controls the SameSite attribute on the authentication cookie. Accepted values:
// "Strict" — cookie sent only on same-site requests. Most restrictive; CSRF-safe.
// "Lax" — cookie sent on same-site requests and top-level cross-site GETs (default).
// "None" — cookie sent on all cross-site requests. REQUIRED for cross-origin SPAs /
// mobile clients calling this API from a different origin. Browsers drop
// "SameSite=None" cookies without the Secure attribute, so CookieSecure
// must be set to "Always".
// "Unspecified" — omit the SameSite attribute entirely (legacy browser behavior).
// Set to null to use ASP.NET Core's default (typically "Lax").
//
"CookieSameSite": null,
//
// Controls when the cookie's Secure attribute is set. Accepted values:
// "SameAsRequest" — Secure is set only when the request itself is HTTPS (default).
// "Always" — Secure is always set; browsers only send the cookie over HTTPS. REQUIRED
// alongside CookieSameSite="None" for cross-origin auth.
// "None" — Secure is never set; cookies are sent over HTTP as well as HTTPS.
// Set to null to use ASP.NET Core's default ("SameAsRequest").
//
"CookieSecure": null,
//
// Enable Microsoft Bearer Token Auth (proprietary format, not JWT)
//
"BearerTokenAuth": false,
//
// Authentication scheme name for bearer token authentication. Set to null to use default.
//
"BearerTokenAuthScheme": "BearerToken",
//
// Bearer token expiration in Postgres interval syntax: e.g. "1 hour", "30 minutes", "2 days".
// Set to null to fall back to the framework default (1 hour).
//
"BearerTokenExpire": "1 hour",
// POST { "refresh": "{{refreshToken}}" }
"BearerTokenRefreshPath": "/api/token/refresh",
//
// Enable standard JWT (JSON Web Token) Bearer Authentication
//
"JwtAuth": false,
//
// Authentication scheme name for JWT authentication. Set to null to fall back to the framework default.
//
"JwtAuthScheme": "Bearer",
//
// Secret key used to sign JWT tokens. Must be at least 32 characters for HS256.
// IMPORTANT: Use a strong, unique secret in production. Store securely (e.g., environment variable).
//
"JwtSecret": null,
//
// JWT issuer (iss claim). Identifies the principal that issued the JWT.
//
"JwtIssuer": null,
//
// JWT audience (aud claim). Identifies the recipients that the JWT is intended for.
//
"JwtAudience": null,
//
// JWT access token expiration in Postgres interval syntax: e.g. "60 minutes", "1 hour", "30 seconds".
// Set to null to fall back to the framework default (60 minutes).
//
"JwtExpire": "60 minutes",
//
// JWT refresh token expiration in Postgres interval syntax: e.g. "7 days", "168 hours", "1 week".
// Set to null to fall back to the framework default (7 days).
//
"JwtRefreshExpire": "7 days",
//
// Validate the issuer (iss) claim. Set to true if JwtIssuer is configured.
//
"JwtValidateIssuer": false,
//
// Validate the audience (aud) claim. Set to true if JwtAudience is configured.
//
"JwtValidateAudience": false,
//
// Validate the token lifetime (exp claim). Default is true.
//
"JwtValidateLifetime": true,
//
// Validate the signing key. Default is true.
//
"JwtValidateIssuerSigningKey": true,
//
// Clock skew to apply when validating token lifetime. Format: PostgreSQL interval.
// Default is 5 minutes to account for clock differences between servers.
//
"JwtClockSkew": "5 minutes",
//
// URL path for JWT token refresh endpoint. POST with { "refreshToken": "..." }
// Returns new access token and refresh token pair.
//
"JwtRefreshPath": "/api/jwt/refresh",
//
// Named additional authentication schemes. Each entry registers a fully-fledged ASP.NET Core
// authentication scheme alongside the main one. A login function returning a scheme name in its
// `scheme` column signs the user in under that scheme — useful for "short-lived sensitive
// session", "separate admin scope", or "different JWT signing key per scope" patterns alongside
// the normal long-lived primary scheme.
//
// Each scheme has a `Type`: `Cookies`, `BearerToken`, or `Jwt`. Schemes inherit any unset field
// from the root Auth section so blocks stay small. See the type-specific override fields below.
//
// Validation: scheme name must not collide with the main scheme names (CookieAuthScheme,
// BearerTokenAuthScheme, JwtAuthScheme). Explicit `CookieName` values must be unique across all
// schemes. Refresh paths (BearerTokenRefreshPath / JwtRefreshPath) must be unique across all
// schemes that define one. Disabled schemes (`Enabled: false`) are skipped at startup.
//
"Schemes": {
// Example: a short-lived single-session cookie for sensitive operations (admin area, payment flow).
// Login functions can return `'short_session'` in the scheme column to sign users in under this scheme.
"short_session": {
"Type": "Cookies",
"Enabled": false,
"CookieValid": "1 hour",
"CookieMultiSessions": false
},
// Example: a separate Microsoft bearer-token scheme with a shorter expiration than the main one.
// Each scheme can declare its own refresh path; if set, it must be unique across schemes.
"api_token": {
"Type": "BearerToken",
"Enabled": false,
"BearerTokenExpire": "30 minutes",
"BearerTokenRefreshPath": "/api/api-token/refresh"
},
// Example: a separate JWT scheme with its own signing secret (different blast radius from the
// main JWT) and a much shorter access-token expiration. Inherits any unset JWT field from the
// root Auth section. JwtSecret must be ≥32 characters for HS256.
"admin_jwt": {
"Type": "Jwt",
"Enabled": false,
"JwtSecret": null,
"JwtIssuer": null,
"JwtAudience": null,
"JwtExpire": "5 minutes",
"JwtRefreshExpire": "1 hour",
"JwtRefreshPath": "/api/admin-jwt/refresh"
}
},
//
// Enable external auth providers
//
"External": {
"Enabled": false,
//
// sessionStorage key to store the status of the external auth process returned by the signin page.
// The value is HTTP status code (200 for success, 401 for unauthorized, 403 for forbidden, etc.)
//
"BrowserSessionStatusKey": "__external_status",
//
// sessionStorage key to store the message of the external auth process returned by the signin page.
//
"BrowserSessionMessageKey": "__external_message",
//
// Path to the signin page to handle the external auth process. Redirect to this page to start the external auth process.
// Format placeholder {0} is the provider name in lowercase (google, linkedin, github, etc.)
//
"SigninUrl": "/signin-{0}",
//
// Sign in page template. Format placeholders {0} is the provider name, {1} is the script to redirect to the external auth provider.
//
"SignInHtmlTemplate": "Talking To {0}Loading...{1}",
//
// URL to redirect after the external auth process is completed. Usually this is resolved from the request automatically. Except when it's not.
//
"RedirectUrl": null,
//
// Path to redirect after the external auth process is completed.
//
"ReturnToPath": "/",
//
// Query string key to store the path to redirect after the external auth process is completed.
// Use this to set dynamic return path. If this query string key is not found, the ReturnToPath value is used.
//
"ReturnToPathQueryStringKey": "return_to",
//
// Login command to execute after the external auth process is completed. There are five positional and optional parameters:
// $1 - external login provider (if parameter exists, type text).
// $2 - external login email (if parameter exists, type text).
// $3 - external login name (if parameter exists, type text).
// $4 - external login JSON data received (if parameter exists, type text, JSON or JSONB).
// $5 - client browser analytics JSON data (if parameter exists, type text, JSON or JSONB).
//
// The command uses the same rules as the login enabled routine.
// See: "NpgsqlRest.“LoginPath"
//
"LoginCommand": "select * from external_login($1,$2,$3,$4,$5)",
//
// Browser client analytics data that will be sent as JSON to external auth command as the 5th parameter if supplied.
//
"ClientAnalyticsData": "{timestamp:new Date().toISOString(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,screen:{width:window.screen.width,height:window.screen.height,colorDepth:window.screen.colorDepth,pixelRatio:window.devicePixelRatio,orientation:screen.orientation.type},browser:{userAgent:navigator.userAgent,language:navigator.language,languages:navigator.languages,cookiesEnabled:navigator.cookieEnabled,doNotTrack:navigator.doNotTrack,onLine:navigator.onLine,platform:navigator.platform,vendor:navigator.vendor},memory:{deviceMemory:navigator.deviceMemory,hardwareConcurrency:navigator.hardwareConcurrency},window:{innerWidth:window.innerWidth,innerHeight:window.innerHeight,outerWidth:window.outerWidth,outerHeight:window.outerHeight},location:{href:window.location.href,hostname:window.location.hostname,pathname:window.location.pathname,protocol:window.location.protocol,referrer:document.referrer},performance:{navigation:{type:performance.navigation?.type,redirectCount:performance.navigation?.redirectCount},timing:performance.timing?{loadEventEnd:performance.timing.loadEventEnd,loadEventStart:performance.timing.loadEventStart,domComplete:performance.timing.domComplete,domInteractive:performance.timing.domInteractive,domContentLoadedEventEnd:performance.timing.domContentLoadedEventEnd}:null}}",
//
// Client IP address that will be added to the client analytics data under this JSON key.
//
"ClientAnalyticsIpKey": "ip",
//
// External providers
//
"Google": {
//
// visit https://console.cloud.google.com/apis/ to configure your Google app and get your client id and client secret
//
"Enabled": false,
"ClientId": "",
"ClientSecret": "",
"AuthUrl": "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id={0}&redirect_uri={1}&scope=openid profile email&state={2}",
"TokenUrl": "https://oauth2.googleapis.com/token",
"InfoUrl": "https://www.googleapis.com/oauth2/v3/userinfo",
"EmailUrl": null
},
"LinkedIn": {
//
// visit https://www.linkedin.com/developers/apps/ to configure your LinkedIn app and get your client id and client secret
//
"Enabled": false,
"ClientId": "",
"ClientSecret": "",
"AuthUrl": "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id={0}&redirect_uri={1}&state={2}&scope=r_liteprofile%20r_emailaddress",
"TokenUrl": "https://www.linkedin.com/oauth/v2/accessToken",
"InfoUrl": "https://api.linkedin.com/v2/me",
"EmailUrl": "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements//(handle~))"
},
"GitHub": {
//
// visit https://github.com/settings/developers/ to configure your GitHub app and get your client id and client secret
//
"Enabled": false,
"ClientId": "",
"ClientSecret": "",
"AuthUrl": "https://github.com/login/oauth/authorize?client_id={0}&redirect_uri={1}&state={2}&allow_signup=false",
"TokenUrl": "https://github.com/login/oauth/access_token",
"InfoUrl": "https://api.github.com/user",
"EmailUrl": null
},
"Microsoft": {
//
// visit https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade to configure your Microsoft app and get your client id and client secret
// Documentation: https://learn.microsoft.com/en-us/entra/identity-platform/
//
"Enabled": false,
"ClientId": "",
"ClientSecret": "",
"AuthUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id={0}&redirect_uri={1}&scope=openid%20profile%20email&state={2}",
"TokenUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"InfoUrl": "https://graph.microsoft.com/oidc/userinfo",
"EmailUrl": null
},
"Facebook": {
//
// visit https://developers.facebook.com/apps/ to configure your Facebook app and get your client id and client secret
// Documentation: https://developers.facebook.com/docs/facebook-login/
//
"Enabled": false,
"ClientId": "",
"ClientSecret": "",
"AuthUrl": "https://www.facebook.com/v20.0/dialog/oauth?response_type=code&client_id={0}&redirect_uri={1}&scope=public_profile%20email&state={2}",
"TokenUrl": "https://graph.facebook.com/v20.0/oauth/access_token",
"InfoUrl": "https://graph.facebook.com/me?fields=id,name,email",
"EmailUrl": null
}
},
//
// WebAuthn/FIDO2 Passkey Authentication
// Provides phishing-resistant, passwordless authentication using device-native biometrics or PINs.
//
"PasskeyAuth": {
//
// Enable passkey authentication.
//
"Enabled": false,
//
// Enable registration endpoints.
//
"EnableRegister": false,
//
// Rate limiter policy name to apply to all passkey endpoints.
// It is recommended to enable rate limiting on passkey endpoints to protect against brute-force attacks.
// Set to the name of a configured rate limiter policy, or null to disable rate limiting.
//
"RateLimiterPolicy": null,
//
// Optional connection name for named DataSource or ConnectionString lookup.
// If null, uses the default DataSource or ConnectionString from NpgsqlRest options.
//
"ConnectionName": null,
//
// Command retry strategy name from CommandRetryOptions.Strategies.
// Set to null to disable command retry for passkey endpoints.
//
"CommandRetryStrategy": "default",
//
// Relying Party ID (domain name). Should match your application domain (e.g., "example.com").
// If null, auto-detected from the request host.
// Note: IP addresses are not permitted - use "localhost" for local development.
//
"RelyingPartyId": null,
//
// Human-readable Relying Party name displayed to users during registration and authentication.
// If null, uses the ApplicationName from configuration.
//
"RelyingPartyName": null,
//
// Allowed origins for origin validation (scheme + domain + port).
// Example: ["https://example.com", "https://www.example.com"]
// If empty, auto-detected from the request.
// Note: IP addresses are not permitted - use "http://localhost:port" for local development.
//
"RelyingPartyOrigins": [],
//
// Post path for adding a passkey to an existing authenticated user (options).
// Post any additional data in the body as JSON (e.g., { "deviceName": "My Phone" }).
// Requires authentication. Set to null to disable this endpoint.
//
"AddPasskeyOptionsPath": "/api/passkey/add/options",
//
// Post path for adding a passkey to an existing authenticated user (completion).
// Post the WebAuthn response data in the body as JSON (challengeId, credentialId, attestationObject, clientDataJSON, transports).
// Additional JSON body fields are userContext passed through to the CompleteAddExistingUserCommand and optional analyticsData.
// Requires authentication. Set to null to disable this endpoint.
//
"AddPasskeyPath": "/api/passkey/add",
//
// Post path for registration options (new user with passkey).
// Post the user registration data the body as JSON (e.g., { "user_name": "...", "user_display_name": "...", "deviceName": "My Phone" }).
// No authentication required. Set to null to disable registration.
//
"RegistrationOptionsPath": "/api/passkey/register/options",
//
// Post path for registration completion (new user with passkey).
// Post the WebAuthn response data in the body as JSON (challengeId, credentialId, attestationObject, clientDataJSON, transports).
// Additional JSON body fields are userContext passed through to the CompleteAddExistingUserCommand and optional analyticsData.
// No authentication required. Set to null to disable registration.
//
"RegistrationPath": "/api/passkey/register",
//
// Post path for the login options endpoint.
// Post the user login data in the body as JSON (e.g., { "user_name": "..." } ).
// Posting the user_name is optional when using discoverable credentials. When discoverable credentials ate not enabled on the authenticator, user_name is required.
//
"LoginOptionsPath": "/api/passkey/login/options",
//
// Post path for the login completion endpoint.
// Post the WebAuthn response data in the body as JSON (challengeId, credentialId, authenticatorData, clientDataJSON, signature, userHandle) and optional analyticsData.
//
"LoginPath": "/api/passkey/login",
//
// Challenge timeout in minutes. Challenges not used within this time will expire.
//
"ChallengeTimeoutMinutes": 5,
//
// User verification requirement:
// - "preferred": Request UV if available, but allow authentication without it
// - "required": Require UV, fail if not available
// - "discouraged": Don't request UV (not recommended for most use cases)
//
// Practical implications:
// - "required": User MUST authenticate with biometric (fingerprint, face) or device PIN.
// High security - proves the person is present, not just possession of the device.
// - "preferred": Browser will request biometric/PIN if available, but allows passkey
// authentication even if UV isn't supported (e.g., older security keys).
// - "discouraged": Just proves device possession, no biometric/PIN prompt. Lower security.
//
// For most apps, use "preferred". For banking/sensitive apps, use "required".
//
"UserVerificationRequirement": "required",
//
// Resident key (discoverable credential) requirement:
// - "preferred": Request discoverable credentials if supported
// - "required": Require discoverable credentials, fail if not supported
// - "discouraged": Request non-discoverable credentials
//
// Practical implications:
// - "required": True passwordless. Browser shows passkey picker with all accounts at login.
// User picks account and authenticates with biometric/PIN. No username input needed.
// - "preferred"/"discouraged": User enters username first, then authenticates with passkey.
//
// For passwordless flows (no username field), set to "required".
//
"ResidentKeyRequirement": "required",
//
// Attestation conveyance preference - controls whether the server requests the authenticator
// to provide cryptographic proof of its identity (make/model) and security properties during registration.
//
// Options:
// - "none": Don't request attestation. Accept any valid authenticator without verifying its identity.
// Best for most apps - simpler, better user privacy, wider device compatibility. (Recommended)
// - "indirect": Request attestation but allow the browser/platform to anonymize it. Rarely useful.
// - "direct": Request full attestation certificate chain from the authenticator.
// Use when you need to verify the authenticator vendor/model meets security requirements.
// - "enterprise": Request enterprise-specific attestation for managed corporate devices
// where IT needs to verify only organization-approved hardware authenticators are used.
//
// When to use non-"none" values:
// - Banking/financial apps requiring hardware security keys only
// - Enterprise environments restricting to specific authenticator models
// - Compliance requirements mandating certain security certifications (FIDO2 L1/L2)
//
// For most consumer applications, "none" is the correct choice - you just want the user
// to authenticate securely, not audit their hardware.
//
"AttestationConveyance": "none",
//
// Whether to validate and update the signature counter (sign count).
// When true, validates that the new sign count is greater than stored, and updates it after authentication.
// When false, skips sign count validation and update entirely.
// Set to false if authenticators don't support it or you want to simplify your database schema.
//
"ValidateSignCount": true,
//
// SQL command to create a challenge when adding a passkey to an existing authenticated user.
// Parameters:
// - $1 = claims (json): JSON object with user claims from the authenticated session
// - $2 = body (json): JSON object from request body (e.g., { "deviceName": "My Phone" })
// Expected return columns (by name):
// - status (int): HTTP status code. Return 200 to proceed, any other status aborts.
// - message (text): Error message when status != 200.
// - challenge (text): Base64-encoded random challenge bytes (typically 32 bytes).
// - challenge_id: Server-side identifier (uuid, int, bigint, or text).
// - user_handle (text): Base64-encoded random bytes (typically 32 bytes) for WebAuthn user.id.
// - user_name (text): Username displayed in the authenticator UI.
// - user_display_name (text): Display name shown in the authenticator UI.
// - exclude_credentials (text): JSON array of existing credentials.
// - user_context (json): Opaque JSON passed through to CompleteAddExistingUserCommand.
// Called by AddPasskeyOptionsPath endpoint
//
"ChallengeAddExistingUserCommand": "select * from passkey_challenge_add_existing($1,$2)",
//
// SQL command to create a challenge for standalone registration (new user).
// Parameter: $1 = JSON object from request body (e.g., { "user_name": "...", "display_name": "..." })
// Expected return columns (by name): Same as ChallengeAddExistingUserCommand
// - user_context should NOT contain "id" field (distinguishes from add-existing-user flow)
// Called by StandaloneRegistrationOptionsPath endpoint
//
"ChallengeRegistrationCommand": "select * from passkey_challenge_registration($1)",
//
// SQL command to create a challenge for authentication.
// Parameters:
// - $1 = user_name (text, optional - null for discoverable credential flow)
// - $2 = body (json): JSON object from request body (e.g., { "deviceInfo": "..." })
// Expected return columns (by name): status, message, challenge, challenge_id, allow_credentials
// Called by AuthenticationOptionsPath endpoint
//
"ChallengeAuthenticationCommand": "select * from passkey_challenge_authentication($1,$2)",
//
// Used by: Flow 1, Flow 2, Flow 3 (ALL flows)
// SQL command to verify and consume a challenge.
// Parameters: $1 = challenge_id (uuid, int, bigint, or text), $2 = operation (text: "registration" or "authentication")
// Returns: challenge (bytea) - the original challenge bytes, or NULL if not found/expired
// Called by all endpoints
//
"VerifyChallengeCommand": "select * from passkey_verify_challenge($1,$2)",
//
// SQL command to get credential data for authentication.
// Parameter: $1 = credential_id (bytea)
// Expected return columns (by name): status, message, public_key, public_key_algorithm, sign_count, user_context
// Note: user_context is passed through to CompleteAuthenticateCommand (typically contains user_id)
// Called by AuthenticatePath endpoint
//
"AuthenticateDataCommand": "select * from passkey_authenticate_data($1)",
//
// SQL command to complete adding a passkey to an existing user account.
// Parameters:
// - $1 = credential_id (bytea): Unique credential identifier from authenticator.
// - $2 = user_handle (bytea): WebAuthn user.id from registration options.
// - $3 = public_key (bytea): Public key in COSE format.
// - $4 = algorithm (int): COSE algorithm identifier (-7 for ES256, -257 for RS256).
// - $5 = transports (text[]): Transport hints (e.g., ["internal", "hybrid"]).
// - $6 = backup_eligible (boolean): Whether credential can be backed up/synced.
// - $7 = user_context (json): Opaque JSON from ChallengeAddExistingUserCommand (contains user ID).
// - $8 = analytics_data (json, optional): Client analytics with server-added IP.
// Expected return columns (by name): status, message
// Called by RegisterPath endpoint
//
"CompleteAddExistingUserCommand": "select * from passkey_complete_add_existing($1,$2,$3,$4,$5,$6,$7,$8)",
//
// SQL command to complete standalone passkey registration (creates new user).
// Parameters: Same as CompleteAddExistingUserCommand
// - user_context should NOT contain "id" field (creates new user instead of linking to existing)
// Expected return columns (by name): status, message
// Called by RegisterPath endpoint
//
"CompleteRegistrationCommand": "select * from passkey_complete_registration($1,$2,$3,$4,$5,$6,$7,$8)",
//
// Flow 3: Login -> AuthenticatePath endpoint (after signature validation)
// SQL command to update sign count and return user claims.
// Parameters:
// - $1 = credential_id (bytea)
// - $2 = new_sign_count (bigint)
// - $3 = user_context (json): Opaque JSON from AuthenticateDataCommand
// - $4 = analytics_data (json, optional): Client analytics with server-added IP
// Expected return columns (by name): status, user_id, user_name, user_roles (plus any custom claims)
// Called by AuthenticatePath endpoint
//
"CompleteAuthenticateCommand": "select * from passkey_complete_authenticate($1,$2,$3,$4)",
//
// The JSON key name used to add the client's IP address to the analytics data server-side.
// Set to null or empty string to disable IP address collection.
//
"ClientAnalyticsIpKey": "ip",
//
// Column name configuration for database responses
//
"StatusColumnName": "status",
"MessageColumnName": "message",
"ChallengeColumnName": "challenge",
"ChallengeIdColumnName": "challenge_id",
"UserNameColumnName": "user_name",
"UserDisplayNameColumnName": "user_display_name",
"UserHandleColumnName": "user_handle",
"ExcludeCredentialsColumnName": "exclude_credentials",
"AllowCredentialsColumnName": "allow_credentials",
"PublicKeyColumnName": "public_key",
"PublicKeyAlgorithmColumnName": "public_key_algorithm",
"SignCountColumnName": "sign_count"
}
},
//
// Serilog settings
//
"Log": {
//
// See https://github.com/serilog/serilog/wiki/Configuration-Basics#minimum-level
// Verbose, Debug, Information, Warning, Error, Fatal.
// Note: NpgsqlRest logger applies to main application logger, which will, by default have the name defined in the ApplicationName setting.
//
"MinimalLevels": {
"NpgsqlRest": "Information",
"NpgsqlRestClient": "Information",
"System": "Warning",
"Microsoft": "Warning"
},
//
// Enable logging to console output.
//
"ToConsole": true,
//
// Minimum log level for console output: Verbose, Debug, Information, Warning, Error, Fatal.
//
"ConsoleMinimumLevel": "Verbose",
//
// Enable logging to file system.
//
"ToFile": false,
//
// File path for log files.
//
"FilePath": "logs/log.txt",
//
// Maximum size limit for log files in bytes before rolling to a new file.
//
"FileSizeLimitBytes": 30000000,
//
// Minimum log level for file output: Verbose, Debug, Information, Warning, Error, Fatal.
//
"FileMinimumLevel": "Verbose",
//
// Maximum number of log files to retain.
//
"RetainedFileCountLimit": 30,
//
// Create a new log file when size limit is reached.
//
"RollOnFileSizeLimit": true,
//
// Enable logging to PostgreSQL database.
//
"ToPostgres": false,
// $1 - log level text, $2 - message text, $3 - timestamp with tz in utc, $4 - exception text or null, $5 - source context
//
// PostgreSQL command to execute for database logging. Parameters: $1=level, $2=message, $3=timestamp, $4=exception, $5=source.
//
"PostgresCommand": "call log($1,$2,$3,$4,$5)",
//
// Minimum log level for PostgreSQL output: Verbose, Debug, Information, Warning, Error, Fatal.
//
"PostgresMinimumLevel": "Verbose",
//
// Enable OpenTelemetry protocol (OTLP) logging output. Requires an OTLP collector endpoint.
//
"ToOpenTelemetry": false,
"OTLPEndpoint": "http://localhost:4317",
"OTLPProtocol": "Grpc", // "Grpc" or "HttpProtobuf"
"OTLResourceAttributes": {
"service.name": "{application}", // application name from the ApplicationName setting
"service.version": "1.0", // application version, set to a static value or use a build process to update it
"service.environment": "{environment}" // environment name from the EnvironmentName setting
},
"OTLPHeaders": {},
"OTLPMinimumLevel": "Verbose",
//
// See https://github.com/serilog/serilog/wiki/Formatting-Output
//
"OutputTemplate": "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message:lj} [{SourceContext}]{NewLine}{Exception}"
},
//
// Response compression settings
//
"ResponseCompression": {
//
// Enable response compression for HTTP responses.
//
"Enabled": false,
//
// Enable response compression for HTTPS responses.
//
"EnableForHttps": false,
//
// Use Brotli compression algorithm when supported by client.
//
"UseBrotli": true,
//
// Use Gzip compression as fallback when Brotli is not supported.
//
"UseGzipFallback": true,
//
// Compression level: Optimal, Fastest, NoCompression, SmallestSize.
//
"CompressionLevel": "Optimal",
//
// MIME types to include for compression.
//
"IncludeMimeTypes": [
"text/plain",
"text/css",
"application/javascript",
"text/javascript",
"text/html",
"application/xml",
"text/xml",
"application/json",
"text/json",
"image/svg+xml",
"font/woff",
"font/woff2",
"application/font-woff",
"application/font-woff2"
],
//
// MIME types to exclude from compression.
//
"ExcludeMimeTypes": []
},
//
// Antiforgery Token Configuration: Protects against Cross-Site Request Forgery (CSRF/XSRF) attacks.
// CSRF attacks occur when a malicious site tricks a user's browser into making unwanted requests to your application
// using the user's authenticated session (cookies).
//
// How it works:
// 1. Server generates a unique token for each session/request
// 2. Token is embedded in forms (hidden field) or sent via header (for AJAX)
// 3. On state-changing requests (POST, PUT, DELETE), server validates the token
// 4. Requests without valid tokens are rejected (400 Bad Request)
//
// Usage in HTML forms:
//
//
// Usage in AJAX/JavaScript:
// fetch('/api/endpoint', {
// method: 'POST',
// headers: { 'RequestVerificationToken': tokenValue },
// body: JSON.stringify(data)
// });
//
// Note: Antiforgery automatically sets the X-Frame-Options: SAMEORIGIN header to help prevent clickjacking.
// If you're using the SecurityHeaders middleware with X-Frame-Options, the Antiforgery header takes precedence
// (SecurityHeaders will skip X-Frame-Options when Antiforgery is enabled).
//
// Reference: https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
//
"Antiforgery": {
//
// Enable antiforgery token validation for state-changing requests.
//
"Enabled": false,
//
// Name of the cookie that stores the antiforgery token.
// Set to null to use the ASP.NET Core default (unique per application, starts with ".AspNetCore.Antiforgery.").
// Custom names are useful when running multiple applications on the same domain.
//
"CookieName": null,
//
// Name of the hidden form field that contains the request verification token.
// This must match the name used in your HTML forms.
//
"FormFieldName": "__RequestVerificationToken",
//
// Name of the HTTP header that can contain the antiforgery token.
// Useful for AJAX requests where adding a form field is not possible.
// JavaScript can read the token from a cookie or meta tag and send it in this header.
//
"HeaderName": "RequestVerificationToken",
//
// When true, the server will NOT look for the token in the form body.
// Forces header-only validation - useful for pure API scenarios where all requests use headers.
// When false (default), server checks both form field and header.
//
"SuppressReadingTokenFromFormBody": false,
//
// When true, prevents the automatic X-Frame-Options: SAMEORIGIN header from being set.
// X-Frame-Options helps prevent clickjacking attacks by blocking the page from being embedded in iframes.
// Only set to true if:
// - You need your pages to be embedded in iframes from other origins, OR
// - You're setting X-Frame-Options elsewhere (e.g., in SecurityHeaders or at the proxy level)
// Default: false (header is set for security)
//
"SuppressXFrameOptionsHeader": false
},
//
// Static files settings
//
"StaticFiles": {
"Enabled": false,
"RootPath": "wwwroot",
//
// List of static file patterns that will require authorization.
// File paths are relative to the RootPath property and pattern matching is case-insensitive.
// Pattern can include wildcards (* matches any chars, ** matches recursively including /, ? matches single char).
// For example: *.html, /user/*, /admin/**/*.html
//
"AuthorizePaths": [],
"UnauthorizedRedirectPath": "/",
"UnauthorizedReturnToQueryParameter": "return_to",
"ParseContentOptions": {
//
// Enable or disable the parsing of the static files.
// When enabled, the static files will be parsed and the tags will be replaced with the values from the claims collection.
// The tags are in the format: {claimType} where claimType is the name of the claim that will be replaced with the value from the claims collection.
//
"Enabled": false,
//
// List of claims types used. These will be parsed to NULL if not found in the claims collection or user is not authenticated.
//
"AvailableClaims": [],
//
// Set to true to cache the parsed files in memory. This will improve the performance of the static files. It only applies to parsed content.
// Note: caching will occur before parsing, it applies only to templates, not parsed content.
//
"CacheParsedFile": true,
//
// Headers to be added to the response for static files. Set to null or empty array to ignore.
//
"Headers": [ "Cache-Control: no-store, no-cache, must-revalidate", "Pragma: no-cache", "Expires: 0" ],
//
// List of static file patterns that will parse the content and replace the tags with the values from the claims collection.
// File paths are relative to the RootPath property and pattern matching is case-insensitive.
// Pattern can include wildcards (* matches any chars, ** matches recursively including /, ? matches single char).
// For example: *.html, *.htm, *.txt, /pages/**/*.html
//
"FilePaths": [ "*.html" ],
//
// Name of the configured Antiforgery form field name to be used in the static files (see Antiforgery FormFieldName setting).
//
"AntiforgeryFieldName": "antiForgeryFieldName",
//
// Value of the Antiforgery token if Antiforgery is enabled.
//
"AntiforgeryToken": "antiForgeryToken"
}
},
//
// Cross-origin resource sharing
//
"Cors": {
//
// Enable Cross-Origin Resource Sharing (CORS) support.
//
"Enabled": false,
//
// List of allowed origins for CORS requests. Empty array allows no origins.
//
"AllowedOrigins": [],
//
// List of allowed HTTP methods for CORS requests.
//
"AllowedMethods": [
"*"
],
//
// List of allowed headers for CORS requests.
//
"AllowedHeaders": [
"*"
],
//
// Allow credentials (cookies, authorization headers) in CORS requests.
//
"AllowCredentials": true,
//
// Maximum age in seconds for preflight request caching (10 minutes).
//
"PreflightMaxAgeSeconds": 600
},
//
// Security Headers: Adds HTTP security headers to all responses to protect against common web vulnerabilities.
// These headers instruct browsers how to handle your content securely.
// Note: X-Frame-Options is automatically handled by the Antiforgery middleware when enabled (see Antiforgery.SuppressXFrameOptionsHeader).
// Reference: https://owasp.org/www-project-secure-headers/
//
"SecurityHeaders": {
//
// Enable security headers middleware. When enabled, configured headers are added to all HTTP responses.
//
"Enabled": false,
//
// X-Content-Type-Options: Prevents browsers from MIME-sniffing a response away from the declared content-type.
// Recommended value: "nosniff"
// Set to null to not include this header.
//
"XContentTypeOptions": "nosniff",
//
// X-Frame-Options: Controls whether the browser should allow the page to be rendered in a ,