Skip to content

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

  1. Call client.SetOptions(new HttpClientOptions { PersistHeadersAfterRequest = true }). This replaces the client's entire existing HttpClientOptions object (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 (friendlyName default true): - MVC clients read IHttpContextAccessor.HttpContext.User.Identity.Name (null-safe). - Blazor clients synchronously block on AuthenticationStateProvider.GetAuthenticationStateAsync() and then read User.Identity.Name from the result. - If friendlyName is true (the default), the resolved name is passed through DomainlessUser() before being returned; otherwise the raw name is returned. null is 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, call client.Headers.Add("OnBehalfOfUser", username); this throws ArgumentException if the key already exists. (BR-NTLM-009, BR-NTLM-010) - On each subsequent request made by this IHttpJsonClient, HttpJsonClient.CreateRequest copies the instance's custom Headers (including the OnBehalfOfUser header added here) onto the outbound RestSharp request, then conditionally clears the Headers collection: because step 1 set PersistHeadersAfterRequest = 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 IHttpJsonClient outlives 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 ArgumentException from the underlying Dictionary.Add.

Sources

  • src/SAIF.NetCore.NtlmApiAuth/src/SAIF.NetCore.NtlmApiAuth/Extensions/IHttpJsonClientExtensions.cs