Configure an outbound client to send OnBehalfOfUser¶
Attaches the current user's name as a persistent OnBehalfOfUser header on an IHttpJsonClient, so it is sent on every subsequent request made by that client instance.
| Field | Value |
|---|---|
| Operation ID | OP-09 |
| Trigger | Consumer client setup code (typically after SetNtlmAuthentication()) |
| Inputs | ICurrentUserService |
| Outputs | IHttpJsonClient reconfigured with a persisted header |
| Primary components | IHttpJsonClientExtensions.ConfigureOnBehalfOfUserAuthorization(ICurrentUserService) |
| Entry point | src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Extensions/IHttpJsonClientExtensions.cs |
Steps¶
- Call
client.SetOptions(new HttpClientOptions { PersistHeadersAfterRequest = true }). This replaces the client's entire existingHttpClientOptionsobject (retry delay, retry statuses, serializer, and any other previously-configured option), not just the persistence flag — a client that had other custom options set before this call loses them unless they are re-specified here. (BR-NTLM-009)
Origin: Step 1 is implemented in Configure options (
SAIF.NetCore.HttpClients) and executed on behalf of this operation. 2. Resolve the user name via Get the current user name, embedded here for reference (friendlyNamedefaulttrue): - MVC clients readIHttpContextAccessor.HttpContext.User.Identity.Name(null-safe). - Blazor clients synchronously block onAuthenticationStateProvider.GetAuthenticationStateAsync()and then readUser.Identity.Namefrom the result. - IffriendlyNameistrue(the default), the resolved name is passed throughDomainlessUser()before being returned; otherwise the raw name is returned.nullis returned if there is no identity in either branch.Origin: Step 2 is implemented in Get the current user name (
SAIF.NetCore.NtlmApiAuth) and executed on behalf of this operation. 3. If the name is non-empty, callclient.Headers.Add("OnBehalfOfUser", username); this throwsArgumentExceptionif the key already exists. (BR-NTLM-009, BR-NTLM-010) - On each subsequent request made by thisIHttpJsonClient,HttpJsonClient.CreateRequestcopies the instance's customHeaders(including theOnBehalfOfUserheader added here) onto the outbound RestSharp request, then conditionally clears theHeaderscollection: because step 1 setPersistHeadersAfterRequest = true, the clear is skipped and the header survives for the next request too, rather than being removed after a single use.Origin: This sub-step is implemented in Execute request (
SAIF.NetCore.HttpClients) and executed on behalf of this operation.
Because step 1 sets PersistHeadersAfterRequest = true before step 3 adds the header, the OnBehalfOfUser header added here is retained by the IHttpJsonClient and resent on every subsequent request made by that same client instance, where the receiving server reads it via the middleware described in Convert the OnBehalfOfUser header into a claim.
Process flow¶
flowchart TD
Start([ConfigureOnBehalfOfUserAuthorization]) --> S1[SetOptions PersistHeadersAfterRequest true]
S1 --> S2[Resolve current user name]
S2 --> S3{Name non-empty?}
S3 -->|No| End([No header added])
S3 -->|Yes| S4["Headers.Add OnBehalfOfUser, name"]
S4 --> End2([Header sent on every request])
Failure and edge-case behavior¶
- If the target
IHttpJsonClientoutlives a single user (e.g. registered Singleton, or a cached/typed client), the first configured user's name is sent on every later request from that client instance, including requests made on behalf of other users. - Calling this operation twice on the same client throws
ArgumentExceptionfrom the underlyingDictionary.Add.
Related documentation¶
Sources¶
src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Extensions/IHttpJsonClientExtensions.cs