From ff805a267c834d551464e54dde316ca1f85abcd3 Mon Sep 17 00:00:00 2001 From: amirhmd Date: Thu, 2 Jul 2026 10:55:29 +0200 Subject: [PATCH 1/2] Add iOS/MAUI cookie handling docs Closes #2387 --- docs/docs/advanced/configuration.md | 2 +- docs/docs/advanced/ios-maui-cookies.md | 40 ++++++++++++++++++++++++++ docs/docs/usage/request.md | 4 +++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 docs/docs/advanced/ios-maui-cookies.md diff --git a/docs/docs/advanced/configuration.md b/docs/docs/advanced/configuration.md index 06d684c5f..bc8a59037 100644 --- a/docs/docs/advanced/configuration.md +++ b/docs/docs/advanced/configuration.md @@ -172,7 +172,7 @@ RestSharp allows configuring `RestClient` using client options, as mentioned at | `PreAuthenticate` | Gets or sets a value that indicates whether the client sends an `Authorization` header with the request. Not supported in browsers. | | `RemoteCertificateValidationCallback` | Custom function to validate the server certificate. Normally, it's used when the server uses a certificate that isn't trusted by default. | | `BaseHost` | Value for the `Host` header sent with each request. | -| `CookieContainer` | Custom cookie container that will be shared among all calls made by the client. Normally not required as RestSharp handles cookies without using a client-level cookie container. | +| `CookieContainer` | Custom cookie container that will be shared among all calls made by the client. Normally not required as RestSharp handles cookies without using a client-level cookie container. On iOS and Mac Catalyst, see [iOS / MAUI Cookie Handling](ios-maui-cookies.md) for additional platform-specific setup required to receive `Set-Cookie` headers. | | `Timeout` | Client-level timeout as `TimeSpan`. Default is 100 seconds. See [Configuring Timeouts](#configuring-timeouts) for details on timeout behavior. | | `Encoding` | Default request encoding. Override it only if you don't use UTF-8. | | `ThrowOnDeserializationError` | Forces the client to throw if it fails to deserialize the response. Remember that not all deserialization issues forces the serializer to throw. Default is `false`, so the client will return a `RestResponse` with deserialization exception details. Only relevant for `Execute...` functions. | diff --git a/docs/docs/advanced/ios-maui-cookies.md b/docs/docs/advanced/ios-maui-cookies.md new file mode 100644 index 000000000..a47117f91 --- /dev/null +++ b/docs/docs/advanced/ios-maui-cookies.md @@ -0,0 +1,40 @@ +--- +title: iOS / MAUI Cookie Handling +--- + +On iOS and Mac Catalyst, `Set-Cookie` response headers are silently absent from `RestResponse.Cookies` and from raw response headers, even though the same request works correctly on Android, Windows, and Linux. + +## Root cause + +Apple's networking stack, `NSURLSession`, intercepts `Set-Cookie` headers before they reach .NET's `HttpClient`. The cookies are stored in `NSHTTPCookieStorage` instead of being forwarded as headers, so RestSharp never sees them. + +## Fix + +Disable `NSURLSession`'s automatic cookie storage by supplying a custom session configuration via [`ConfigureMessageHandler`](configuration.md#using-custom-message-handler): + +```csharp +#if IOS || MACCATALYST +using Foundation; + +var options = new RestClientOptions(baseUrl) { + ConfigureMessageHandler = _ => { + var config = NSUrlSessionConfiguration.DefaultSessionConfiguration; + config.HttpCookieStorage = null; + config.HttpCookieAcceptPolicy = NSHttpCookieAcceptPolicy.Never; + return new NSUrlSessionHandler(config); + } +}; +#endif +``` + +With this configuration, `NSURLSession` passes `Set-Cookie` headers through to .NET unchanged. RestSharp captures them in `RestResponse.Cookies` as it does on all other platforms. + +## Multi-tenant safety + +Disabling the system cookie store is the correct approach for API clients that serve multiple users or tenants. When `NSHTTPCookieStorage` is active, cookies from one user's session can leak into a subsequent request made by the same client instance. Opting out gives RestSharp full control: cookies are scoped to the individual request via the per-request [`CookieContainer`](../usage/request.md#cookies), and nothing is persisted outside that scope. + +:::warning Anti-pattern: shared CookieContainer with UseCookies = true +Do **not** set `HttpClientHandler.UseCookies = true` with a shared `CookieContainer` on the handler. This pools cookies across every request made by the client, which is unsafe for any multi-tenant scenario on any platform. + +RestSharp deliberately avoids this pattern. Cookies are managed at the request level; see [Cookies](../usage/request.md#cookies) for details. +::: diff --git a/docs/docs/usage/request.md b/docs/docs/usage/request.md index 76343b347..e03117bcb 100644 --- a/docs/docs/usage/request.md +++ b/docs/docs/usage/request.md @@ -239,6 +239,10 @@ There is a `CookieContainer` instance on the request level. You can either assig If your use case requires sharing cookies between requests made by the client instance, you can use the client-level `CookieContainer`, which you must provide as the options' property. You can add cookies to the container using the container API. No response cookies, however, would be auto-added to the container, but you can do it in code by getting cookies from the `Cookies` property of the response and adding them to the client-level container available via `IRestClient.Options.CookieContainer` property. +:::note iOS and Mac Catalyst +On iOS and Mac Catalyst, `NSURLSession` intercepts `Set-Cookie` headers before .NET can read them, so `RestResponse.Cookies` will be empty even on a successful request. See [iOS / MAUI Cookie Handling](../advanced/ios-maui-cookies.md) for the fix. +::: + ## Request Body RestSharp supports multiple ways to add a request body: From fb78f1bc1f4cfb3440734fa7972a421e323b47f6 Mon Sep 17 00:00:00 2001 From: amirhmd Date: Thu, 2 Jul 2026 11:05:58 +0200 Subject: [PATCH 2/2] Add note on handler config being bypassed by iOS cookie workaround --- docs/docs/advanced/ios-maui-cookies.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/docs/advanced/ios-maui-cookies.md b/docs/docs/advanced/ios-maui-cookies.md index a47117f91..5ef6c3e1a 100644 --- a/docs/docs/advanced/ios-maui-cookies.md +++ b/docs/docs/advanced/ios-maui-cookies.md @@ -29,6 +29,18 @@ var options = new RestClientOptions(baseUrl) { With this configuration, `NSURLSession` passes `Set-Cookie` headers through to .NET unchanged. RestSharp captures them in `RestResponse.Cookies` as it does on all other platforms. +:::warning This replaces RestSharp's configured handler +Returning a new `NSUrlSessionHandler` from `ConfigureMessageHandler` **replaces** the `HttpClientHandler` that RestSharp already configured from `RestClientOptions` — it does not wrap or extend it. Any handler-level options you set on `RestClientOptions` will **not** apply to the new handler, including: + +- `Proxy` +- `Credentials` / `UseDefaultCredentials` +- `AutomaticDecompression` +- `RemoteCertificateValidationCallback` +- `ClientCertificates` + +If your app relies on any of these, re-apply the equivalent configuration directly on the `NSUrlSessionHandler` instance (or on the `NSUrlSessionConfiguration`) before returning it. +::: + ## Multi-tenant safety Disabling the system cookie store is the correct approach for API clients that serve multiple users or tenants. When `NSHTTPCookieStorage` is active, cookies from one user's session can leak into a subsequent request made by the same client instance. Opting out gives RestSharp full control: cookies are scoped to the individual request via the per-request [`CookieContainer`](../usage/request.md#cookies), and nothing is persisted outside that scope. @@ -37,4 +49,4 @@ Disabling the system cookie store is the correct approach for API clients that s Do **not** set `HttpClientHandler.UseCookies = true` with a shared `CookieContainer` on the handler. This pools cookies across every request made by the client, which is unsafe for any multi-tenant scenario on any platform. RestSharp deliberately avoids this pattern. Cookies are managed at the request level; see [Cookies](../usage/request.md#cookies) for details. -::: +::: \ No newline at end of file