{ // // 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 ,