Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pnpm build
- `docs/docs/advanced/` — Advanced topics (authenticators, configuration, serialization, interceptors, error handling)
- `docs/docs/intro.md` — Getting started page
- `docs/docs/changelog.md` — Release changelog
- `docs/versioned_docs/` — Frozen snapshots for past versions (v110–v113)
- `docs/versioned_docs/` — Frozen snapshots for past versions (v110–v114.1)
- `docs/versioned_sidebars/` — Sidebar configs for each frozen version
- `docs/versions.json` — List of released doc versions
- `docs/src/pages/` — Standalone pages (migration guide, support)
Expand Down
4 changes: 4 additions & 0 deletions docs/docs/advanced/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ var client = new RestClient(
);
```

:::note
The built-in JSON and XML serializers are registered lazily and are only instantiated when first used. When you replace the JSON serializer in `configureSerialization` (for example with `UseNewtonsoftJson()`), `System.Text.Json` is never loaded. This matters on .NET Framework, where a different `System.Text.Json` version loaded by the host application could otherwise cause assembly binding failures.
:::

All RestSharp serializers implement the `IRestSerializer` interface. Among other things, the interface requires implementing the `AcceptedContentTypes` property, which must return a collection of content types supported by the serializer. Being configured to use certain serializers, RestSharp populates the `Accept` header accordingly, so it doesn't need to be set manually.

When making a call, RestSharp sets the request content type according to the request body type. For example, when you use `AddJsonBody`, the content type is set to `application/json`. Normally, you won't need to set the `Content-Type` header manually. If you need to set a custom content type for a JSON call, you can use the optional `contentType` argument of `AddJsonBody`, for example:
Expand Down
19 changes: 19 additions & 0 deletions docs/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ For release notes of previous versions, please check the [Releases page](https:/

Changes between major versions are documented in the documentation for each version on this website.

## v114.1

### New features

* **`TrailingHeaders` on `RestResponse`** — HTTP response trailers (RFC 9110 trailer fields) are now exposed as a separate `TrailingHeaders` collection, kept apart from `Headers` and `ContentHeaders`. Trailers are populated on .NET 8 and later after the response content has been read; on .NET Framework and .NET Standard the collection is always empty. ([#2414](https://github.com/restsharp/RestSharp/pull/2414))

### Behavior changes

* **Default serializers are registered lazily** — `RestClient` no longer instantiates the built-in `System.Text.Json` serializer before the `configureSerialization` callback runs. Replacing the JSON serializer (for example with `UseNewtonsoftJson()`) now avoids loading `System.Text.Json` at all, which fixes assembly binding failures in .NET Framework plugin scenarios. ([#2403](https://github.com/restsharp/RestSharp/pull/2403))
* **Synchronous `Execute` methods run on the thread pool** — the sync-over-async bridge now uses `Task.Run` and `GetAwaiter().GetResult()` instead of a custom synchronization context, preventing deadlocks when calling the synchronous API from a UI or ASP.NET synchronization context. ([#2371](https://github.com/restsharp/RestSharp/pull/2371))

### Bug fixes

* Fix `AddObjectStatic` throwing `NullReferenceException` when any property of the object is `null`. Null properties are now skipped, consistent with `AddObject`. ([#2401](https://github.com/restsharp/RestSharp/pull/2401))

### Dependencies

* `RestSharp.Serializers.CsvHelper` now depends on CsvHelper 33.1.0. ([#2378](https://github.com/restsharp/RestSharp/pull/2378))

## v114.0

### Breaking changes
Expand Down
13 changes: 13 additions & 0 deletions docs/docs/usage/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Response object contains the following properties:
| `Cookies` | `CookieCollection?` | Collection of cookies received with the response, if any. |
| `Headers` | Collection of `HeaderParameter` | Response headers. |
| `ContentHeaders` | Collection of `HeaderParameter` | Response content headers. |
| `TrailingHeaders` | Collection of `HeaderParameter` | Response trailers (headers sent after the body). Populated on .NET 8+ only, empty otherwise. |
| `ErrorMessage` | `string?` | Transport or another non-HTTP error generated while attempting request. |
| `ErrorException` | `Exception?` | Exception thrown when executing the request, if any. |
| `Version` | `Version?` | HTTP protocol version of the request. |
Expand All @@ -42,6 +43,18 @@ foreach (var param in response.MergedParameters) {
}
```

### Trailing headers

Some servers send additional header fields after the response body, known as trailers (see [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#name-trailer-fields)). They are typically used for message integrity checks, signatures, or status information that is only known once the body has been generated. RestSharp exposes them via the `TrailingHeaders` property, separately from `Headers` and `ContentHeaders`:

```csharp
var response = await client.ExecuteAsync(request);

var digest = response.TrailingHeaders.FirstOrDefault(h => h.Name == "Digest")?.Value;
```

Trailers are populated on .NET 8 and later, after the response content has been fully read. On .NET Framework and .NET Standard targets, `HttpClient` does not expose trailers, so the collection is always empty. Trailers are also not available when streaming the response with `DownloadStreamAsync`, since the body is not read by RestSharp in that case. As with every other response property, `TrailingHeaders` is only filled in by RestSharp's default response processing: if you supply an [`AdvancedResponseWriter`](../advanced/configuration.md), your writer receives the raw `HttpResponseMessage` and must copy its `TrailingHeaders` itself.

In addition, `RestResponse<T>` has one additional property:

| Property | Type | Description |
Expand Down
7 changes: 7 additions & 0 deletions docs/versioned_docs/version-v114.1/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"label": "Advanced topics",
"position": 4,
"link": {
"type": "generated-index"
}
}
271 changes: 271 additions & 0 deletions docs/versioned_docs/version-v114.1/advanced/authenticators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
# Authenticators

RestSharp includes authenticators for basic HTTP, OAuth1 and token-based (JWT and OAuth2).

There are two ways to set the authenticator: client-wide or per-request.

Set the client-wide authenticator by assigning the `Authenticator` property of `RestClientOptions`:

```csharp
var options = new RestClientOptions("https://example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var client = new RestClient(options);
```

To set the authenticator per-request, assign the `Authenticator` property of `RestRequest`:

```csharp
var request = new RestRequest("/api/users/me") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var response = await client.ExecuteAsync(request, cancellationToken);
```

## Basic authentication

The `HttpBasicAuthenticator` allows you pass a username and password as a basic `Authorization` header using a base64 encoded string.

```csharp
var options = new RestClientOptions("https://example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var client = new RestClient(options);
```

## OAuth1

For OAuth1 authentication the `OAuth1Authenticator` class provides static methods to help generate an OAuth authenticator.
OAuth1 authenticator will add the necessary OAuth parameters to the request, including signature.

The authenticator will use `HMAC SHA1` to create a signature by default.
Each static function to create the authenticator allows you to override the default and use another method to generate the signature.

### Request token

Getting a temporary request token is the usual first step in the 3-legged OAuth1 flow.
Use `OAuth1Authenticator.ForRequestToken` function to get the request token authenticator.
This method requires a `consumerKey` and `consumerSecret` to authenticate.

```csharp
var options = new RestClientOptions("https://api.twitter.com") {
Authenticator = OAuth1Authenticator.ForRequestToken(consumerKey, consumerSecret)
};
var client = new RestClient(options);
var request = new RestRequest("oauth/request_token");
```

The response should contain the token and the token secret, which can then be used to complete the authorization process.
If you need to provide the callback URL, assign the `CallbackUrl` property of the authenticator to the callback destination.

### Access token

Getting an access token is the usual third step in the 3-legged OAuth1 flow.
This method retrieves an access token when provided `consumerKey`, `consumerSecret`, `oauthToken`, and `oauthTokenSecret`.
If you don't have a token for this call, you need to make a call to get the request token as described above.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, oauthToken, oauthTokenSecret
);
var options = new RestClientOptions("https://api.twitter.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
var request = new RestRequest("oauth/access_token");
```

If the second step in 3-leg OAuth1 flow returned a verifier value, you can use another overload of `ForAccessToken`:

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, oauthToken, oauthTokenSecret, verifier
);
```

The response should contain the access token that can be used to make calls to protected resources.

For refreshing access tokens, use one of the two overloads of `ForAccessToken` that accept `sessionHandle`.

### Protected resource

When the access token is available, use `ForProtectedResource` function to get the authenticator for accessing protected resources.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, consumerSecret, accessToken, accessTokenSecret
);
var options = new RestClientOptions("https://api.twitter.com/1.1") {
Authenticator = authenticator
};
var client = new RestClient(options);
var request = new RestRequest("statuses/update.json", Method.Post)
.AddParameter("status", "Hello Ladies + Gentlemen, a signed OAuth request!")
.AddParameter("include_entities", "true");
```

### xAuth

xAuth is a simplified version of OAuth1. It allows sending the username and password as `x_auth_username` and `x_auth_password` request parameters and directly get the access token. xAuth is not widely supported, but RestSharp still allows using it.

Create an xAuth authenticator using `OAuth1Authenticator.ForClientAuthentication` function:

```csharp
var authenticator = OAuth1Authenticator.ForClientAuthentication(
consumerKey, consumerSecret, username, password
);
```

### 0-legged OAuth

The access token authenticator can be used in 0-legged OAuth scenarios by providing `null` for the `consumerSecret`.

```csharp
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey, null, oauthToken, oauthTokenSecret
);
```

## OAuth2

RestSharp provides OAuth2 authenticators at two levels: **token lifecycle authenticators** that handle the full flow (obtaining, caching, and refreshing tokens automatically), and **simple authenticators** that just stamp a pre-obtained token onto requests.

### Token lifecycle authenticators

These authenticators manage tokens end-to-end. They use their own internal `HttpClient` for token endpoint calls, so there's no circular dependency with the `RestClient` they're attached to. All are thread-safe for concurrent use.

#### Client credentials

Use `OAuth2ClientCredentialsAuthenticator` for machine-to-machine flows. It POSTs `grant_type=client_credentials` to your token endpoint, caches the token, and refreshes it automatically before it expires.

```csharp
var request = new OAuth2TokenRequest(
"https://auth.example.com/oauth2/token",
"my-client-id",
"my-client-secret"
) {
Scope = "api.read api.write"
};

var options = new RestClientOptions("https://api.example.com") {
Authenticator = new OAuth2ClientCredentialsAuthenticator(request)
};
using var client = new RestClient(options);
```

The authenticator will obtain a token on the first request and reuse it until it expires. The `ExpiryBuffer` property (default 30 seconds) controls how far in advance of actual expiry the token is considered stale.

#### Refresh token

Use `OAuth2RefreshTokenAuthenticator` when you already have an access token and refresh token (e.g., from an authorization code flow). It uses the initial access token until it expires, then automatically refreshes using the `refresh_token` grant type.

```csharp
var request = new OAuth2TokenRequest(
"https://auth.example.com/oauth2/token",
"my-client-id",
"my-client-secret"
) {
OnTokenRefreshed = response => {
// Persist the new tokens to your storage
SaveTokens(response.AccessToken, response.RefreshToken);
}
};

var options = new RestClientOptions("https://api.example.com") {
Authenticator = new OAuth2RefreshTokenAuthenticator(
request,
accessToken: "current-access-token",
refreshToken: "current-refresh-token",
expiresAt: DateTimeOffset.UtcNow.AddMinutes(30)
)
};
using var client = new RestClient(options);
```

If the server rotates refresh tokens, the authenticator will automatically use the new refresh token for subsequent refreshes. The `OnTokenRefreshed` callback fires every time a new token is obtained, so you can persist the updated tokens.

#### Custom token provider

Use `OAuth2TokenAuthenticator` when you have a non-standard token flow or want full control over how tokens are obtained. Provide an async delegate that returns an `OAuth2Token`:

```csharp
var options = new RestClientOptions("https://api.example.com") {
Authenticator = new OAuth2TokenAuthenticator(async cancellationToken => {
var token = await myCustomTokenService.GetTokenAsync(cancellationToken);
return new OAuth2Token(token.Value, token.ExpiresAt);
})
};
using var client = new RestClient(options);
```

The authenticator caches the result and re-invokes your delegate when the token expires.

#### Bringing your own HttpClient

By default, the token lifecycle authenticators create their own `HttpClient` for token endpoint calls (and dispose it when the authenticator is disposed). If you need to customize it (e.g., for proxy settings or mTLS), pass your own:

```csharp
var request = new OAuth2TokenRequest(
"https://auth.example.com/oauth2/token",
"my-client-id",
"my-client-secret"
) {
HttpClient = myCustomHttpClient // not disposed by the authenticator
};
```

### Simple authenticators

If you manage tokens yourself and just need to stamp them onto requests, use these simpler authenticators.

`OAuth2UriQueryParameterAuthenticator` accepts the access token as the only constructor argument, and it will send the provided token as a query parameter `oauth_token`.

`OAuth2AuthorizationRequestHeaderAuthenticator` has two constructors. One only accepts a single argument, which is the access token. The other constructor also allows you to specify the token type. The authenticator will then add an `Authorization` header using the specified token type or `OAuth` as the default token type, and the token itself.

For example:

```csharp
var authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(
token, "Bearer"
);
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

The code above will tell RestSharp to send the bearer token with each request as a header. Essentially, the code above does the same as the sample for `JwtAuthenticator` below.

## JWT

The JWT authentication can be supported by using `JwtAuthenticator`. It is a very simple class that can be constructed like this:

```csharp
var authenticator = new JwtAuthenticator(myToken);
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

For each request, it will add an `Authorization` header with the value `Bearer <your token>`.

As you might need to refresh the token from, you can use the `SetBearerToken` method to update the token.

## Custom authenticator

You can write your own implementation by implementing `IAuthenticator` and
registering it with your RestClient:

```csharp
var authenticator = new SuperAuthenticator(); // implements IAuthenticator
var options = new RestClientOptions("https://example.com") {
Authenticator = authenticator
};
var client = new RestClient(options);
```

The `Authenticate` method is the very first thing called upon calling `RestClient.Execute` or `RestClient.Execute<T>`.
It gets the `RestRequest` currently being executed giving you access to every part of the request data (headers, parameters, etc.)

You can find an example of using the built-in OAuth2 authenticator in a typed API client [here](../usage/example.md#authenticator).
Loading
Loading