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
BR-SE-11 Web apps default to Cookie authentication with optional OpenIdConnect challenge
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
BR-SE-16 Legacy cookie gates check presence only and are fail-open
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