Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oauth2 support section for dotnet client #1652

Merged
merged 3 commits into from
May 24, 2023
Merged
Changes from 2 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
82 changes: 82 additions & 0 deletions site/dotnet-api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Key sections of the guide are:
* [Async Consumer Implementations](#consuming-async)
* [Concurrency Considerations and Safety](#concurrency)
* [Automatic Recovery From Network Failures](#recovery)
* [OAuth 2 support](#oauth2-support)
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved

An [API reference](https://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.html) is available separately.

Expand Down Expand Up @@ -874,3 +875,84 @@ growing between recoveries.
Acknowledgements with stale delivery tags will not be
sent. Applications that use manual acknowledgements and automatic
recovery must be capable of handling redeliveries.

## <a id="oauth2-support" class="anchor" href="#oauth2-support">OAuth 2 Support</a>

The client can authenticate against an OAuth 2 server like [UAA](https://github.com/cloudfoundry/uaa).
The [OAuth 2 plugin](https://github.com/rabbitmq/rabbitmq-server/tree/main/deps/rabbitmq_auth_backend_oauth2)
must be enabled on the server side and configured to use the same OAuth 2 server as the client.

MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
### <a id="oauth2-getting-token" class="anchor" href="#oauth2-getting-token">Getting the OAuth 2 Token</a>

The .Net client provides the `OAuth2ClientCredentialsProvider`
class to get a JWT token using the [OAuth 2 Client Credentials flow](https://tools.ietf.org/html/rfc6749#section-4.4).
The client will send the access token in the password field when opening a connection.
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
The broker will then verify the access token signature, validity, and permissions
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
before authorising the connection and granting access to the requested
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
virtual host.

<pre class="lang-csharp">
using RabbitMQ.Client.Impl.OAuth2;

...

ICredentialsProvider credentialsProvider = new OAuth2ClientCredentialsProvider("prod-uaa-1",
new OAuth2Client("client_id", "client_secret", new Uri("http://somedomain.com/token")));

var connectionFactory = new ConnectionFactory {
CredentialsProvider = credentialsProvider
};
var connection = connectionFactory.CreateConnection();

</pre>

In production, make sure to use HTTPS for the token endpoint URI and configure
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
a `HttpClientHandler` appropriately for the `HttpClient` :

<pre class="lang-csharp">
...
HttpClientHandler httpClientHandler = buildHttpClientHandlerWithTLSEnabled();

ICredentialsProvider credentialsProvider = new OAuth2ClientCredentialsProvider("prod-uaa-1",
new OAuth2Client("client_id", "client_secret", new Uri("http://somedomain.com/token"),
OAuth2ClientCredentialsProvider.EMPTY, httpClientHandler));
</pre>

Note: In case your Authorization server requires extra request parameters beyond what the specification
requires, you can do so by adding `<key, value>` pairs to a `Dictionary` and passing it to the
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
`OAuth2ClientCredentialsProvider` constructor rather than an `EMPTY` one as shown above.


### <a id="oauth2-refreshing-token" class="anchor" href="#oauth2-refreshing-token">Refreshing the Token</a>

Tokens expire and the broker will refuse operations on connections with
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
expired tokens. To avoid this, it is possible to call
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
`ICredentialsProvider#Refresh()` before expiration and send the new
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
token to the server. This is cumbersome
from an application point of view, so the .Net client provides
help with the `TimerBasedCredentialRefresher`. This utility
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
schedules a timer for every token received. Once the timer expires, it reports the
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
connection which in turn calls `ICredentialsProvider#Refresh()`.

The following snippet shows how to create a `TimerBasedCredentialRefresher`
instance and set it up on the `ConnectionFactory`:

<pre class="lang-csharp">
using RabbitMQ.Client.Impl.OAuth2;

...

ICredentialsProvider credentialsProvider = new OAuth2ClientCredentialsProvider("prod-uaa-1",
new OAuth2Client("client_id", "client_secret", new Uri("http://somedomain.com/token")));
ICredentialsRefresher credentialsRefresher = new TimerBasedCredentialRefresher();

var connectionFactory = new ConnectionFactory {
CredentialsProvider = credentialsProvider,
CredentialsRefresher = credentialsRefresher
};
var connection = connectionFactory.CreateConnection();
</pre>

The `TimerBasedCredentialRefresher` schedules a refresh after 2/3
of the token validity time, e.g. if the token expires in 60 minutes,
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved
it will be refreshed after 40 minutes.
MarcialRosales marked this conversation as resolved.
Show resolved Hide resolved