From dd4b2e4bae0b416524a32f4e141c641031897cf7 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 11 May 2023 11:41:35 +0200 Subject: [PATCH 1/3] WIP Add Oauth2 support section Pending: TLS --- site/dotnet-api-guide.md | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/site/dotnet-api-guide.md b/site/dotnet-api-guide.md index 651f688682..46ad4ff4d6 100644 --- a/site/dotnet-api-guide.md +++ b/site/dotnet-api-guide.md @@ -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) An [API reference](https://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.html) is available separately. @@ -874,3 +875,68 @@ 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. + +## OAuth 2 Support + +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. + +### Getting the OAuth 2 Token + +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. +The broker will then verify the access token signature, validity, and permissions +before authorising the connection and granting access to the requested +virtual host. + +
+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();
+
+
+ + +### Refreshing the Token + +Tokens expire and the broker will refuse operations on connections with +expired tokens. To avoid this, it is possible to call +`ICredentialsProvider#Refresh()` before expiration and send the new +token to the server. This is cumbersome +from an application point of view, so the .Net client provides +help with the `DefaultCredentialsRefreshService`. This utility +tracks used tokens, refreshes them before they expire, and send +the new tokens for the connections it is responsible for. + +The following snippet shows how to create a `TimerBasedCredentialRefresher` +instance and set it up on the `ConnectionFactory`: + +
+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();
+
+ +The `TimerBasedCredentialRefresher` schedules a refresh after 2/3 +of the token validity time, e.g. if the token expires in 60 minutes, +it will be refreshed after 40 minutes. From 505f9d55b8c5deead26da9be1e6b34a21a2df430 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 11 May 2023 15:50:57 +0200 Subject: [PATCH 2/3] Reword --- site/dotnet-api-guide.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/site/dotnet-api-guide.md b/site/dotnet-api-guide.md index 46ad4ff4d6..f4f5b6da01 100644 --- a/site/dotnet-api-guide.md +++ b/site/dotnet-api-guide.md @@ -906,6 +906,22 @@ var connection = connectionFactory.CreateConnection(); +In production, make sure to use HTTPS for the token endpoint URI and configure +a `HttpClientHandler` appropriately for the `HttpClient` : + +
+...
+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));
+
+ +Note: In case your Authorization server requires extra request parameters beyond what the specification +requires, you can do so by adding `` pairs to a `Dictionary` and passing it to the +`OAuth2ClientCredentialsProvider` constructor rather than an `EMPTY` one as shown above. + ### Refreshing the Token @@ -914,9 +930,9 @@ expired tokens. To avoid this, it is possible to call `ICredentialsProvider#Refresh()` before expiration and send the new token to the server. This is cumbersome from an application point of view, so the .Net client provides -help with the `DefaultCredentialsRefreshService`. This utility -tracks used tokens, refreshes them before they expire, and send -the new tokens for the connections it is responsible for. +help with the `TimerBasedCredentialRefresher`. This utility +schedules a timer for every token received. Once the timer expires, it reports the +connection which in turn calls `ICredentialsProvider#Refresh()`. The following snippet shows how to create a `TimerBasedCredentialRefresher` instance and set it up on the `ConnectionFactory`: @@ -939,4 +955,4 @@ var connection = connectionFactory.CreateConnection(); The `TimerBasedCredentialRefresher` schedules a refresh after 2/3 of the token validity time, e.g. if the token expires in 60 minutes, -it will be refreshed after 40 minutes. +it will be refreshed after 40 minutes. From daa05653bc5bfe42d2ada6488cfaccbd2a213d96 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 22 May 2023 15:18:39 +0200 Subject: [PATCH 3/3] Incorporate reviewer's feedback --- site/dotnet-api-guide.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/site/dotnet-api-guide.md b/site/dotnet-api-guide.md index f4f5b6da01..3eae71ca7e 100644 --- a/site/dotnet-api-guide.md +++ b/site/dotnet-api-guide.md @@ -37,7 +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) +* [OAuth 2 Support](#oauth2-support) An [API reference](https://rabbitmq.github.io/rabbitmq-dotnet-client/api/RabbitMQ.Client.html) is available separately. @@ -880,14 +880,14 @@ recovery must be capable of handling redeliveries. 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. +must be turned on on the server side and configured to use the same OAuth 2 server as the client. ### Getting the OAuth 2 Token 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. -The broker will then verify the access token signature, validity, and permissions +The client sends the access token in the password field when opening a connection. +The broker then verifies the access token signature, validity, and permissions before authorising the connection and granting access to the requested virtual host. @@ -906,7 +906,7 @@ var connection = connectionFactory.CreateConnection(); -In production, make sure to use HTTPS for the token endpoint URI and configure +In production, ensure you use HTTPS for the token endpoint URI and configure a `HttpClientHandler` appropriately for the `HttpClient` :
@@ -919,19 +919,17 @@ ICredentialsProvider credentialsProvider = new OAuth2ClientCredentialsProvider("
 
Note: In case your Authorization server requires extra request parameters beyond what the specification -requires, you can do so by adding `` pairs to a `Dictionary` and passing it to the +requires, you can add `` pairs to a `Dictionary` and passing it to the `OAuth2ClientCredentialsProvider` constructor rather than an `EMPTY` one as shown above. ### Refreshing the Token -Tokens expire and the broker will refuse operations on connections with -expired tokens. To avoid this, it is possible to call -`ICredentialsProvider#Refresh()` before expiration and send the new -token to the server. This is cumbersome -from an application point of view, so the .Net client provides +When tokens expire, the broker refuses further operations over the connection. It is possible to call +`ICredentialsProvider#Refresh()` before expiring and send the new +token to the server. This is not convenient for applications so, the .Net client provides help with the `TimerBasedCredentialRefresher`. This utility -schedules a timer for every token received. Once the timer expires, it reports the +schedules a timer for every token received. When the timer expires, it reports the connection which in turn calls `ICredentialsProvider#Refresh()`. The following snippet shows how to create a `TimerBasedCredentialRefresher` @@ -954,5 +952,5 @@ var connection = connectionFactory.CreateConnection(); The `TimerBasedCredentialRefresher` schedules a refresh after 2/3 -of the token validity time, e.g. if the token expires in 60 minutes, -it will be refreshed after 40 minutes. +of the token validity time. For example, if the token expires in 60 minutes, +it is refreshed after 40 minutes.