Skip to content

Configuration settings

This library reads no IConfiguration/appsettings.json keys directly. Every OIDC-related setting is read live from the Environment API section NetCore_OIDC through IEnvironmentClient, via the typed facade OidcSettings: 20 static keys in total — 16 string-type, 3 integer-type (ClockSkewSeconds, CookieExpireMinutes, RoleTransformCacheOffset), and 1 secure key (BOLAppCredentials) — plus a separate dynamic indexer-based read (OidcSettings.this[key], with no in-repository caller) that is not part of the 20-key static catalog. The library defines no options classes bound from appsettings.json; its two IConfigureNamedOptions<T> implementations source every value from IOidcSettings, not from IConfiguration.

Configuration retrieval methods

Method Class / interface Namespace Usage pattern Call sites
IEnvironmentClient.GetSettingValue(string key, string section) IEnvironmentClient (impl. BaseEnvironmentClient/SecureEnvironmentClient) SAIF.NetCore.Services.Contracts Live read on every property access, section fixed to NetCore_OIDC. The composed lookup key is "netcore_oidc.<key>", lower-cased. A missing key returns "". OidcSettings string properties and the this[key] indexer
IEnvironmentClient.GetSettingValue<int>(string key, string section) same same Typed read. A missing key throws EnvironmentClientException. OidcSettings.RoleTransformCacheOffset, CookieExpireMinutes, ClockSkewSeconds
IEnvironmentClient.GetSecureSetting(string key, string section, string systemHashValue) same; real decryption only in SecureEnvironmentClient same Decrypted read. The hash comes from ISystemHashService.ComputeHash(SystemType.Other, Assembly.GetExecutingAssembly().GetName().Name) — the executing assembly is this library, so every consuming app shares one hash identity. On BaseEnvironmentClient this always throws. OidcSettings.AppCredentials
IEnvironmentClient.IsDeveloperMachine() same same Read once in the singleton constructor; relaxes the missing-certificate failure. DataProtectionCertService..ctor
OidcSettings indexer this[string key] IOidcSettings SAIF.NetCore.Services.Contracts Passes any caller-supplied key through to the string retrieval method in section NetCore_OIDC. No caller in this repository uses it. —

Read timing.

Consumer Lifetime When the setting is read Effect of an Environment API change
ConfigureCookieAuthenticationOptions, ConfigureOpenIdConnectOptions options configurators When the named options for "Cookies"/"OpenIdConnect" are first built, then cached by the options system Requires an app restart*
ClaimTransformationCacheService (singleton) singleton RoleTransformCacheOffset, read once in the constructor Requires an app restart
DataProtectionCertService (singleton) singleton IsDeveloperMachine() in the constructor; DPCertName on each GetCertificate() call Developer flag: restart. Certificate name: live
LoginRedirectService, RefreshJwtMiddleware, JwtRefreshService, RoleClaimTransformer transient/scoped Per call Live, governed by the EnvironmentClient refresh cycle*

Environment API settings catalog

All keys belong to section NetCore_OIDC, so the composed key is NetCore_OIDC:<key>. EnvironmentClient lower-cases the composed key, so matching is case-insensitive but not spelling-tolerant.

Key (NetCore_OIDC:) Secure Type / format Seed default Missing → Required Consuming class (member)
BOLAppCredentials Yes clientId\|clientSecret secure (vault reference; not reproduced) On the standard SecureEnvironmentClient, a null/default cached or fetched value throws EnvironmentClientException ("No crypto data found for requested secure setting") before OidcAppCredentials is ever constructed; the non-secure BaseEnvironmentClient throws EnvironmentClientException for a different reason ("Operation not allowed"). Independently of those client-layer errors, OidcAppCredentials's own constructor (in SAIF.NetCore.ServiceContracts) rejects a null raw value with ArgumentNullException, and a non-null value without exactly one \| with ArgumentException. SecureEnvironmentClient validates only for null/default, not pipe-field shape, so a non-null malformed secret (for example an empty string, or a string missing/with extra \| characters) reaches this parser through the standard client and throws ArgumentException there; only the ArgumentNullException branch requires a custom IEnvironmentClient implementation that returns null Required (OIDC challenge) OidcSettings.AppCredentials → ConfigureOpenIdConnectOptions.Configure (ClientId/ClientSecret)
CallbackPath No URL path /authorization-code/callback "" → new PathString(""); the OIDC handler rejects an empty callback path during options validation* Required ConfigureOpenIdConnectOptions.Configure
ClockSkewSeconds No int seconds 5 EnvironmentClientException Required ConfigureOpenIdConnectOptions.Configure → TokenValidationParameters.ClockSkew
CookieAccessDeniedPath No URL path /unknown "" → empty AccessDeniedPath Recommended ConfigureCookieAuthenticationOptions.Configure
CookieExpireMinutes No int minutes 60 EnvironmentClientException Required ConfigureCookieAuthenticationOptions.Configure → ExpireTimeSpan
CookieLoginPath No URL path (login app) /OnlineLoginV2 "": login redirect goes to https://{host}{CookieSigninUri}, and the loop guard matches every path, so origin is never appended* Required ConfigureCookieAuthenticationOptions.Configure; LoginRedirectService.Redirect
CookieName No cookie name; must be shared across SSO apps SAIF.Asp.AuthenticationCookie "": an empty cookie name breaks shared-cookie SSO* Required ConfigureCookieAuthenticationOptions.Configure
CookieSigninUri No URL path /account/signin "" → redirect to the login app root Required LoginRedirectService.Redirect
DefaultOidcScope No CSV of scopes (no trim) profile,openid,email,offline_access "" → [""], adding one empty scope* Required OidcSettings.DefaultScopes → ConfigureOpenIdConnectOptions.Configure
DPApplicationName No data-protection app discriminator; must be identical across apps sharing the cookie SAIFBusinessOnline "": both the cookie and OIDC (-oidc) key rings use a blank or near-blank discriminator* Required ConfigureCookieAuthenticationOptions.Configure; ConfigureOpenIdConnectOptions.Configure (as {value}-oidc)
DPCertName No exact X.509 Subject in LocalMachine\My CN=BOL_OIDC_DP "": no certificate matches → MissingDataProtectionCertificateException (non-developer machines); developer machines get null and an unencrypted key ring Required (servers) DataProtectionCertService.GetCertificate → ConfigureCookieAuthenticationOptions
DPKeyDirectory No file-system path C:\OIDC\DP "" → new DirectoryInfo("") → ArgumentException at options build* Required ConfigureCookieAuthenticationOptions.Configure; ConfigureOpenIdConnectOptions.Configure
OktaAuthorizationServerUrl No URL path segment /oauth2/default "" → authority = Okta org root Required ConfigureOpenIdConnectOptions.Configure (authority); JwtRefreshService.Refresh
OktaBaseUrl No absolute HTTPS URL of the Okta tenant present only in EnvironmentSettings.json/.enttest7.json; absent from .global.json "" → relative authority → OIDC metadata retrieval fails* Required ConfigureOpenIdConnectOptions.Configure; JwtRefreshService.Refresh
RefreshTokenQueryString No query-string prefix ending in refresh_token= ?grant_type=refresh_token&redirect_uri=&scope=offline_access openid&refresh_token= (unencoded space) "" → token POST without a grant → Okta 400 → JwtRefreshException* Required when IJwtRefreshService is used JwtRefreshService.Refresh
RefreshTokenRedirectUrl No URL path of the JwtRefresh app /JwtRefresh/token/refresh "" → 301 to a relative URL made of the query only* Required when UseJwtRefreshMiddleware RefreshJwtMiddleware.InvokeAsync
RefreshTokenUrl No URL path /v1/token "" → POST to the authorization-server root* Required when IJwtRefreshService is used OidcSettings.RefresnTokenUrl (property-name typo) → JwtRefreshService.Refresh
RoleClaimIdentitifier (code key, typo) No substring matched against lower-cased claim types seed key is RoleClaimIdentifier (correct spelling) / _roles_ — the code key has no seed entry "" (likely in every seeded environment): "anything".Contains("") is true, so every non-array claim becomes a ClaimTypes.Role Required (security-relevant) OidcSettings.RoleClaimIdentifier → RoleClaimTransformer.TransformAsync → RoleClaimService.AddRoleClaims
RoleTransformCacheOffset No int seconds 30 EnvironmentClientException, while resolving the singleton IClaimTransformCacheService, failing the scoped RoleClaimService resolution Required ClaimTransformationCacheService..ctor
SignedOutCallbackPath No URL path /signout/callback "" → PathString("") Required ConfigureOpenIdConnectOptions.Configure
(dynamic) NetCore_OIDC:<caller key> No any — "" — OidcSettings.this[key] — no in-repo caller

Declared but never read (orphaned constants). The removed SigningKeys* services left these behind; no code reads them and the seed files do not list them: JwtSigningKeysUrl, JwtSigninKeysExpireMinutes.

Host-level toggles that affect this library come from SAIF.NetCore.ServiceExtensions/AppStartup, not this library: HostLocation (must be External), ChallengeType (OpenIdConnect), UseJwtRefreshMiddleware.

Developer overrides

Override Applied when Effect on this library
IEnvironmentClient.IsDeveloperMachine() Evaluated once when the DataProtectionCertService singleton is constructed A missing DPCertName certificate returns null instead of throwing; the cookie key ring in DPKeyDirectory is then written without certificate encryption
Developer settings override file (AppSettings:EnvApiDeveloperSettingsOverride, conventionally developersettings.json) Developer machine and the file exists; re-read on each non-secure lookup Can override any non-secure NetCore_OIDC.* value by entries with section: "NetCore_OIDC", matched case-insensitively. It cannot override BOLAppCredentials, because secure reads bypass the override. It can be used locally to supply the typo key RoleClaimIdentitifier*

The library ships no developer settings file of its own.

Environment variables

The library reads no environment variables directly. These are read by packages and the host that activate or support OIDC, and are listed for completeness.

Variable Read by Scope Accepted values Default when unset Effect on this library
ASPNETCORE_HOST_LOCATION AppStartup.HostLocation (ServiceExtensions) Process, then Machine (Windows-only) EXTERNAL/INTERNAL (case-insensitive) NotDefined Only EXTERNAL registers and wires OIDC; RegisterOidcServices and the LowerCaseUrl/JwtClaim/RefreshJwt middleware run only then
ASPNETCORE_REDIRECT_SSL AppStartup.EnforceSslRedirect Machine only true/false true HTTPS redirection runs after the OIDC middleware, so it is relevant to the cookie SecurePolicy risk
EnvironmentApiUrl BaseEnvironmentClient field initializer Process absolute URL none → MissingEnvironmentUrlVariableException No OIDC setting can be resolved without it
COREAPIENABLED (usually ASPNETCORE_COREAPIENABLED) SecureEnvironmentClient ctor via IConfiguration 1 unset → secure client throws unless on a developer machine AppStartup registers the secure client, and BOLAppCredentials needs it

Database connection configuration

Not applicable. This library has no database access, no IDBConnectionClient usage, and no connection strings. Persistence is limited to the file system (data-protection key ring at DPKeyDirectory) and the Windows certificate store (DPCertName, LocalMachine\My, read-only).

Secure settings

Setting key Retrieval Hash method Application type Application name (hash input) Credential provided
NetCore_OIDC:BOLAppCredentials IEnvironmentClient.GetSecureSetting(key, section, systemHashValue) ISystemHashService.ComputeHash(SystemType, string) SystemType.Other Assembly.GetExecutingAssembly().GetName().Name = SAIF.NetCore.OIDC (the library assembly, not the host app) Okta OIDC client credentials, clientId\|clientSecret, parsed by OidcAppCredentials

Notes: the hash input is the OIDC assembly name, so every consuming application presents the same system hash for this secret — crypto-API authorization cannot distinguish one consuming app from another. The secure client is required (BaseEnvironmentClient.RetrieveSecureSetting always throws EnvironmentClientException); AppStartup registers SecureEnvironmentClient. On the standard SecureEnvironmentClient, RetrieveSecureSetting<T> itself throws EnvironmentClientException if the cached/fetched value is null or the type default, before the value is ever returned to OidcSettings.AppCredentials — so the credential-parsing constructor OidcAppCredentials (declared in SAIF.NetCore.ServiceContracts, not in this library's OidcSettings.cs) never observes a null raw value through that path. OidcAppCredentials separately and independently rejects a null raw value with ArgumentNullException and a non-null but malformed (wrong pipe-field-count) raw value with ArgumentException. Because SecureEnvironmentClient validates only for null/default and performs no pipe-field validation, a non-null malformed secret reaches this parser through the standard client and triggers the ArgumentException branch there; only the ArgumentNullException branch requires a custom IEnvironmentClient implementation that returns null where the standard client would have thrown first. The seed marks BolAppCredentials as SecureFlag: true; its value is a reference and is not reproduced here.

Transitive configuration (from dependencies)

Key / input Required by Why this library needs it
EnvironmentApiUrl (env var) SAIF.NetCore.EnvironmentClient All 20 Environment API reads
COREAPIENABLED=1 (server) SecureEnvironmentClient BOLAppCredentials secure read
AppSettings:ApplicationHost, AppSettings:EnvApiDeveloperOverrideAllowed, AppSettings:EnvApiDeveloperSettingsOverride HostResolver/BaseEnvironmentClient Track resolution; developer flag; override file
EnvironmentClient:GetHostsByTrack:RetryCount/:RetrySleepMs (optional) BaseEnvironmentClient Startup resilience of the client this library depends on
ISystemHashService registration (assembly-based) SAIF.NetCore.SystemHash Hash for BOLAppCredentials
ISaifLogService and its configuration SAIF.NetCore.Logging Error/warning logging in options, middleware, and the certificate service
IHttpJsonClient SAIF.NetCore.HttpClients JwtRefreshService POST to Okta
Host toggles AppStartup.AuthenticationChallengeType (default Cookie) and AppStartup.UseJwtRefreshMiddleware (default true) SAIF.NetCore.ServiceExtensions Code properties, not configuration keys. LowerCaseUrlMiddleware and RefreshJwtMiddleware are always registered as concrete scoped types by RegisterOidcServices, regardless of either toggle; AuthenticationChallengeType instead decides only whether LowerCaseUrlMiddleware is added to the pipeline by AppStartup.Configure, and UseJwtRefreshMiddleware decides both whether IJwtRefreshService is registered in DI and whether RefreshJwtMiddleware is added to the pipeline — see Service registration

This library has no ProjectReferences and only one internal package reference, SAIF.NetCore.ServiceContracts, which is contracts-only with no configuration reads of its own.

Environment variance assessment

The seed files (EnvironmentSettings.json, .global.json, .enttest7.json) are solution items, not compiled, and imply a pattern of global defaults plus a per-track OktaBaseUrl. Only one track, enttest7, is represented in this repository.

Setting key Varies by environment Classification basis
BOLAppCredentials Yes Credential; Okta app per tenant*
OktaBaseUrl Yes Tenant host URL; the only key with a per-track seed file
OktaAuthorizationServerUrl No (typically) Static path /oauth2/default, same in all seeds
RefreshTokenUrl, RefreshTokenQueryString No Okta protocol constants*
RefreshTokenRedirectUrl, CookieLoginPath, CookieSigninUri, CookieAccessDeniedPath, CallbackPath, SignedOutCallbackPath No Relative app paths, identical across hosts*
CookieName, DPApplicationName No — must be identical across all SSO apps in an environment Shared-cookie SSO depends on equality*
DPKeyDirectory Possibly (per server farm) File-system path; must be shared or synchronized per farm*
DPCertName Possibly Certificate subject may differ per environment PKI*
CookieExpireMinutes, ClockSkewSeconds, RoleTransformCacheOffset No Numeric thresholds*
DefaultOidcScope No Static scope list*
RoleClaimIdentitifier (code) / RoleClaimIdentifier (seed) No Static claim pattern _roles_; see CR-01 for the key mismatch
ASPNETCORE_HOST_LOCATION (transitive) Yes (per server role) Decides whether OIDC is active at all

Configuration dependencies (risks)

ID Risk Severity
CR-01 RoleClaimIdentitifier/RoleClaimIdentifier key mismatch: the code requests netcore_oidc.roleclaimidentitifier; the seed defines RoleClaimIdentifier. Lower-casing does not reconcile a spelling difference, so the read returns "", and RoleClaimService matches every non-array claim type. Impact is limited today only because the transformer is registered as a concrete type, not as IClaimsTransformation (see Security and authorization). High (conditional)
CR-02 OktaBaseUrl absent from the global seed; any track without its own override has an empty authority, so OIDC metadata retrieval and JwtRefreshService both fail. Medium
CR-03 Silent empty-string defaults on 16 string keys; misconfiguration surfaces later as broken redirects, empty scopes, or an invalid authority rather than a clear startup error. Medium
CR-04 Int keys throw at singleton/options construction: a missing RoleTransformCacheOffset breaks DI resolution of the claim-transform chain; a missing CookieExpireMinutes or ClockSkewSeconds breaks the auth options build, failing every authenticated request. Medium
CR-05 No cookie SecurePolicy; HTTPS redirect is controlled by ASPNETCORE_REDIRECT_SSL and runs after the OIDC middleware. Medium
CR-06 Single secure-hash identity (SystemType.Other + "SAIF.NetCore.OIDC") for every consuming app's client secret. Design / Medium
CR-07 Settings captured for the process lifetime in singletons and options; Environment API changes need an app restart. Low
CR-08 The developer-machine flag disables certificate encryption of the cookie key ring; a misdetected developer flag on a server would silently weaken data protection. Medium
CR-09 DPKeyDirectory must be shared across farm nodes and readable by the app pool, or cookies fail to decrypt on other nodes. Medium
CR-10 RefreshTokenQueryString contains an unencoded space and is concatenated with an unencoded refresh token. Low
CR-11 Orphaned keys JwtSigningKeysUrl and JwtSigninKeysExpireMinutes are declared but never read. Low (tech debt)
CR-12 This library is activated by an environment variable (ASPNETCORE_HOST_LOCATION=EXTERNAL); a missing or incorrect value silently leaves the site without OIDC. Medium

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.OIDC/src/SAIF.NetCore.OIDC/Services/OidcSettings.cs
  • src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Constants.cs
  • src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/DataProtectionCertService.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/OidcAppCredentials.cs (consumer dependency; owns the clientId|clientSecret parsing and the ArgumentNullException/ArgumentException credential-parser failure modes documented on this page)
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs (consumer dependency; GetSettingValue/GetSettingValue<int> lookup, lower-casing, and missing-key behavior documented on this page)
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SecureEnvironmentClient.cs (consumer dependency; GetSecureSetting/RetrieveSecureSetting decryption and null/default failure behavior for BOLAppCredentials)
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/HostResolver.cs (consumer dependency; developer-override and track-resolution behavior)
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IOidcSettings.cs (consumer dependency; defines the RefresnTokenUrl property-name typo referenced on this page)
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/Startup/AppStartup.cs (consumer composition; ASPNETCORE_HOST_LOCATION/ASPNETCORE_REDIRECT_SSL/AuthenticationChallengeType/UseJwtRefreshMiddleware host toggles documented on this page)
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs (consumer composition; RegisterOidcServices registration conditions documented on this page)
  • src/SAIF.NetCore.OIDC/src/EnvironmentSettings.json (seed defaults declaring source)
  • src/SAIF.NetCore.OIDC/src/EnvironmentSettings.global.json (seed defaults declaring source; absence of OktaBaseUrl documented on this page)
  • src/SAIF.NetCore.OIDC/src/EnvironmentSettings.enttest7.json (track-specific seed override declaring source)