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..5ef6c3e1a --- /dev/null +++ b/docs/docs/advanced/ios-maui-cookies.md @@ -0,0 +1,52 @@ +--- +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. + +:::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. + +:::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. +::: \ No newline at end of file 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: