---
title: App startup configure services
summary: AppStartup.ConfigureServices selects the web-app auth stack and composes session/logging/MVC.
status: draft
owner: Framework.NCore maintainers
last_reviewed: 2026-10-02
library: SAIF.NetCore.ServiceExtensions
reference_type: package
operation_id: OP-14
trigger: ASP.NET Core calls ConfigureServices on an AppStartup-derived class during host startup.
---

# App startup configure services

This operation is the service-configuration phase of the web UI startup archetype. It selects between the OIDC and Core dual-authentication stacks based on `ASPNETCORE_HOST_LOCATION`, and composes session, logging, and MVC services.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-14 |
| **Trigger** | The ASP.NET Core host calls `ConfigureServices(IServiceCollection services)` on an `AppStartup`-derived class (`OidcMvcStartup`/`OidcRazorStartup`). |
| **Inputs** | The `IServiceCollection`; `AppStartup`'s instance flags (`EnableSessionMiddleware`, `EnableLegacyBolAuthMiddleware`, `EnableNewtonsoftJsonSerializer`, `HostLocation`, `AuthenticationChallengeType`, `UseJwtRefreshMiddleware`). |
| **Outputs** | A fully composed DI container for the web application. |
| **Primary components** | `AppStartup.ConfigureServices` |
| **Entry point** | `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs` (L100–137) |

## Steps

1. `AddHttpContextAccessor`, `AddAuthorization`, and delegate to [Register secure environment client](register-secure-environment-client.md) via `RegisterSecureEnvironmentClient(GetType())` ([BR-SE-03](../business-rules.md#br-se-03-secure-mode-system-hash-is-computed-from-the-caller's-assembly))
   - The hash identity is keyed on the derived Startup class's own assembly, not `AppStartup` itself.

   > **Origin**: `RegisterSecureEnvironmentClient` performs three concrete registrations, all deferred to first resolution (none run during this call): (1) the same `AddHttpContextAccessor`/`ISystemWrapper`/`IHostResolver`/`IHttpJsonClient` registrations as [Register environment client](register-environment-client.md) steps 1–3; (2) a Singleton `IEnvironmentClient → SecureEnvironmentClient` type-based descriptor; (3) a Singleton `ISystemHashService` factory that constructs `new SystemHashService(Assembly.GetAssembly(type))`, where `type` is the `GetType()` passed in above. See [Register secure environment client](register-secure-environment-client.md) for the full step-by-step detail, including its documented `ArgumentNullException` edge case when `type` is `null`.

2. If `EnableSessionMiddleware`: `AddSession()` and register Scoped `AspNetSessionIdMiddleware` ([BR-SE-17](../business-rules.md#br-se-17-session-support-and-its-gate-are-opt-in))
3. If `EnableLegacyBolAuthMiddleware`: register Scoped `LegacyBolAuthMiddleware` ([BR-SE-16](../business-rules.md#br-se-16-legacy-cookie-gates-check-presence-only-and-are-fail-open))
   - This registration is never added to the request pipeline (dead code*; see [App startup configure pipeline](app-startup-configure-pipeline.md)).
4. `AddControllersWithViews()`, plus `AddNewtonsoftJson()` if `EnableNewtonsoftJsonSerializer` is set.
5. Delegate to [Register logging and error handler](register-logging-and-error-handler.md) via `RegisterLoggingService()`.

   > **Origin**: `RegisterLoggingService()` registers a Singleton `ISaifLogService → SaifLog`; registration only adds the descriptor — `SaifLog`'s own configuration/logging logic runs later, as documented on [Register logging and error handler](register-logging-and-error-handler.md) step 1 and [`Configure web logger`](../../saif-netcore-logging/operations/op-01-configure-web-logger.md) (`SAIF.NetCore.Logging`).
6. If `HostLocation == External`: delegate to [Register OIDC services](register-oidc-services.md) via `RegisterOidcServices(AuthenticationChallengeType, UseJwtRefreshMiddleware)`; additionally register Transient `ISecurityClient → SecurityClient`; Transient `IAuthorizationPolicyProvider → SecurityApiPolicy` + Singleton `IAuthorizationHandler → SecurityApiHandler`; call `AddIdentityAccessor()`; call `AddSecurityApiAuthorizeAttribute()` ([BR-SE-18](../business-rules.md#br-se-18-web-app-auth-stack-is-selected-by-aspnetcore_host_location))
   - `ISecurityClient` ends up registered both Transient (here) and Singleton (inside `AddIdentityAccessor`/`AddSecurityApiAuthorizeAttribute`), and the SecurityApi policy-provider/handler pair is registered twice; the last registration wins when the container resolves a single instance (duplicate-registration defect*).

   > **Origin**: `RegisterOidcServices` registers Scoped `JwtClaimMiddleware`/`LowerCaseUrlMiddleware`/`RefreshJwtMiddleware`, Singleton `IOidcSettings → OidcSettings`, role-claim/login-redirect/cookie-claim/data-protection-certificate services, then synchronously calls `AddWebAuthentication(type)`, which configures the deferred Cookie/OpenIdConnect option configurators and sets the default authenticate/sign-in/challenge schemes (`"Cookies"` for authenticate/sign-in; challenge defaults to `"OpenIdConnect"` only when `type == OpenIdConnect`). See [Register OIDC services](register-oidc-services.md) for its full embedded registration steps and timing.
7. Else (internal host location): delegate to [Register Core API auth services](register-core-api-auth-services.md) via `RegisterCoreAPIAuthServices()` ([BR-SE-18](../business-rules.md#br-se-18-web-app-auth-stack-is-selected-by-aspnetcore_host_location))

   > **Origin**: `RegisterCoreAPIAuthServices()` configures JWT bearer options, registers the identity accessor, adds the "dual" JWT/Windows policy scheme, and registers SecurityApi dynamic authorization. See [Register Core API auth services](register-core-api-auth-services.md) for its full embedded registration steps and per-request scheme-selection timing.
8. Delegate to [Register logging and error handler](register-logging-and-error-handler.md) via `RegisterFactoryActivatedMiddleware()`.

   > **Origin**: `RegisterFactoryActivatedMiddleware()` registers a Scoped `IErrorHandleable` factory (`ErrorHandler`) plus Transient `DynamicErrorHandler` and `DebugRequestHandler`; registration only adds these descriptors — `ErrorHandler`'s exception handling runs later, per request, as documented on [Register logging and error handler](register-logging-and-error-handler.md) steps 2–3 and [`Log and return exception`](../../saif-netcore-errorhandler/operations/log-and-return-exception.md) (`SAIF.NetCore.ErrorHandler`).

## Process flow

This diagram shows the one-time `ConfigureServices` composition that runs once during host startup for a web-app `AppStartup`-derived class, not any per-request behavior of the services it registers.

```mermaid
flowchart TD
    Start([ConfigureServices invoked]) --> S1["AddHttpContextAccessor / AddAuthorization /<br>RegisterSecureEnvironmentClient"]
    S1 --> S2{EnableSessionMiddleware?}
    S2 -->|Yes| S2a["AddSession + register AspNetSessionIdMiddleware"]
    S2 -->|No| S3{EnableLegacyBolAuthMiddleware?}
    S2a --> S3
    S3 -->|Yes| S3a[Register LegacyBolAuthMiddleware - never piped]
    S3 -->|No| S4[AddControllersWithViews]
    S3a --> S4
    S4 --> S5[RegisterLoggingService]
    S5 --> S6{HostLocation == External?}
    S6 -->|Yes| S6a["RegisterOidcServices + duplicate SecurityApi registrations"]
    S6 -->|No| S6b[RegisterCoreAPIAuthServices]
    S6a --> S7[RegisterFactoryActivatedMiddleware]
    S6b --> S7
    S7 --> EndOk([DI container composed])
```

## Failure and edge-case behavior
- Duplicate `ISecurityClient`/SecurityApi registrations in the External branch mean the last registration wins on a single-instance resolve, which may not be the intended implementation.
- `LegacyBolAuthMiddleware` is registered in DI but never wired into the pipeline, so enabling `EnableLegacyBolAuthMiddleware` has no observable effect on request handling (dead code*).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **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/Startup/AppStartup.cs`
