---
title: SAIF.NetCore.OIDC operations and processes
summary: Business and technical operations supported by this library.
status: draft
owner: SAIF Framework Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.OIDC
reference_type: package
---

# SAIF.NetCore.OIDC operations and processes

This page is the operation catalog and cross-operation hub for this library. Each operation's detailed, step-by-step logic lives in its own page under `operations/`.

## Operation catalog
| Operation | Intent | Trigger | Inputs | Outputs | Rules |
| --- | --- | --- | --- | --- | --- |
| [Configure cookie authentication](operations/op-01-configure-cookie-auth.md) | Build the `"Cookies"` named options: cookie attributes, data-protection ticket format, login-redirect event | Options system builds the named `CookieAuthenticationOptions` (first use of `"Cookies"`) | `IOidcSettings`, `IDataProtectionCertService`, `ILoginRedirectService` | Configured `CookieAuthenticationOptions` | [BR-OIDC-01](business-rules.md#br-oidc-01-option-configurators-apply-only-to-their-named-scheme), [BR-OIDC-02](business-rules.md#br-oidc-02-cookie-attributes-are-fixed%3B-secure-policy-is-not-enforced), [BR-OIDC-03](business-rules.md#br-oidc-03-cookie-tickets-are-protected-by-a-shared-file-system-key-ring), [BR-OIDC-14](business-rules.md#br-oidc-14-data-protection-certificate-must-match-subject-and-validity-window), [BR-OIDC-15](business-rules.md#br-oidc-15-a-missing-dp-certificate-is-fatal-outside-developer-machines), [BR-OIDC-16](business-rules.md#br-oidc-16-login-redirects-always-use-https-on-the-current-host) |
| [Configure OpenID Connect](operations/op-02-configure-oidc.md) | Build the `"OpenIdConnect"` named options: Okta authority, client credentials, token validation, state protection, redirect/failure events | Options system builds the named `OpenIdConnectOptions` (first use of `"OpenIdConnect"`) | `IOidcSettings` | Configured `OpenIdConnectOptions` | [BR-OIDC-01](business-rules.md#br-oidc-01-option-configurators-apply-only-to-their-named-scheme), [BR-OIDC-04](business-rules.md#br-oidc-04-oidc-client-credentials-come-from-a-secure%2C-library-hashed-setting), [BR-OIDC-05](business-rules.md#br-oidc-05-inbound-jwt-claim-names-are-not-remapped), [BR-OIDC-06](business-rules.md#br-oidc-06-oidc-uses-the-authorization-code-flow-against-okta), [BR-OIDC-07](business-rules.md#br-oidc-07-id-tokens-must-pass-full-validation), [BR-OIDC-08](business-rules.md#br-oidc-08-oidc-correlation-state-uses-an-unencrypted-key-ring) |
| [Before redirect to identity provider](operations/op-03-before-redirect-idp.md) | Forward session/IdP hints and `x-idp-*` query parameters to the Okta authorize request | `OnRedirectToIdentityProvider` event, during an OIDC challenge | `AuthenticationProperties`, request query | Augmented `ProtocolMessage` | [BR-OIDC-09](business-rules.md#br-oidc-09-session%2Fidp-hints-and-x-idp-wildcard-parameters-are-forwarded-to-okta) |
| [Handle remote authentication failure](operations/op-04-handle-remote-failure.md) | Classify and log an IdP failure, then redirect to login instead of showing an exception page | `OnRemoteFailure` event | `context.Failure` | Logged entry; 302 redirect with `x-idp-err=se` | [BR-OIDC-10](business-rules.md#br-oidc-10-remote-auth-failures-are-classified%2C-logged%2C-then-redirected), [BR-OIDC-16](business-rules.md#br-oidc-16-login-redirects-always-use-https-on-the-current-host) |
| [Synchronize JWT values into claims and cookie](operations/op-05-sync-jwt-claims.md) | Keep `id_token`/`access_token` claims current and re-sign the auth cookie on every authenticated request | `JwtClaimMiddleware.InvokeAsync`, every request | `HttpContext`, current cookie principal | Updated claims; re-issued cookie, or an exception | [BR-OIDC-11](business-rules.md#br-oidc-11-a-failed-cookie-re-authentication-aborts-the-request), [BR-OIDC-12](business-rules.md#br-oidc-12-jwt-claims-and-the-cookie-are-refreshed-on-every-request) |
| [Redirect expired sessions to token refresh](operations/op-06-redirect-expired-sessions.md) | Detect an expired access token and short-circuit the pipeline with a redirect to the JwtRefresh app | `RefreshJwtMiddleware.InvokeAsync`, every request when enabled | Cookie `access_token`/`refresh_token` | 301 redirect, or `next` | [BR-OIDC-13](business-rules.md#br-oidc-13-an-expired-access-token-short-circuits-the-pipeline) |
| [Resolve data-protection certificate](operations/op-07-resolve-dp-certificate.md) | Find the LocalMachine\My certificate used to encrypt the data-protection key ring | Called from [OP-01](operations/op-01-configure-cookie-auth.md) step 2 | `DPCertName` | A certificate, `null` (developer machine), or an exception | [BR-OIDC-14](business-rules.md#br-oidc-14-data-protection-certificate-must-match-subject-and-validity-window), [BR-OIDC-15](business-rules.md#br-oidc-15-a-missing-dp-certificate-is-fatal-outside-developer-machines) |
| [Redirect to login](operations/op-08-redirect-to-login.md) | Build and issue the HTTPS redirect to the login application, preserving the return URL | `OnRedirectToLogin` cookie event; [OP-04](operations/op-04-handle-remote-failure.md) step 3 | `HttpContext`, optional extra query parameters | 302 redirect | [BR-OIDC-16](business-rules.md#br-oidc-16-login-redirects-always-use-https-on-the-current-host), [BR-OIDC-17](business-rules.md#br-oidc-17-the-origin-query-parameter-is-conditionally-added-from-the-already-built-login-url), [BR-OIDC-10](business-rules.md#br-oidc-10-remote-auth-failures-are-classified%2C-logged%2C-then-redirected) |
| [Transform role claims](operations/op-09-transform-role-claims.md) | Map Okta group/role claim values onto `ClaimTypes.Role`, with a per-subject cache guard | `RoleClaimTransformer.TransformAsync`, only if a consumer registers it as `IClaimsTransformation` | Current `ClaimsPrincipal` | Cloned principal with role claims added, or the original principal | [BR-OIDC-18](business-rules.md#br-oidc-18-a-role-claim-is-created-for-every-matching%2C-non-array-claim), [BR-OIDC-19](business-rules.md#br-oidc-19-role-transformation-is-skipped-when-roles-already-exist-and-are-cached), [BR-OIDC-20](business-rules.md#br-oidc-20-array-valued-role-claims-are-ignored) |
| [Role-transform cache](operations/op-10-role-transform-cache.md) | Track, per subject, how recently roles were transformed, to skip repeat work | Called from [OP-09](operations/op-09-transform-role-claims.md) steps 3 and 4 | Subject identifier | `true`/`false`; cache upsert | [BR-OIDC-21](business-rules.md#br-oidc-21-transformed-roles-are-considered-fresh-for-a-configurable-offset) |
| [Resolve OIDC settings](operations/op-11-resolve-oidc-settings.md) | Read every OIDC setting from the Environment API section `NetCore_OIDC` | Property access on `IOidcSettings`, on demand throughout this library | Environment API section `NetCore_OIDC` | String, int, scope array, or credential value | [BR-OIDC-22](business-rules.md#br-oidc-22-all-oidc-settings-are-read-from-the-netcore_oidc-section), [BR-OIDC-04](business-rules.md#br-oidc-04-oidc-client-credentials-come-from-a-secure%2C-library-hashed-setting) |
| [Refresh tokens against Okta](operations/op-12-refresh-tokens.md) | Exchange a refresh token for a new access/ID token pair at the Okta token endpoint | `JwtRefreshService.Refresh`, called by a consumer (for example, the JwtRefresh application) | `refreshToken`, `clientId`/`clientSecret` | `JwtSet`, or `JwtRefreshException` | [BR-OIDC-23](business-rules.md#br-oidc-23-a-token-refresh-posts-to-a-composed-okta-endpoint-with-basic-auth), [BR-OIDC-24](business-rules.md#br-oidc-24-a-non-successful-refresh-raises-a-typed-exception), [BR-OIDC-25](business-rules.md#br-oidc-25-refreshed-expiry-is-computed-from-expires_in%3B-rotation-is-discarded) |
| [Enforce URL casing](operations/op-13-enforce-url-casing.md) | Permanently redirect mixed-case URLs to a lower-cased form | `LowerCaseUrlMiddleware.InvokeAsync`, every request | Request scheme/host/path | 301 redirect, or `next` | [BR-OIDC-26](business-rules.md#br-oidc-26-mixed-case-urls-are-permanently-redirected-to-lower-case) |

## Process flow diagram
The following diagram visualizes the primary request-time flow across the thirteen operations.

```mermaid
flowchart TD
	Challenge([OIDC/Cookie challenge]) --> OP01[Configure cookie authentication]
	Challenge --> OP02[Configure OpenID Connect]
	OP01 --> OP07[Resolve data-protection certificate]
	OP02 --> OP11a[Resolve OIDC settings]
	OP02 --> Redirect{Redirect to IdP?}
	Redirect -->|Yes| OP03[Before redirect to identity provider]
	OP03 --> Okta([Okta authorize/token endpoint])
	Okta --> Failure{Remote failure?}
	Failure -->|Yes| OP04[Handle remote authentication failure]
	OP04 --> OP08[Redirect to login]
	Failure -->|No| Authenticated([Authenticated session established])
	Authenticated --> Request([Subsequent request])
	Request --> CasingGate{Challenge type is OpenIdConnect?}
	CasingGate -->|Yes| OP13[Enforce URL casing]
	CasingGate -->|No| OP05
	OP13 --> OP05[Synchronize JWT values into claims and cookie]
	OP05 -->|failure| ErrorHandler([HttpContextAuthenticateException])
	OP05 --> RefreshGate{UseJwtRefreshMiddleware enabled?}
	RefreshGate -->|Yes| OP06[Redirect expired sessions to token refresh]
	RefreshGate -->|No| PipelineContinues([Pipeline continues])
	OP06 -->|expired| JwtRefreshApp([301 to external JwtRefresh application - request ends])
	OP06 -->|valid| PipelineContinues
	OP01 -.->|OnRedirectToLogin| OP08
	OP11a -.-> OP01
	OP11a -.-> OP02
	OP11a -.-> OP06

	subgraph Unwired["Not wired into the active pipeline<br>unless a consumer registers RoleClaimTransformer<br>as IClaimsTransformation"]
		OP09[Transform role claims] --> OP10[Role-transform cache]
	end
	OP05 -.->|"only if registered as IClaimsTransformation -<br>not observed in any analyzed registration"| OP09

	subgraph External["Separate, externally-triggered flow - no in-repo caller"]
		OP12[Refresh tokens against Okta]
	end
	JwtRefreshApp -.->|downstream call, unknown/inferred| OP12
```

`RefreshJwtMiddleware`'s 301 redirect ends the current request; it does not call `JwtRefreshService.Refresh` directly, so [OP-12](operations/op-12-refresh-tokens.md) is shown as a separate, externally-triggered flow whose caller is unknown/inferred** — not a verified continuation of this request. Likewise, [Transform role claims](operations/op-09-transform-role-claims.md) and [Role-transform cache](operations/op-10-role-transform-cache.md) are shown in a separate subgraph because `RoleClaimTransformer` is never invoked automatically unless a consumer registers it as `IClaimsTransformation` — no such registration was found in this repository (see [Service registration](service-registration.md)).

## Failure and edge-case behavior
- A missing or expired data-protection certificate is fatal on any non-developer machine ([Resolve data-protection certificate](operations/op-07-resolve-dp-certificate.md)); on a developer machine the cookie/OIDC-state key rings fall back to unencrypted storage.
- A malformed or missing `BOLAppCredentials` secure setting throws during OIDC options construction ([Configure OpenID Connect](operations/op-02-configure-oidc.md) step 2), before any OIDC request can be served.
- A failed cookie re-authentication aborts the request with `HttpContextAuthenticateException` ([Synchronize JWT values into claims and cookie](operations/op-05-sync-jwt-claims.md) step 5); there is no built-in recovery path.
- The `RoleClaimIdentitifier`/`RoleClaimIdentifier` key mismatch (see [Configuration](configuration.md), CR-01) means [Transform role claims](operations/op-09-transform-role-claims.md) can silently promote every non-array claim to a role, if and only if a consumer has registered the transformer at all.
- `RefreshJwtMiddleware` decodes the access token without signature validation before checking expiry ([Redirect expired sessions to token refresh](operations/op-06-redirect-expired-sessions.md) step 3); a malformed token is expected to throw `ArgumentException`*.
- `LowerCaseUrlMiddleware`'s mixed-case detection has a scheme/pathBase bug (see [Enforce URL casing](operations/op-13-enforce-url-casing.md)), and always calls `next` even after issuing a 301.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Business rules](business-rules.md)
- [Security and authorization](security-authorization.md)
- [Service registration](service-registration.md)
- [API reference](api/index.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.OIDC/src/SAIF.NetCore.OIDC/SAIF.NetCore.OIDC.csproj`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Constants.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Contracts/RefreshTokenResult.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Exceptions/HttpContextAuthenticateException.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Exceptions/JwtRefreshException.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Exceptions/MissingDataProtectionCertificateException.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Extensions/ClaimsPrincipalExtensions.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Extensions/HttpContextExtensions.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Middleware/JwtClaimMiddleware.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Middleware/LowerCaseUrlMiddleware.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Middleware/RefreshJwtMiddleware.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Options/ConfigureCookieAuthenticationOptions.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Options/ConfigureOpenIdConnectOptions.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/ClaimTransformationCacheService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/DataProtectionCertService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/JwtAuthorizationCookieClaimService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/JwtRefreshService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/LoginRedirectService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/OidcSettings.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/RoleClaimService.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/RoleClaimTransformer.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/UrlCaseService.cs`
