---
title: SAIF.NetCore.HttpClients security and authorization
summary: Outbound credential-transmission mechanisms and associated risk findings; this library performs no inbound authorization.
status: draft
owner: Framework
last_reviewed: 2026-10-02
library: SAIF.NetCore.HttpClients
reference_type: package
---

# SAIF.NetCore.HttpClients security and authorization

This page documents the **outbound** credential-transmission mechanisms implemented by `SAIF.NetCore.HttpClients`. The library makes no access-control decisions of its own: it is an outbound HTTP client that attaches credentials and identity headers a **remote** service may use for its own authentication and authorization. Scope is narrow by design — 0 Active Directory resources are referenced and 0 authorization enforcement points exist in this library's code.

## Authorization architecture

There is no authorization architecture to document for this library. `HttpJsonClient` holds and attaches outbound credentials (bearer, HTTP Basic, NTLM) and caller-identity headers on requests it sends to a caller-supplied base URL; it does not evaluate any access-control decision, call a security service, or consult Active Directory itself. The diagram below shows where credentials are attached in the outbound request pipeline, not an authorization decision flow.

```mermaid
flowchart TD
    Caller["Consumer code<br>(e.g. SAIF.NetCore.ServiceExtensions,<br>SAIF.NetCore.DBConnectionClient)"] --> Client[HttpJsonClient]
    Client --> Attach["Attach configured credential:<br>Bearer / Basic / NTLM<br>+ caller-identity headers"]
    Attach --> Remote["Caller-supplied remote endpoint<br>(authenticates/authorizes independently)"]
```

## Security interfaces and contracts

This library defines no local authorization interfaces. The contracts below are the outbound credential-facing surface that consumers configure — they describe what credentials are sent and how, not an access-control decision made by this library.

| Contract | Member(s) | Purpose | Source |
| --- | --- | --- | --- |
| `IHttpJsonClient` | `BearerToken`, `SetBasicAuthentication(userName, password)`, `SetNtlmAuthentication(userName, password)`, `SetNtlmAuthentication()` | Public interface through which consumers configure outbound credentials; `HttpJsonClient` is its sole implementation | `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpJsonClient.cs` |
| `HttpClientOptions` | `PersistHeadersAfterRequest` | Controls whether the `Headers`-dictionary-based credentials (Basic auth, custom on-behalf-of headers) survive past the first request | `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs` |
| `ClientInfo` | `MachineName`, `ApplicationId`, `UserName` | Caller-identity values propagated as unauthenticated `Client-Name`/`Application-ID`/`Client-User` headers (see SEC-HTTP-05) | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs` |

None of these contracts perform authorization. They exist so other libraries can program against `IHttpJsonClient`/`HttpClientOptions` rather than RestSharp directly, while still controlling exactly which outbound credential a request carries and how long it persists on the instance.

## AD resource catalog

No AD groups or resource names are referenced in this library. `SetNtlmAuthentication()` uses the current process Windows identity as a credential source for outbound calls, which is a credential source, not an AD group check.

| Resource name | Purpose | Enforcement point | Source |
| --- | --- | --- | --- |
| — (none) | — | — | — |

## Authorization enforcement points

None. This library performs no authorization. It does not define `[SAIFAuthorize]`-style attributes, does not call `IsAuthorized`/`IsInRole`/`HasAccess`, and integrates with no WCF SecurityService. It only attaches credentials and identity headers that a remote service may use for its own authentication and authorization decisions.

## Outbound credential mechanisms

The following mechanisms are available on `HttpJsonClient` to authenticate outbound requests. "Lifetime on the instance" describes whether the credential is cleared between requests under the library's default options.

| ID | Mechanism | Public member | How it is transmitted | Lifetime on the instance | Source |
| --- | --- | --- | --- | --- | --- |
| SEC-HTTP-01 | Bearer token | `BearerToken` (`string` get/set) | Added as an `Authorization` request header whenever non-empty | Persists until the caller changes it — a property, not a `Headers` entry, so it is never cleared by the library | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` |
| SEC-HTTP-02 | HTTP Basic | `SetBasicAuthentication(userName, password)` | `Headers.Add("Authorization", "Basic " + Base64(UTF8("user:password")))` | Stored in `Headers`; cleared after the first request unless `PersistHeadersAfterRequest = true` | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` |
| SEC-HTTP-03 | NTLM (explicit credentials) | `SetNtlmAuthentication(userName, password)` | `NtlmAuthenticator(user, pw)`, assigned to every new `RestClient` | Persists for the life of the instance (private field, never cleared) | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` |
| SEC-HTTP-04 | NTLM (process identity) | `SetNtlmAuthentication()` | `NtlmAuthenticator()` (RestSharp → current process default credentials)* | Persists for the life of the instance | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` |
| SEC-HTTP-05 | Caller identity propagation | Constructor / `SetClientInfo(baseUrl, userName, machineName, applicationId)` | Headers `Client-Name` (machine), `Application-ID`, `Client-User` added to every request | Persists (`_clientInfo` field) | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs`, `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs` |
| SEC-HTTP-06 | Arbitrary custom headers (e.g. on-behalf-of user) | `Headers` dictionary (public get/set) | Copied verbatim to each request | Cleared after the first request unless `PersistHeadersAfterRequest = true` | `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs` |

The identity headers in SEC-HTTP-05 are assertions, not authenticated claims: the library sends whatever `userName`, `machineName`, and `applicationId` the caller supplies, with no signature or integrity protection, so a receiving service must not treat `Client-User` as an authenticated identity*.

## Credential-handling risks

The following risks were identified from a full read of this library's source together with its consumers' call sites. Severity is the analyst's qualitative assessment, not a formally scored rating.

| ID | Risk | Severity | Related mechanism |
| --- | --- | --- | --- |
| SR-HTTP-01 | Passwords/secrets are accepted and held as plain `string`; the Basic credential is readable from the public `Headers` dictionary; no zeroing or disposal occurs | Medium | [SEC-HTTP-02](#outbound-credential-mechanisms) |
| SR-HTTP-02 | 401 is a default retry status, so Bearer/NTLM credentials are resent up to 4 times per call, amplifying failed-authentication counts and AD lockout risk for service accounts* | Medium–High | [SEC-HTTP-01](#outbound-credential-mechanisms), [SEC-HTTP-03](#outbound-credential-mechanisms), [SEC-HTTP-04](#outbound-credential-mechanisms) |
| SR-HTTP-03 | With default options, Basic auth and custom headers are **not** resent on retry, so a 401 retry cannot succeed and only adds roughly 3 seconds of blocking latency (affects token-refresh-style callers) | Low (correctness/availability) | [SEC-HTTP-02](#outbound-credential-mechanisms), [SEC-HTTP-06](#outbound-credential-mechanisms) |
| SR-HTTP-04 | `PersistHeadersAfterRequest = true` keeps the Basic `Authorization` header (and any on-behalf-of identity header) on the instance for all later requests; rotating credentials then throws `ArgumentException` | Medium | [SEC-HTTP-02](#outbound-credential-mechanisms) |
| SR-HTTP-05 | A `BearerToken` combined with an `Authorization` key already in `Headers` produces duplicate `Authorization` headers on one request; server-side resolution of the duplicate is unverified** | Low–Medium | [SEC-HTTP-01](#outbound-credential-mechanisms), [SEC-HTTP-02](#outbound-credential-mechanisms) |
| SR-HTTP-06 | No HTTPS enforcement exists; Basic and bearer credentials can be sent over a plain `http://` base URL | Medium (High if any production URL is HTTP)** | [SEC-HTTP-01](#outbound-credential-mechanisms), [SEC-HTTP-02](#outbound-credential-mechanisms) |
| SR-HTTP-07 | Mutable credential state on a shared instance can leak credentials or identity across callers or threads if a consumer holds the transient-registered instance beyond its intended scope* | Medium | All mechanisms |
| SR-HTTP-08 | Identity headers (`Client-User`, `Client-Name`, `Application-ID`) are unauthenticated, caller-asserted values* | Low (for this library; depends on the receiver) | [SEC-HTTP-05](#outbound-credential-mechanisms) |

## Notes
- This page's scope is deliberately narrow: outbound credential transmission only. The library has 0 AD resource references, 0 authorization enforcement points, 0 data-level authorization checks, and 0 segregation-of-duties rules.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.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.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/HttpJsonClient/HttpJsonClient.cs`
- `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/ClientInfo.cs`
- `src/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients/SAIF.NetCore.HttpClients.Tests/HttpJsonClients/HttpJsonClientTests.cs`
- `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/HttpClientOptions.cs`
- `src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/IHttpJsonClient.cs`
- `src/SAIF.NetCore.DbConnectionClient/SAIF.NetCore.DBConnectionClient/SAIF.NetCore.DBConnectionClient/ConnectionClient.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/JwtRefreshService.cs`
- `src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Extensions/IHttpJsonClientExtensions.cs`
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions.Integration.Tests/Controllers/HttpClientController.cs`
