Skip to content

SAIF.NetCore.ServiceExtensions business rules

This page captures business rules enforced by this library.

BR-SE-01 Non-secure environment clients cannot retrieve secure settings

Field Value
Statement A non-secure environment client may not retrieve secure settings; no system hash is available in that mode.
Trigger RegisterEnvironmentClient() registers BaseEnvironmentClient and a null ISystemHashService.
Enforcement SE/IServiceCollectionExtensions.cs L51–52; BaseEnvironmentClient.cs L744–769.
Outcome Secure-setting reads throw EnvironmentClientException; ISystemHashService resolves to null.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-02 A null system hash must not be resolved as required

Field Value
Statement ISystemHashService must not be resolved in non-secure mode; the unit test suite asserts a throw.
Trigger GetRequiredService<ISystemHashService>() call against a non-secure registration.
Enforcement SE/IServiceCollectionExtensions.cs L52; SET/StartupTests.cs L110.
Outcome InvalidOperationException (MS DI behavior for a factory returning null to GetRequiredService).
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-03 Secure-mode system hash is computed from the caller's assembly

Field Value
Statement In secure mode, the system hash is computed for the assembly of the supplied type (Startup type, console type, or T for Windows services).
Trigger RegisterSecureEnvironmentClient(type).
Enforcement SE/IServiceCollectionExtensions.cs L89; AppStartup.cs L105; IHostBuilderExtensions.cs L31; IServiceCollectionExtensions.cs L225.
Outcome Hash identity = caller assembly.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-04 DB connection credentials are fetched as a secure, hash-gated setting

Field Value
Statement DB connection-service credentials are fetched as the secure setting connectionservicecreds/dbconnectionclient, authorized with the WebApp hash of the DBConnectionClient assembly.
Trigger First resolve of IDBConnectionClient.
Enforcement SE/IServiceCollectionExtensions.cs L64–73.
Outcome Credentials applied to ConnectionClient.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-05 The credential value must be a user|password pair

Field Value
Statement The credential value format is user\|password.
Trigger Splitting the secure setting resolved by BR-SE-04.
Enforcement SE/IServiceCollectionExtensions.cs L71–72.
Outcome Fewer than two parts causes IndexOutOfRangeException.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-06 Edge APIs authenticate with JWT bearer only

Field Value
Statement Edge APIs authenticate with JWT bearer only.
Trigger RegisterEdgeAPIAuthServices().
Enforcement SE/IServiceCollectionExtensions.cs L99–110.
Outcome Default authentication scheme is JwtBearer.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-07 Core APIs authenticate via a dual JWT/Windows policy scheme

Field Value
Statement Core APIs authenticate via a "dual" policy scheme: an Authorization header whose value starts with "Bearer " selects JwtBearer; otherwise the Windows/IIS scheme (IISServerDefaults, i.e. Negotiate/NTLM via IIS) is selected.
Trigger RegisterCoreAPIAuthServices().
Enforcement SE/IServiceCollectionExtensions.cs L250–259; AuthService AddDualAuthentication.
Outcome Authentication scheme selected per request.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-08 SecurityApi provides authorization policies dynamically

Field Value
Statement Authorization policies are provided dynamically by SecurityApiPolicy and evaluated by SecurityApiHandler via ISecurityClient.
Trigger AddSecurityApiAuthorizeAttribute().
Enforcement SE/IServiceCollectionExtensions.cs L257; AppStartup.cs L129–131.
Outcome Policy provider and handler registered for [SecurityApiAuthorize].
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-09 Opt-in global authenticated-user requirement

Field Value
Statement Opt-in global rule: every MVC action requires an authenticated user.
Trigger RequireAuthenticatedUser().
Enforcement SE/IServiceCollectionExtensions.cs L116–128.
Outcome Global AuthorizeFilter added to MvcOptions; an unauthenticated request is challenged.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-10 JWT refresh and lower-case URL middleware are conditionally registered

Field Value
Statement JWT refresh service is registered only when useJwtRefreshMiddleware is true; RefreshJwtMiddleware is added to the pipeline only when UseJwtRefreshMiddleware is true; LowerCaseUrlMiddleware only when the challenge type is OpenIdConnect.
Trigger OIDC registration / pipeline configuration.
Enforcement SE/IServiceCollectionExtensions.cs L141, L156–159; AppStartup.cs L168–176.
Outcome Conditional middleware registration/wiring.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

Field Value
Statement Web apps authenticate and sign in with Cookies; the challenge scheme is Cookies by default, or OpenIdConnect if configured.
Trigger AddWebAuthentication(type).
Enforcement SE/IServiceCollectionExtensions.cs L202–209.
Outcome Default authentication/sign-in/challenge schemes set accordingly.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs

BR-SE-12 Exactly one IErrorHandler must be registered among error handleables

Field Value
Statement Exactly one IErrorHandler must be among registered IErrorHandleables; others implementing IErrorHandlerHook are treated as hooks.
Trigger DynamicErrorHandler activation.
Enforcement SE/Middleware/DynamicErrorHandler.cs constructor.
Outcome InvalidOperationException if zero or more than one IErrorHandler is registered.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DynamicErrorHandler.cs

BR-SE-13 Exception-to-HTTP-response mapping

Field Value
Statement Exception → HTTP mapping: HttpStatusCodeException maps to its own status/content type (handled); SAIFException maps to 500 text/plain (handled); any other exception maps to 500 (unhandled).
Trigger An unhandled exception reaches DynamicErrorHandler.InvokeAsync.
Enforcement SE/Middleware/DynamicErrorHandler.cs L50–80.
Outcome Error response written with Error-ID/Server-Name headers, plus a log entry.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DynamicErrorHandler.cs

BR-SE-14 Hook failures are aggregated, not suppressed

Field Value
Statement Hook failures do not suppress the original error; they are aggregated with it.
Trigger An IErrorHandlerHook throws during DynamicErrorHandler invocation.
Enforcement SE/Middleware/DynamicErrorHandler.cs.
Outcome AggregateException containing the original error plus hook failures is logged.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DynamicErrorHandler.cs

BR-SE-15 Every request is logged at Debug level

Field Value
Statement Every request is logged at Debug level with web request details.
Trigger Any request through a pipeline where ConfigureDebugRequestHandling() is applied.
Enforcement SE/Middleware/DebugRequestHandler.cs L36–46.
Outcome A Debug-level log entry is written for every request.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DebugRequestHandler.cs

Field Value
Statement Legacy BOL/ASP.NET session gate: a request lacking the relevant cookie is redirected to the LegacySessionProvider endpoint with a returnUrl; the check is presence-only and the pipeline continues regardless.
Trigger A request missing SAIFAUTH/SAIF.BOL.Auth (LegacyBolAuthMiddleware) or ASP.NET_SessionId (AspNetSessionIdMiddleware).
Enforcement SE/Middleware/LegacyBolAuthMiddleware.cs L36–47; SE/Middleware/AspNetSessionIdMiddleware.cs L35–47.
Outcome A 302 redirect is issued, but downstream middleware and the endpoint still execute (fail-open*).
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/LegacyBolAuthMiddleware.cs

BR-SE-17 Session support and its gate are opt-in

Field Value
Statement Session support (and the session gate) is enabled only by EnableSessionMiddleware.
Trigger AppStartup constructor sets EnableSessionMiddleware = true.
Enforcement SE/Startup/AppStartup.cs L107–111, L152–156.
Outcome AddSession() and AspNetSessionIdMiddleware are registered/wired only when the flag is set.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs

BR-SE-18 Web-app auth stack is selected by ASPNETCORE_HOST_LOCATION

Field Value
Statement The web-app auth stack is chosen by ASPNETCORE_HOST_LOCATION: EXTERNAL selects OIDC; any other value selects Core dual authentication.
Trigger AppStartup.ConfigureServices.
Enforcement SE/Startup/AppStartup.cs L62–75, L125–135.
Outcome Auth stack selection (OIDC vs. Core dual auth) for the resulting application.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs

BR-SE-19 HSTS is always on; HTTPS redirect is configurable for web apps

Field Value
Statement HSTS is always on; HTTPS redirection is on by default for web apps unless the machine env var ASPNETCORE_REDIRECT_SSL is set to a false value. APIs always redirect.
Trigger Pipeline Configure execution.
Enforcement SE/Startup/AppStartup.cs L85–95, L178–181; CoreAPIStartup.cs L84–85; EdgeAPIStartup.cs L53–54.
Outcome UseHsts() always runs; UseHttpsRedirection() for web apps is conditional on AppStartup.EnforceSslRedirect.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs

BR-SE-20 Console configuration source precedence

Field Value
Statement Console configuration precedence: appsettings.json is overridden by environment variables, which are overridden by command line.
Trigger BuildConsoleApp.
Enforcement SE/IHostBuilderExtensions.cs L44–60.
Outcome Later-added configuration sources win for the console host.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IHostBuilderExtensions.cs

BR-SE-21 Console process exit code reflects ConsoleBase.ExitCode

Field Value
Statement The console process exit code equals ConsoleBase.ExitCode after Finish runs.
Trigger ConsoleBase.Run().
Enforcement SE/ConsoleBase.cs L28–37.
Outcome Environment.ExitCode is set from ConsoleBase.ExitCode.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/ConsoleBase.cs

BR-SE-22 Enabling OpenTelemetry replaces existing logging providers

Field Value
Statement Enabling OpenTelemetry replaces all existing logging providers with OpenTelemetry's own.
Trigger ConfigureOpenTelemetry.
Enforcement SE/OpenTelemetryExtensions.cs.
Outcome Console and other previously configured logging providers are lost unless re-added*.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/OpenTelemetryExtensions.cs

BR-SE-23 All meters and activity sources are collected; all traces sampled

Field Value
Statement All meters and activity sources are collected; all traces are sampled.
Trigger ConfigureOpenTelemetry.
Enforcement SE/OpenTelemetryExtensions.cs.
Outcome AddMeter("*"), AddSource("*"), and AlwaysOnSampler are applied.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/OpenTelemetryExtensions.cs

BR-SE-24 OTLP export requires the OTLP endpoint variable

Field Value
Statement OTLP export is enabled only when OTEL_EXPORTER_OTLP_ENDPOINT is set.
Trigger ConfigureOpenTelemetry.
Enforcement SE/OpenTelemetryExtensions.cs L62, L108.
Outcome No endpoint value means no OTLP exporter is registered.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/OpenTelemetryExtensions.cs

BR-SE-25 Required configuration keys must exist

Field Value
Statement Required configuration keys must exist; a missing key throws ArgumentNullException.
Trigger GetRequiredValue<T> / KeyMustExist.
Enforcement SE/IConfigurationExtensions.cs L22, L83–110.
Outcome ArgumentNullException thrown for a missing or whitespace-only key.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IConfigurationExtensions.cs

BR-SE-26 Controller helpers convert to HTTP errors by throwing

Field Value
Statement Controller helpers convert error conditions to HTTP errors by throwing, relying on DynamicErrorHandler to translate the exception into a response.
Trigger A controller calls one of the Throw* helpers.
Enforcement SE/ControllerBaseExtensions.cs.
Outcome HttpStatusCodeException is thrown and handled per BR-SE-13.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/ControllerBaseExtensions.cs

BR-SE-27 Username and auth time are derived from JWT claims

Field Value
Statement Username is the upper-cased local part (before @) of the first claim whose type contains "nameidentifier"; auth time is parsed from the auth_time claim.
Trigger A controller derived from JwtControllerBase calls GetUsernameFromClaim() / GetAuthTimeFromClaim().
Enforcement SE/JwtControllerBase.cs L22.
Outcome An exception during username parsing yields "Unknown"; a missing claim yields null. If the auth_time claim's value fails long.TryParse (missing, non-numeric, or absent claim), the method returns default(DateTime) (DateTime.MinValue) without throwing. If auth_time parses as a long but that value is outside the range DateTimeOffset.FromUnixTimeSeconds supports, the conversion call itself throws uncaught (ArgumentOutOfRangeException) — this is a distinct failure mode from the parse-failure default, not also absorbed into DateTime.MinValue.
Source src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/JwtControllerBase.cs

Notes

  • BR-SE-16's "fail-open" characterization is inferred from the unconditional await next(context) call that follows the redirect in both cookie-gate middlewares; see security-authorization.md for the full analysis of this control weakness.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Middleware/DynamicErrorHandler.cs, DebugRequestHandler.cs, LegacyBolAuthMiddleware.cs, AspNetSessionIdMiddleware.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs, CoreAPIStartup.cs, EdgeAPIStartup.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IHostBuilderExtensions.cs, ConsoleBase.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/OpenTelemetryExtensions.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IConfigurationExtensions.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/ControllerBaseExtensions.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/JwtControllerBase.cs
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtenstions.Tests/StartupTests.cs