diff --git a/Build.csproj b/Build.csproj
index 2acee38279..01008f2878 100644
--- a/Build.csproj
+++ b/Build.csproj
@@ -8,6 +8,7 @@
+
diff --git a/RabbitMQDotNetClient.sln b/RabbitMQDotNetClient.sln
index ee53fa9c93..490df48bd0 100644
--- a/RabbitMQDotNetClient.sln
+++ b/RabbitMQDotNetClient.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29806.167
+# Visual Studio Version 17
+VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{34486CC0-D61E-46BA-9E5E-6E8EFA7C34B5}"
ProjectSection(SolutionItems) = preProject
@@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApplications", "TestApp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChannel", "projects\TestApplications\CreateChannel\CreateChannel.csproj", "{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Client.OAuth2", "projects\RabbitMQ.Client.OAuth2\RabbitMQ.Client.OAuth2.csproj", "{794C7B31-0E9A-44A4-B285-0F3CAF6209F1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -45,6 +47,10 @@ Global
{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Release|Any CPU.Build.0 = Release|Any CPU
+ {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/projects/Benchmarks/Benchmarks.csproj b/projects/Benchmarks/Benchmarks.csproj
index d780f4e332..bb69066308 100644
--- a/projects/Benchmarks/Benchmarks.csproj
+++ b/projects/Benchmarks/Benchmarks.csproj
@@ -20,7 +20,7 @@
-
+
diff --git a/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs
new file mode 100644
index 0000000000..9a8abb7ee5
--- /dev/null
+++ b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs
@@ -0,0 +1,296 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace RabbitMQ.Client.OAuth2
+{
+ public interface IOAuth2Client
+ {
+ public IToken RequestToken();
+ public IToken RefreshToken(IToken token);
+ }
+
+ public interface IToken
+ {
+ public string AccessToken { get; }
+ public string RefreshToken { get; }
+ public TimeSpan ExpiresIn { get; }
+ public bool hasExpired { get; }
+ }
+
+ public class Token : IToken
+ {
+ private readonly JsonToken _source;
+ private readonly DateTime _lastTokenRenewal;
+
+ public Token(JsonToken json)
+ {
+ this._source = json;
+ this._lastTokenRenewal = DateTime.Now;
+ }
+
+ public string AccessToken
+ {
+ get
+ {
+ return _source.access_token;
+ }
+ }
+
+ public string RefreshToken
+ {
+ get
+ {
+ return _source.refresh_token;
+ }
+ }
+
+ public TimeSpan ExpiresIn
+ {
+ get
+ {
+ return TimeSpan.FromSeconds(_source.expires_in);
+ }
+ }
+
+ bool IToken.hasExpired
+ {
+ get
+ {
+ TimeSpan age = DateTime.Now - _lastTokenRenewal;
+ return age > ExpiresIn;
+ }
+ }
+ }
+
+ public class OAuth2ClientBuilder
+ {
+ private readonly string _clientId;
+ private readonly string _clientSecret;
+ private readonly Uri _tokenEndpoint;
+ private string _scope;
+ private IDictionary _additionalRequestParameters;
+ private HttpClientHandler _httpClientHandler;
+
+ public OAuth2ClientBuilder(string clientId, string clientSecret, Uri tokenEndpoint)
+ {
+ _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));
+ _clientSecret = clientSecret ?? throw new ArgumentNullException(nameof(clientSecret));
+ _tokenEndpoint = tokenEndpoint ?? throw new ArgumentNullException(nameof(tokenEndpoint));
+
+ }
+
+ public OAuth2ClientBuilder SetScope(string scope)
+ {
+ _scope = scope ?? throw new ArgumentNullException(nameof(scope));
+ return this;
+ }
+
+ public OAuth2ClientBuilder SetHttpClientHandler(HttpClientHandler handler)
+ {
+ _httpClientHandler = handler ?? throw new ArgumentNullException(nameof(handler));
+ return this;
+ }
+
+ public OAuth2ClientBuilder AddRequestParameter(string param, string paramValue)
+ {
+ if (param == null)
+ {
+ throw new ArgumentNullException("param is null");
+ }
+ if (paramValue == null)
+ {
+ throw new ArgumentNullException("paramValue is null");
+ }
+ if (_additionalRequestParameters == null)
+ {
+ _additionalRequestParameters = new Dictionary();
+ }
+ _additionalRequestParameters[param] = paramValue;
+ return this;
+ }
+
+ public IOAuth2Client Build()
+ {
+ return new OAuth2Client(_clientId, _clientSecret, _tokenEndpoint,
+ _scope, _additionalRequestParameters, _httpClientHandler);
+ }
+ }
+
+ /**
+ * Default implementation of IOAuth2Client. It uses Client_Credentials OAuth2 flow to request a
+ * token. The basic constructor assumes no scopes are needed only the OAuth2 Client credentiuals.
+ * The additional constructor accepts a Dictionary with all the request parameters passed onto the
+ * OAuth2 request token.
+ */
+ internal class OAuth2Client : IOAuth2Client, IDisposable
+ {
+ const string GRANT_TYPE = "grant_type";
+ const string CLIENT_ID = "client_id";
+ const string SCOPE = "scope";
+ const string CLIENT_SECRET = "client_secret";
+ const string REFRESH_TOKEN = "refresh_token";
+ const string GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials";
+
+ private readonly string _clientId;
+ private readonly string _clientSecret;
+ private readonly Uri _tokenEndpoint;
+ private readonly string _scope;
+ private readonly IDictionary _additionalRequestParameters;
+
+ public static readonly IDictionary EMPTY = new Dictionary();
+
+ private HttpClient _httpClient;
+
+ public OAuth2Client(string clientId, string clientSecret, Uri tokenEndpoint, string scope,
+ IDictionary additionalRequestParameters,
+ HttpClientHandler httpClientHandler)
+ {
+ this._clientId = clientId;
+ this._clientSecret = clientSecret;
+ this._scope = scope;
+ this._additionalRequestParameters = additionalRequestParameters == null ? EMPTY : additionalRequestParameters;
+ this._tokenEndpoint = tokenEndpoint;
+
+ _httpClient = httpClientHandler == null ? new HttpClient() :
+ new HttpClient(httpClientHandler);
+ _httpClient.DefaultRequestHeaders.Accept.Clear();
+ _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+ }
+
+ public IToken RequestToken()
+ {
+ var req = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint);
+ req.Content = new FormUrlEncodedContent(buildRequestParameters());
+
+ Task response = _httpClient.SendAsync(req);
+ response.Wait();
+ response.Result.EnsureSuccessStatusCode();
+ Task token = response.Result.Content.ReadFromJsonAsync();
+ token.Wait();
+ return new Token(token.Result);
+ }
+
+ public IToken RefreshToken(IToken token)
+ {
+ if (token.RefreshToken == null)
+ {
+ throw new InvalidOperationException("Token has no Refresh Token");
+ }
+
+ var req = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint)
+ {
+ Content = new FormUrlEncodedContent(buildRefreshParameters(token))
+ };
+
+ Task response = _httpClient.SendAsync(req);
+ response.Wait();
+ response.Result.EnsureSuccessStatusCode();
+ Task refreshedToken = response.Result.Content.ReadFromJsonAsync();
+ refreshedToken.Wait();
+ return new Token(refreshedToken.Result);
+ }
+
+ public void Dispose()
+ {
+ _httpClient.Dispose();
+ }
+
+ private Dictionary buildRequestParameters()
+ {
+ var dict = new Dictionary(_additionalRequestParameters);
+ dict.Add(CLIENT_ID, _clientId);
+ dict.Add(CLIENT_SECRET, _clientSecret);
+ if (_scope != null && _scope.Length > 0)
+ {
+ dict.Add(SCOPE, _scope);
+ }
+ dict.Add(GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS);
+ return dict;
+ }
+
+ private Dictionary buildRefreshParameters(IToken token)
+ {
+ var dict = buildRequestParameters();
+ dict.Remove(GRANT_TYPE);
+ dict.Add(GRANT_TYPE, REFRESH_TOKEN);
+ if (_scope != null)
+ {
+ dict.Add(SCOPE, _scope);
+ }
+ dict.Add(REFRESH_TOKEN, token.RefreshToken);
+ return dict;
+ }
+ }
+
+ public class JsonToken
+ {
+ public JsonToken()
+ {
+ }
+
+ public JsonToken(string access_token, string refresh_token, TimeSpan expires_in_span)
+ {
+ this.access_token = access_token;
+ this.refresh_token = refresh_token;
+ this.expires_in = (long)expires_in_span.TotalSeconds;
+ }
+
+ public JsonToken(string access_token, string refresh_token, long expires_in)
+ {
+ this.access_token = access_token;
+ this.refresh_token = refresh_token;
+ this.expires_in = expires_in;
+ }
+
+ public string access_token
+ {
+ get; set;
+ }
+
+ public string refresh_token
+ {
+ get; set;
+ }
+
+ public long expires_in
+ {
+ get; set;
+ }
+ }
+}
diff --git a/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs b/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs
new file mode 100644
index 0000000000..08972f4e2f
--- /dev/null
+++ b/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs
@@ -0,0 +1,142 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Threading;
+
+namespace RabbitMQ.Client.OAuth2
+{
+ public class OAuth2ClientCredentialsProvider : ICredentialsProvider
+ {
+ const int TOKEN_RETRIEVAL_TIMEOUT = 5000;
+ private ReaderWriterLock _lock = new ReaderWriterLock();
+
+ private readonly string _name;
+ private readonly IOAuth2Client _oAuth2Client;
+ private IToken _token;
+
+ public OAuth2ClientCredentialsProvider(string name, IOAuth2Client oAuth2Client)
+ {
+ _name = name ?? throw new ArgumentNullException(nameof(name));
+ _oAuth2Client = oAuth2Client ?? throw new ArgumentNullException(nameof(oAuth2Client));
+ }
+
+ public string Name
+ {
+ get
+ {
+ return _name;
+ }
+ }
+
+ public string UserName
+ {
+ get
+ {
+ checkState();
+ return string.Empty;
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ return checkState().AccessToken;
+ }
+ }
+
+ public Nullable ValidUntil
+ {
+ get
+ {
+ IToken t = checkState();
+ if (t is null)
+ {
+ return null;
+ }
+ else
+ {
+ return t.ExpiresIn;
+ }
+ }
+ }
+
+ public void Refresh()
+ {
+ retrieveToken();
+ }
+
+ private IToken checkState()
+ {
+ _lock.AcquireReaderLock(TOKEN_RETRIEVAL_TIMEOUT);
+ try
+ {
+ if (_token != null)
+ {
+ return _token;
+ }
+ }
+ finally
+ {
+ _lock.ReleaseReaderLock();
+ }
+
+ return retrieveToken();
+ }
+
+ private IToken retrieveToken()
+ {
+ _lock.AcquireWriterLock(TOKEN_RETRIEVAL_TIMEOUT);
+ try
+ {
+ return requestOrRenewToken();
+ }
+ finally
+ {
+ _lock.ReleaseReaderLock();
+ }
+ }
+
+ private IToken requestOrRenewToken()
+ {
+ if (_token == null || _token.RefreshToken == null)
+ {
+ _token = _oAuth2Client.RequestToken();
+ }
+ else
+ {
+ _token = _oAuth2Client.RefreshToken(_token);
+ }
+ return _token;
+ }
+ }
+}
diff --git a/projects/RabbitMQ.Client.OAuth2/README.md b/projects/RabbitMQ.Client.OAuth2/README.md
new file mode 100644
index 0000000000..dbcdcbe7bc
--- /dev/null
+++ b/projects/RabbitMQ.Client.OAuth2/README.md
@@ -0,0 +1 @@
+# RabbitMQ .NET Client - OAuth2
diff --git a/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj
new file mode 100644
index 0000000000..9e2c29ab48
--- /dev/null
+++ b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj
@@ -0,0 +1,72 @@
+
+
+
+ net6.0;netstandard2.0
+ $(NoWarn);CS1591
+ true
+ RabbitMQ OAuth2 Client Library for .NET
+ VMware
+ VMware, Inc. or its affiliates.
+ Copyright © 2007-2023 VMware, Inc. or its affiliates.
+ The RabbitMQ OAuth2 Client Library for .NET enables OAuth2 token refresh for RabbitMQ.Client
+ true
+ true
+ icon.png
+ Apache-2.0 OR MPL-2.0
+ https://www.rabbitmq.com/dotnet.html
+ rabbitmq, amqp, oauth2
+ RabbitMQ
+ true
+ https://github.com/rabbitmq/rabbitmq-dotnet-client.git
+ true
+ snupkg
+ ../rabbit.snk
+ true
+ oauth2-
+ minimal
+ true
+ ../../packages
+ true
+ latest
+ 7.0
+ README.md
+
+
+
+ true
+ true
+
+
+
+
+ $(MinVerMajor).$(MinVerMinor).$(MinVerPatch)-$(MinVerPreRelease)-pr.$(CONCOURSE_PULL_REQUEST_NUMBER)
+ $(PackageVersion)+$(MinVerBuildMetadata)
+ $(PackageVersion)
+
+
+
+
+
+
+
+
+
+ <_Parameter1>Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+
+ <_Parameter1>Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/RabbitMQ.Client.OAuth2/icon.png b/projects/RabbitMQ.Client.OAuth2/icon.png
new file mode 100644
index 0000000000..092bfef15c
Binary files /dev/null and b/projects/RabbitMQ.Client.OAuth2/icon.png differ
diff --git a/projects/RabbitMQ.Client/README.md b/projects/RabbitMQ.Client/README.md
new file mode 100644
index 0000000000..5cf0d0cd26
--- /dev/null
+++ b/projects/RabbitMQ.Client/README.md
@@ -0,0 +1 @@
+# RabbitMQ .NET Client
diff --git a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
index bce32216b3..1a51baf82a 100644
--- a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
+++ b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj
@@ -25,10 +25,11 @@
v
minimal
true
- ..\..\packages
+ ../../packages
true
latest
7.0
+ README.md
@@ -48,6 +49,7 @@
+
<_Parameter1>Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5
diff --git a/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs b/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs
new file mode 100644
index 0000000000..5357897529
--- /dev/null
+++ b/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs
@@ -0,0 +1,76 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+
+namespace RabbitMQ.Client
+{
+ public class BasicCredentialsProvider : ICredentialsProvider
+ {
+ private readonly string _name;
+ private readonly string _userName;
+ private readonly string _password;
+
+ public BasicCredentialsProvider(string name, string userName, string password)
+ {
+ _name = name ?? string.Empty;
+ _userName = userName ?? throw new ArgumentNullException(nameof(userName));
+ _password = password ?? throw new ArgumentNullException(nameof(password));
+ }
+
+ public string Name
+ {
+ get { return _name; }
+ }
+
+ public string UserName
+ {
+ get { return _userName; }
+ }
+
+ public string Password
+ {
+ get { return _password; }
+ }
+
+ public Nullable ValidUntil
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ public void Refresh()
+ {
+ }
+ }
+}
diff --git a/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs b/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs
index 9c66e26cd4..4ff107df22 100644
--- a/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs
+++ b/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs
@@ -55,7 +55,14 @@ public sealed class ConnectionConfig
///
/// Password to use when authenticating to the server.
///
- public string Password { get; internal set; }
+ public string Password { get; }
+
+ ///
+ /// Default CredentialsProvider implementation. If set, this
+ /// overrides UserName / Password
+ ///
+ public ICredentialsProvider CredentialsProvider;
+ public ICredentialsRefresher CredentialsRefresher;
///
/// SASL auth mechanisms to use.
@@ -76,6 +83,7 @@ public sealed class ConnectionConfig
/// Maximum channel number to ask for.
///
public ushort MaxChannelCount { get; }
+
///
/// Frame-max parameter to ask for (in bytes).
///
@@ -136,7 +144,9 @@ public sealed class ConnectionConfig
internal Func FrameHandlerFactory { get; }
- internal ConnectionConfig(string virtualHost, string userName, string password, IList authMechanisms,
+ internal ConnectionConfig(string virtualHost, string userName, string password,
+ ICredentialsProvider credentialsProvider, ICredentialsRefresher credentialsRefresher,
+ IList authMechanisms,
IDictionary clientProperties, string? clientProvidedName,
ushort maxChannelCount, uint maxFrameSize, bool topologyRecoveryEnabled,
TopologyRecoveryFilter topologyRecoveryFilter, TopologyRecoveryExceptionHandler topologyRecoveryExceptionHandler,
@@ -147,6 +157,8 @@ internal ConnectionConfig(string virtualHost, string userName, string password,
VirtualHost = virtualHost;
UserName = userName;
Password = password;
+ CredentialsProvider = credentialsProvider ?? new BasicCredentialsProvider(clientProvidedName, userName, password);
+ CredentialsRefresher = credentialsRefresher;
AuthMechanisms = authMechanisms;
ClientProperties = clientProperties;
ClientProvidedName = clientProvidedName;
diff --git a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs
index e4cd1171f1..bbe2a10579 100644
--- a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs
+++ b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs
@@ -160,6 +160,8 @@ public sealed class ConnectionFactory : ConnectionFactoryBase, IConnectionFactor
///
public static System.Net.Sockets.AddressFamily DefaultAddressFamily { get; set; }
+ public static readonly ICredentialsRefresher DefaultCredentialsRefresher = new NoOpCredentialsRefresher();
+
///
/// Set to false to disable automatic connection recovery.
/// Defaults to true.
@@ -307,11 +309,26 @@ public AmqpTcpEndpoint Endpoint
["information"] = Encoding.UTF8.GetBytes("Licensed under the MPL. See https://www.rabbitmq.com/")
};
+ ///
+ /// Username to use when authenticating to the server.
+ ///
+ public string UserName { get; set; } = DefaultUser;
+
///
/// Password to use when authenticating to the server.
///
public string Password { get; set; } = DefaultPass;
+ ///
+ /// CredemtialsProvider used to obtain username and pasword.
+ ///
+ public ICredentialsProvider CredentialsProvider { get; set; }
+
+ ///
+ /// Used to refresh credentials.
+ ///
+ public ICredentialsRefresher CredentialsRefresher { get; set; } = DefaultCredentialsRefresher;
+
///
/// Maximum channel number to ask for.
///
@@ -327,11 +344,6 @@ public AmqpTcpEndpoint Endpoint
///
public TimeSpan RequestedHeartbeat { get; set; } = DefaultHeartbeat;
- ///
- /// Username to use when authenticating to the server.
- ///
- public string UserName { get; set; } = DefaultUser;
-
///
/// Virtual host to access during this connection.
///
@@ -540,6 +552,8 @@ private ConnectionConfig CreateConfig(string clientProvidedName)
VirtualHost,
UserName,
Password,
+ CredentialsProvider,
+ CredentialsRefresher,
AuthMechanisms,
ClientProperties,
clientProvidedName,
diff --git a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs
index 4f8736baa6..b1480cf887 100644
--- a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs
+++ b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs
@@ -73,6 +73,14 @@ public interface IConnectionFactory
///
string VirtualHost { get; set; }
+ ///
+ /// Credentials provider. It is optional. When set, username and password
+ /// are obtained thru this provider.
+ ///
+ ICredentialsProvider CredentialsProvider { get; set; }
+
+ ICredentialsRefresher CredentialsRefresher { get; set; }
+
///
/// Sets or gets the AMQP Uri to be used for connections.
///
diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs b/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs
new file mode 100644
index 0000000000..2bb365f80d
--- /dev/null
+++ b/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs
@@ -0,0 +1,53 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+namespace RabbitMQ.Client
+{
+ public interface ICredentialsProvider
+ {
+ string Name { get; }
+ string UserName { get; }
+ string Password { get; }
+
+ ///
+ /// If credentials have an expiry time this property returns the interval.
+ /// Otherwise, it returns null.
+ ///
+ System.TimeSpan? ValidUntil { get; }
+
+ ///
+ /// Before the credentials are available, be it Username, Password or ValidUntil,
+ /// the credentials must be obtained by calling this method.
+ /// And to keep it up to date, this method must be called before the ValidUntil interval.
+ ///
+ void Refresh();
+ }
+}
diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs
new file mode 100644
index 0000000000..ebcd438924
--- /dev/null
+++ b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs
@@ -0,0 +1,141 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Tracing;
+namespace RabbitMQ.Client
+{
+ public interface ICredentialsRefresher
+ {
+ ICredentialsProvider Register(ICredentialsProvider provider, NotifyCredentialRefreshed callback);
+ bool Unregister(ICredentialsProvider provider);
+
+ delegate void NotifyCredentialRefreshed(bool succesfully);
+ }
+
+ [EventSource(Name = "TimerBasedCredentialRefresher")]
+ public class TimerBasedCredentialRefresherEventSource : EventSource
+ {
+ public static TimerBasedCredentialRefresherEventSource Log { get; } = new TimerBasedCredentialRefresherEventSource();
+
+ [Event(1)]
+ public void Registered(string name) => WriteEvent(1, "Registered", name);
+ [Event(2)]
+ public void Unregistered(string name) => WriteEvent(2, "nNregistered", name);
+ [Event(3)]
+ public void ScheduledTimer(string name, double interval) => WriteEvent(3, "ScheduledTimer", name, interval);
+ [Event(4)]
+ public void TriggeredTimer(string name) => WriteEvent(4, "TriggeredTimer", name);
+ [Event(5)]
+ public void RefreshedCredentials(string name, bool succesfully) => WriteEvent(5, "RefreshedCredentials", name, succesfully);
+
+ }
+
+ public class TimerBasedCredentialRefresher : ICredentialsRefresher
+ {
+ private Dictionary _registrations = new Dictionary();
+
+ public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback)
+ {
+ if (!provider.ValidUntil.HasValue || provider.ValidUntil.Value.Equals(TimeSpan.Zero))
+ {
+ return provider;
+ }
+
+ _registrations.Add(provider, scheduleTimer(provider, callback));
+ TimerBasedCredentialRefresherEventSource.Log.Registered(provider.Name);
+ return provider;
+ }
+
+ public bool Unregister(ICredentialsProvider provider)
+ {
+ if (!_registrations.ContainsKey(provider))
+ {
+ return false;
+ }
+
+ var timer = _registrations[provider];
+ if (timer != null)
+ {
+ TimerBasedCredentialRefresherEventSource.Log.Unregistered(provider.Name);
+ timer.Stop();
+ _registrations.Remove(provider);
+ timer.Dispose();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private System.Timers.Timer scheduleTimer(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback)
+ {
+ System.Timers.Timer timer = new System.Timers.Timer();
+ timer.Interval = provider.ValidUntil.Value.TotalMilliseconds * (1.0 - (1 / 3.0));
+ timer.Elapsed += (o, e) =>
+ {
+ TimerBasedCredentialRefresherEventSource.Log.TriggeredTimer(provider.Name);
+ try
+ {
+ provider.Refresh();
+ scheduleTimer(provider, callback);
+ callback.Invoke(provider.Password != null);
+ TimerBasedCredentialRefresherEventSource.Log.RefreshedCredentials(provider.Name, true);
+ }
+ catch (Exception)
+ {
+ callback.Invoke(false);
+ TimerBasedCredentialRefresherEventSource.Log.RefreshedCredentials(provider.Name, false);
+ }
+
+ };
+ timer.Enabled = true;
+ timer.AutoReset = false;
+ TimerBasedCredentialRefresherEventSource.Log.ScheduledTimer(provider.Name, timer.Interval);
+ return timer;
+ }
+ }
+
+ class NoOpCredentialsRefresher : ICredentialsRefresher
+ {
+ public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback)
+ {
+ return provider;
+ }
+
+ public bool Unregister(ICredentialsProvider provider)
+ {
+ return false;
+ }
+ }
+}
diff --git a/projects/RabbitMQ.Client/client/api/PlainMechanism.cs b/projects/RabbitMQ.Client/client/api/PlainMechanism.cs
index 69bb501b08..964e624c29 100644
--- a/projects/RabbitMQ.Client/client/api/PlainMechanism.cs
+++ b/projects/RabbitMQ.Client/client/api/PlainMechanism.cs
@@ -37,7 +37,7 @@ public class PlainMechanism : IAuthMechanism
{
public byte[] handleChallenge(byte[] challenge, ConnectionConfig config)
{
- return Encoding.UTF8.GetBytes($"\0{config.UserName}\0{config.Password}");
+ return Encoding.UTF8.GetBytes($"\0{config.CredentialsProvider.UserName}\0{config.CredentialsProvider.Password}");
}
}
}
diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
index f767e12971..625ba1b2d4 100644
--- a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
+++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs
@@ -175,15 +175,12 @@ public override string ToString()
internal IFrameHandler FrameHandler => InnerConnection.FrameHandler;
- internal string Password => _config.Password;
-
///API-side invocation of updating the secret.
public void UpdateSecret(string newSecret, string reason)
{
ThrowIfDisposed();
EnsureIsOpen();
_innerConnection.UpdateSecret(newSecret, reason);
- _config.Password = newSecret;
}
///API-side invocation of connection.close with timeout.
diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
index af3d414370..ea561d6986 100644
--- a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
+++ b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs
@@ -160,10 +160,28 @@ private async ValueTask StartAndTuneAsync()
_channel0.ConnectionTuneOk(channelMax, frameMax, (ushort)Heartbeat.TotalSeconds);
+ MaybeStartCredentialRefresher();
+
// now we can start heartbeat timers
MaybeStartHeartbeatTimers();
}
+ private void MaybeStartCredentialRefresher()
+ {
+ if (_config.CredentialsProvider.ValidUntil != null)
+ {
+ _config.CredentialsRefresher.Register(_config.CredentialsProvider, NotifyCredentialRefreshed);
+ }
+ }
+
+ private void NotifyCredentialRefreshed(bool succesfully)
+ {
+ if (succesfully)
+ {
+ UpdateSecret(_config.CredentialsProvider.Password, "Token refresh");
+ }
+ }
+
private IAuthMechanismFactory GetAuthMechanismFactory(string supportedMechanismNames)
{
// Our list is in order of preference, the server one is not.
diff --git a/projects/TestApplications/CreateChannel/CreateChannel.csproj b/projects/TestApplications/CreateChannel/CreateChannel.csproj
index 01275f734c..9fe7c226d7 100644
--- a/projects/TestApplications/CreateChannel/CreateChannel.csproj
+++ b/projects/TestApplications/CreateChannel/CreateChannel.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/projects/TestApplications/MassPublish/MassPublish.csproj b/projects/TestApplications/MassPublish/MassPublish.csproj
index 01275f734c..9fe7c226d7 100644
--- a/projects/TestApplications/MassPublish/MassPublish.csproj
+++ b/projects/TestApplications/MassPublish/MassPublish.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/projects/TestApplications/OAuth2/OAuth2.csproj b/projects/TestApplications/OAuth2/OAuth2.csproj
new file mode 100644
index 0000000000..65fd6bff4c
--- /dev/null
+++ b/projects/TestApplications/OAuth2/OAuth2.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/TestApplications/OAuth2/Program.cs b/projects/TestApplications/OAuth2/Program.cs
new file mode 100644
index 0000000000..4b71854664
--- /dev/null
+++ b/projects/TestApplications/OAuth2/Program.cs
@@ -0,0 +1,153 @@
+using System.Text;
+using Microsoft.Extensions.Configuration;
+using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using RabbitMQ.Client.OAuth2;
+
+namespace OAuth2
+{
+ public static class Program
+ {
+ private static string _exchange = "test_direct";
+ private static AutoResetEvent? _doneEvent;
+
+ public class OAuth2Options
+ {
+ public string? Name { get; set; }
+ public string? ClientId { get; set; }
+ public string? ClientSecret { get; set; }
+ public string? Scope { get; set; }
+ public string? TokenEndpoint { get; set; }
+ public int TokenExpiresInSeconds { get; set; }
+ }
+
+ public static void Main(string[] args)
+ {
+
+ IConfiguration configuration = new ConfigurationBuilder()
+ .AddEnvironmentVariables()
+ .AddCommandLine(args)
+ .Build();
+
+ _doneEvent = new AutoResetEvent(false);
+
+ OAuth2Options? oauth2Options = configuration.Get();
+ if (oauth2Options == null)
+ {
+ throw new ArgumentException("There are no OAuth2 Options");
+ }
+
+ var connectionFactory = new ConnectionFactory
+ {
+ AutomaticRecoveryEnabled = true,
+ CredentialsProvider = GetCredentialsProvider(oauth2Options),
+ CredentialsRefresher = GetCredentialsRefresher()
+ };
+
+ using (IConnection connection = connectionFactory.CreateConnection())
+ {
+ using (IChannel publisher = declarePublisher(connection))
+ using (IChannel subscriber = declareConsumer(connection))
+ {
+ Publish(publisher);
+ Consume(subscriber);
+
+ if (oauth2Options.TokenExpiresInSeconds > 0)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ Console.WriteLine("Wait until Token expires. Attempt #" + (i + 1));
+ Thread.Sleep((oauth2Options.TokenExpiresInSeconds + 10) * 1000);
+ Console.WriteLine("Resuming ..");
+ Publish(publisher);
+ _doneEvent.Reset();
+ Consume(subscriber);
+ }
+ }
+ }
+ }
+ }
+
+ private static ICredentialsRefresher GetCredentialsRefresher()
+ {
+ return new TimerBasedCredentialRefresher();
+ }
+
+ public static IChannel declarePublisher(IConnection connection)
+ {
+ var publisher = connection.CreateChannel();
+ publisher.ConfirmSelect();
+ publisher.ExchangeDeclare("test_direct", ExchangeType.Direct, true, false);
+ return publisher;
+ }
+
+ public static void Publish(IChannel publisher)
+ {
+ const string message = "Hello World!";
+
+ var body = new ReadOnlyMemory(Encoding.UTF8.GetBytes(message));
+ var properties = new BasicProperties
+ {
+ AppId = "oauth2",
+ };
+
+ publisher.BasicPublish(exchange: _exchange, routingKey: "hello", basicProperties: properties, body: body);
+
+ Console.WriteLine("Sent message");
+ publisher.WaitForConfirmsOrDieAsync().Wait();
+ Console.WriteLine("Confirmed Sent message");
+ }
+
+ public static IChannel declareConsumer(IConnection connection)
+ {
+ var subscriber = connection.CreateChannel();
+ subscriber.QueueDeclare("testqueue", true, false, false);
+ subscriber.QueueBind("testqueue", _exchange, "hello");
+ return subscriber;
+ }
+
+ public static void Consume(IChannel subscriber)
+ {
+ var asyncListener = new AsyncEventingBasicConsumer(subscriber);
+ asyncListener.Received += AsyncListener_Received;
+ string consumerTag = subscriber.BasicConsume("testqueue", true, "testconsumer", asyncListener);
+ _doneEvent?.WaitOne(1);
+ Console.WriteLine("Received message");
+ subscriber.BasicCancel(consumerTag);
+ }
+
+ private static Task AsyncListener_Received(object sender, BasicDeliverEventArgs @event)
+ {
+ _doneEvent?.Set();
+ return Task.CompletedTask;
+ }
+
+ public static OAuth2ClientCredentialsProvider GetCredentialsProvider(OAuth2Options? options)
+ {
+ if (options == null)
+ {
+ throw new ArgumentException("There are no OAuth2 Options");
+ }
+
+ if (options.ClientId == null)
+ {
+ throw new ArgumentNullException("oauth2Options.ClientId is null");
+ }
+
+ if (options.TokenEndpoint == null)
+ {
+ throw new ArgumentException("oauth2Options.TokenEndpoint is null");
+ }
+
+ Console.WriteLine("OAuth2Client ");
+ Console.WriteLine("- ClientId: " + options.ClientId);
+ Console.WriteLine("- ClientSecret: " + options.ClientSecret);
+ Console.WriteLine("- TokenEndpoint: " + options.TokenEndpoint);
+ Console.WriteLine("- Scope: " + options.Scope);
+
+ var tokenEndpointUri = new Uri(options.TokenEndpoint);
+ var oAuth2Client = new OAuth2ClientBuilder(options.ClientId, options.ClientSecret, tokenEndpointUri).Build();
+ return new OAuth2ClientCredentialsProvider(options.Name, oAuth2Client);
+ }
+ }
+}
diff --git a/projects/TestApplications/OAuth2/README.md b/projects/TestApplications/OAuth2/README.md
new file mode 100644
index 0000000000..3392315d03
--- /dev/null
+++ b/projects/TestApplications/OAuth2/README.md
@@ -0,0 +1,33 @@
+# OAuth2 Test Application
+
+Application to test and demonstrate OAuth2 authorization and token refresh
+in a .Net Console application.
+
+There are Authorization servers like UAA which do not return a `refresh_token`
+when using *Client credentials* flow to request a token. This means that the
+.Net client must request a new token before it expires. Whereas there are other
+Authorization servers like Keycloak which does return one. This means that the
+.Net client refreshes the token before it expires.
+
+To test against UAA follow these steps:
+1. Start UAA -> `./start-uaa.sh`
+2. Start RabbitMQ with MODE=uaa -> `MODE=uaa ./start-rabbit.sh`
+3. Start Test with MODE=uaa -> `MODE=uaa ./start-test.sh`
+
+To test against Keycloak follow these steps:
+1. Start Keycloak -> `./start-keycloak.sh`
+2. Start RabbitMQ with MODE=keycloak -> `MODE=keycloak ./start-rabbit.sh`
+3. Start Test with MODE=keycloak -> `MODE=keycloak ./start-test.sh`
+
+## Authorization Servers
+
+Both Authorization servers have the following relevant configuration:
+- An oauth client called `producer`
+- A RSA signing key
+- Access Token lifetime of 1 minute
+
+## Test Application
+
+The Test Application declares an exchange, a queue, binds the queue to the exchange,
+and sends 4 messages and waits for them and in between each message it waits until
+the token has expired to force a token refresh.
diff --git a/projects/TestApplications/OAuth2/common.sh b/projects/TestApplications/OAuth2/common.sh
new file mode 100755
index 0000000000..c6922ba05a
--- /dev/null
+++ b/projects/TestApplications/OAuth2/common.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+readonly docker_name_prefix='rabbitmq-dotnet-client-oauth2'
+
+function docker_stop
+{
+ local -r name="$1"
+ local -r docker_name="$docker_name_prefix-$name"
+
+ set +o errexit
+ if docker stop "$docker_name"
+ then
+ docker rm "$docker_name"
+ fi
+ set -o errexit
+}
diff --git a/projects/TestApplications/OAuth2/enabled_plugins b/projects/TestApplications/OAuth2/enabled_plugins
new file mode 100644
index 0000000000..0bd9a01a1e
--- /dev/null
+++ b/projects/TestApplications/OAuth2/enabled_plugins
@@ -0,0 +1 @@
+[rabbitmq_management,rabbitmq_auth_backend_oauth2].
diff --git a/projects/TestApplications/OAuth2/keycloak/import/test-realm.json b/projects/TestApplications/OAuth2/keycloak/import/test-realm.json
new file mode 100644
index 0000000000..df2b7594f8
--- /dev/null
+++ b/projects/TestApplications/OAuth2/keycloak/import/test-realm.json
@@ -0,0 +1,2488 @@
+{
+ "id" : "test",
+ "realm" : "test",
+ "notBefore" : 0,
+ "defaultSignatureAlgorithm" : "RS256",
+ "revokeRefreshToken" : false,
+ "refreshTokenMaxReuse" : 0,
+ "accessTokenLifespan" : 60,
+ "accessTokenLifespanForImplicitFlow" : 900,
+ "ssoSessionIdleTimeout" : 1800,
+ "ssoSessionMaxLifespan" : 36000,
+ "ssoSessionIdleTimeoutRememberMe" : 0,
+ "ssoSessionMaxLifespanRememberMe" : 0,
+ "offlineSessionIdleTimeout" : 2592000,
+ "offlineSessionMaxLifespanEnabled" : false,
+ "offlineSessionMaxLifespan" : 5184000,
+ "clientSessionIdleTimeout" : 0,
+ "clientSessionMaxLifespan" : 0,
+ "clientOfflineSessionIdleTimeout" : 0,
+ "clientOfflineSessionMaxLifespan" : 0,
+ "accessCodeLifespan" : 60,
+ "accessCodeLifespanUserAction" : 300,
+ "accessCodeLifespanLogin" : 1800,
+ "actionTokenGeneratedByAdminLifespan" : 43200,
+ "actionTokenGeneratedByUserLifespan" : 300,
+ "oauth2DeviceCodeLifespan" : 600,
+ "oauth2DevicePollingInterval" : 5,
+ "enabled" : true,
+ "sslRequired" : "external",
+ "registrationAllowed" : false,
+ "registrationEmailAsUsername" : false,
+ "rememberMe" : false,
+ "verifyEmail" : false,
+ "loginWithEmailAllowed" : true,
+ "duplicateEmailsAllowed" : false,
+ "resetPasswordAllowed" : false,
+ "editUsernameAllowed" : false,
+ "bruteForceProtected" : false,
+ "permanentLockout" : false,
+ "maxFailureWaitSeconds" : 900,
+ "minimumQuickLoginWaitSeconds" : 60,
+ "waitIncrementSeconds" : 60,
+ "quickLoginCheckMilliSeconds" : 1000,
+ "maxDeltaTimeSeconds" : 43200,
+ "failureFactor" : 30,
+ "roles" : {
+ "realm" : [ {
+ "id" : "2b61bc53-60cc-48fc-b89b-ee3e80204895",
+ "name" : "rabbitmq.tag:management",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "c28bf7ca-9fb7-485c-a68b-d5fb4bd844fb",
+ "name" : "rabbitmq.tag:administrator",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "d2b776e4-8c4d-4168-9d52-76aaa115ee70",
+ "name" : "uma_authorization",
+ "description" : "${role_uma_authorization}",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "6faef857-1c9b-4474-ba01-ad1946d243d6",
+ "name" : "rabbitmq-proxy-client-role",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "0a838a26-4908-4750-a1d0-7cc322c698ae",
+ "name" : "producer",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "dd893988-6661-4849-a0f1-1cd1a63b51a5",
+ "name" : "rabbitmq.read:*/*",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "6feb7afe-2fa8-4569-8fb8-e50c2a4302d2",
+ "name" : "offline_access",
+ "description" : "${role_offline-access}",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "af1bc955-6d4d-42e9-b0d4-343e7eb075d0",
+ "name" : "rabbitmq-role",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "77e9131f-1eb3-45a3-9f3b-f74991a99def",
+ "name" : "rabbitmq.configure:*/*",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "97bb2b6b-33ff-404e-b754-351604d9f34c",
+ "name" : "rabbitmq",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "b84ae322-7112-41d1-8a3f-0009447ded47",
+ "name" : "default-roles-test",
+ "description" : "${role_default-roles}",
+ "composite" : true,
+ "composites" : {
+ "realm" : [ "offline_access", "uma_authorization" ],
+ "client" : {
+ "account" : [ "view-profile", "manage-account" ]
+ }
+ },
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "5516969b-be85-490c-9715-9c1186075d60",
+ "name" : "rabbitmq-management",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ }, {
+ "id" : "216cfa85-9b8a-4fc0-bee1-814e2978d82b",
+ "name" : "rabbitmq.write:*/*",
+ "composite" : false,
+ "clientRole" : false,
+ "containerId" : "test",
+ "attributes" : { }
+ } ],
+ "client" : {
+ "realm-management" : [ {
+ "id" : "6721a146-c9e3-4a24-9d26-6dbc7e3aae1f",
+ "name" : "manage-authorization",
+ "description" : "${role_manage-authorization}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "147b7e9a-d884-42b7-a970-245c2b5590b0",
+ "name" : "view-clients",
+ "description" : "${role_view-clients}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "query-clients" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "c25f4711-ee9b-4457-9636-7dacffceb676",
+ "name" : "view-events",
+ "description" : "${role_view-events}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "1ce1e692-4dae-498f-8ac6-ca119eb329ef",
+ "name" : "query-realms",
+ "description" : "${role_query-realms}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "78cd990d-68bd-4e71-9561-5e4412bcbfb7",
+ "name" : "create-client",
+ "description" : "${role_create-client}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "c5bc4413-71cb-43f1-b48b-c9428aed47cd",
+ "name" : "query-clients",
+ "description" : "${role_query-clients}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "21b62a0b-62fd-4a39-8b97-8ce8b89ad9d8",
+ "name" : "view-identity-providers",
+ "description" : "${role_view-identity-providers}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "9df26a88-36e9-4670-8b63-dc4e57ebcce8",
+ "name" : "manage-clients",
+ "description" : "${role_manage-clients}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "02bc109a-d318-4196-aa15-171651685b50",
+ "name" : "view-users",
+ "description" : "${role_view-users}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "query-users", "query-groups" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "97499d4c-fb81-4ee6-bd5e-6eb198424654",
+ "name" : "manage-events",
+ "description" : "${role_manage-events}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "bf64efcd-f8a4-47e6-bc5e-0ff29635b885",
+ "name" : "realm-admin",
+ "description" : "${role_realm-admin}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "realm-management" : [ "manage-authorization", "view-clients", "view-events", "query-realms", "create-client", "view-identity-providers", "query-clients", "view-users", "manage-clients", "manage-events", "view-realm", "manage-realm", "manage-users", "query-users", "query-groups", "manage-identity-providers", "view-authorization", "impersonation" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "fb251ef8-0f7e-4e85-a423-e3bf515dbe5c",
+ "name" : "manage-realm",
+ "description" : "${role_manage-realm}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "09df7745-f99e-4961-add6-eca3e2ab9b44",
+ "name" : "view-realm",
+ "description" : "${role_view-realm}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "45a27cee-8828-4427-90b4-9394b080db18",
+ "name" : "manage-users",
+ "description" : "${role_manage-users}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "7454f27b-cabc-4160-9835-1747659f6f00",
+ "name" : "query-users",
+ "description" : "${role_query-users}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "42d9a084-b4e0-42f5-8c29-9623fb265f79",
+ "name" : "query-groups",
+ "description" : "${role_query-groups}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "893a1a00-5e1f-4dc2-983d-640a3cce58fa",
+ "name" : "manage-identity-providers",
+ "description" : "${role_manage-identity-providers}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "8c9b5f3e-2819-4dbe-81d0-fa8721ff9f1d",
+ "name" : "view-authorization",
+ "description" : "${role_view-authorization}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ }, {
+ "id" : "1e3044c3-fb93-48fa-9b27-eb4d8c6ccad7",
+ "name" : "impersonation",
+ "description" : "${role_impersonation}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "attributes" : { }
+ } ],
+ "mgt_api_client" : [ ],
+ "security-admin-console" : [ ],
+ "admin-cli" : [ ],
+ "producer" : [ ],
+ "account-console" : [ ],
+ "broker" : [ {
+ "id" : "147bafb6-45a8-45ba-b214-7826b1fc4856",
+ "name" : "read-token",
+ "description" : "${role_read-token}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "f32cd0e1-5b78-412a-ba07-6ad2a9aeb007",
+ "attributes" : { }
+ } ],
+ "rabbitmq-client-code" : [ ],
+ "rabbitmq" : [ {
+ "id" : "f5caa7a5-0770-41d8-a3a3-8691470b6d82",
+ "name" : "rabbitmq-role",
+ "description" : "",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "a57c9f6a-8b64-47dc-af53-d6ccc2d4aa60",
+ "attributes" : { }
+ } ],
+ "account" : [ {
+ "id" : "957f712c-e735-402d-9f41-ad9832749f51",
+ "name" : "delete-account",
+ "description" : "${role_delete-account}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "9145b32b-f8ef-4ff0-b50a-be1af192a65a",
+ "name" : "manage-account-links",
+ "description" : "${role_manage-account-links}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "59cac6df-51cd-4a3c-bf77-03bc2b34fe69",
+ "name" : "manage-consent",
+ "description" : "${role_manage-consent}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "account" : [ "view-consent" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "508ce853-78d5-428c-9589-0e310fa7fe40",
+ "name" : "view-profile",
+ "description" : "${role_view-profile}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "061542b2-67d9-4388-aadb-9c936f19d607",
+ "name" : "view-groups",
+ "description" : "${role_view-groups}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "6d326537-afdd-4f72-8973-14b164361a7e",
+ "name" : "view-applications",
+ "description" : "${role_view-applications}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "1fc2ea3e-395a-45bd-ae2f-a9eb674ed4b2",
+ "name" : "manage-account",
+ "description" : "${role_manage-account}",
+ "composite" : true,
+ "composites" : {
+ "client" : {
+ "account" : [ "manage-account-links" ]
+ }
+ },
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ }, {
+ "id" : "95ad82e5-1859-496e-ba95-6b38f8043efd",
+ "name" : "view-consent",
+ "description" : "${role_view-consent}",
+ "composite" : false,
+ "clientRole" : true,
+ "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "attributes" : { }
+ } ],
+ "rabbitmq-proxy-client" : [ {
+ "id" : "ba66d339-cbca-41c1-87fe-38e7b50efd52",
+ "name" : "rabbitmq-proxy-client-role",
+ "composite" : true,
+ "composites" : {
+ "realm" : [ "rabbitmq-role", "rabbitmq-proxy-client-role", "rabbitmq" ]
+ },
+ "clientRole" : true,
+ "containerId" : "c265f3db-ed3a-4898-8800-af044b3c30f5",
+ "attributes" : { }
+ } ]
+ }
+ },
+ "groups" : [ {
+ "id" : "6746dbec-7e2b-4540-ae00-73aa2a93a04e",
+ "name" : "rabbitmq",
+ "path" : "/rabbitmq",
+ "attributes" : { },
+ "realmRoles" : [ "rabbitmq" ],
+ "clientRoles" : { },
+ "subGroups" : [ ]
+ } ],
+ "defaultRole" : {
+ "id" : "b84ae322-7112-41d1-8a3f-0009447ded47",
+ "name" : "default-roles-test",
+ "description" : "${role_default-roles}",
+ "composite" : true,
+ "clientRole" : false,
+ "containerId" : "test"
+ },
+ "requiredCredentials" : [ "password" ],
+ "otpPolicyType" : "totp",
+ "otpPolicyAlgorithm" : "HmacSHA1",
+ "otpPolicyInitialCounter" : 0,
+ "otpPolicyDigits" : 6,
+ "otpPolicyLookAheadWindow" : 1,
+ "otpPolicyPeriod" : 30,
+ "otpPolicyCodeReusable" : false,
+ "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName" ],
+ "webAuthnPolicyRpEntityName" : "keycloak",
+ "webAuthnPolicySignatureAlgorithms" : [ "ES256" ],
+ "webAuthnPolicyRpId" : "",
+ "webAuthnPolicyAttestationConveyancePreference" : "not specified",
+ "webAuthnPolicyAuthenticatorAttachment" : "not specified",
+ "webAuthnPolicyRequireResidentKey" : "not specified",
+ "webAuthnPolicyUserVerificationRequirement" : "not specified",
+ "webAuthnPolicyCreateTimeout" : 0,
+ "webAuthnPolicyAvoidSameAuthenticatorRegister" : false,
+ "webAuthnPolicyAcceptableAaguids" : [ ],
+ "webAuthnPolicyPasswordlessRpEntityName" : "keycloak",
+ "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ],
+ "webAuthnPolicyPasswordlessRpId" : "",
+ "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified",
+ "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified",
+ "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified",
+ "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified",
+ "webAuthnPolicyPasswordlessCreateTimeout" : 0,
+ "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false,
+ "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ],
+ "users" : [ {
+ "id" : "4cf4d6b5-09e5-453f-bf22-c8efdc2dd1dc",
+ "createdTimestamp" : 1651841525973,
+ "username" : "rabbit_admin",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : true,
+ "firstName" : "",
+ "lastName" : "",
+ "email" : "rabbit_admin@rabbit.com",
+ "credentials" : [ {
+ "id" : "deca2be2-28ad-4f98-981f-3ec68bf12ae2",
+ "type" : "password",
+ "createdDate" : 1651841816533,
+ "secretData" : "{\"value\":\"bRuz2IKP4+kG3IKo258mVNqW8Nts6CkZavF3tf4M+/dlJFNPJIallxephOKUiVPtMOdO9Huq9K0uwTBYSZY3fg==\",\"salt\":\"v2qUXLV0n8402Ef8brQg1Q==\",\"additionalParameters\":{}}",
+ "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}"
+ } ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "rabbitmq.tag:administrator", "rabbitmq.configure:*/*", "rabbitmq", "rabbitmq.write:*/*", "rabbitmq.read:*/*" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "15f03347-e2fc-4f8c-9743-f4dfd59f67fe",
+ "createdTimestamp" : 1652084304711,
+ "username" : "service-account-mgt_api_client",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "serviceAccountClientId" : "mgt_api_client",
+ "credentials" : [ ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-test", "rabbitmq-management" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "63ec2047-6689-45c0-981d-f9b127a6bb7f",
+ "createdTimestamp" : 1652084012762,
+ "username" : "service-account-producer",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "serviceAccountClientId" : "producer",
+ "credentials" : [ ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-test", "producer" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ }, {
+ "id" : "7a51406b-d6d8-4c77-9b8a-135a2f07d8d5",
+ "createdTimestamp" : 1677053286393,
+ "username" : "service-account-rabbitmq-proxy-client",
+ "enabled" : true,
+ "totp" : false,
+ "emailVerified" : false,
+ "serviceAccountClientId" : "rabbitmq-proxy-client",
+ "credentials" : [ ],
+ "disableableCredentialTypes" : [ ],
+ "requiredActions" : [ ],
+ "realmRoles" : [ "default-roles-test" ],
+ "notBefore" : 0,
+ "groups" : [ ]
+ } ],
+ "scopeMappings" : [ {
+ "client" : "producer",
+ "roles" : [ "producer" ]
+ }, {
+ "clientScope" : "rabbitmq.read:*/*",
+ "roles" : [ "producer" ]
+ }, {
+ "clientScope" : "offline_access",
+ "roles" : [ "offline_access" ]
+ }, {
+ "clientScope" : "rabbitmq.configure:*/*",
+ "roles" : [ "producer" ]
+ }, {
+ "clientScope" : "rabbitmq.tag:management",
+ "roles" : [ "rabbitmq-management" ]
+ }, {
+ "clientScope" : "rabbitmq.write:*/*",
+ "roles" : [ "producer" ]
+ }, {
+ "clientScope" : "rabbitmq.tag:administrator",
+ "roles" : [ "rabbitmq.tag:administrator" ]
+ } ],
+ "clientScopeMappings" : {
+ "account" : [ {
+ "client" : "account-console",
+ "roles" : [ "manage-account", "view-groups" ]
+ } ]
+ },
+ "clients" : [ {
+ "id" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025",
+ "clientId" : "account",
+ "name" : "${client_account}",
+ "rootUrl" : "${authBaseUrl}",
+ "baseUrl" : "/realms/test/account/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/realms/test/account/*" ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "80023652-2709-4646-9367-b6114aa73bae",
+ "clientId" : "account-console",
+ "name" : "${client_account-console}",
+ "rootUrl" : "${authBaseUrl}",
+ "baseUrl" : "/realms/test/account/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/realms/test/account/*" ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+",
+ "pkce.code.challenge.method" : "S256"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "protocolMappers" : [ {
+ "id" : "ebcf72c5-f58a-48cb-a6fb-db44e8735d7e",
+ "name" : "audience resolve",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-resolve-mapper",
+ "consentRequired" : false,
+ "config" : { }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "e5484264-82ff-46df-b38e-d5456439f413",
+ "clientId" : "admin-cli",
+ "name" : "${client_admin-cli}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : false,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "f32cd0e1-5b78-412a-ba07-6ad2a9aeb007",
+ "clientId" : "broker",
+ "name" : "${client_broker}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : true,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "c5be3c24-0c88-4672-a77a-79002fcc9a9d",
+ "clientId" : "mgt_api_client",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "LWOuYqJ8gjKg3D2U8CJZDuID3KiRZVDa",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : false,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : true,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "saml.force.post.binding" : "false",
+ "saml.multivalued.roles" : "false",
+ "frontchannel.logout.session.required" : "false",
+ "post.logout.redirect.uris" : "+",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "backchannel.logout.revoke.offline.tokens" : "false",
+ "saml.server.signature.keyinfo.ext" : "false",
+ "use.refresh.tokens" : "true",
+ "oidc.ciba.grant.enabled" : "false",
+ "backchannel.logout.session.required" : "true",
+ "client_credentials.use_refresh_token" : "false",
+ "require.pushed.authorization.requests" : "false",
+ "saml.client.signature" : "false",
+ "saml.allow.ecp.flow" : "false",
+ "id.token.as.detached.signature" : "false",
+ "saml.assertion.signature" : "false",
+ "client.secret.creation.time" : "1652084304",
+ "saml.encrypt" : "false",
+ "saml.server.signature" : "false",
+ "exclude.session.state.from.auth.response" : "false",
+ "saml.artifact.binding" : "false",
+ "saml_force_name_id_format" : "false",
+ "acr.loa.map" : "{}",
+ "tls.client.certificate.bound.access.tokens" : "false",
+ "saml.authnstatement" : "false",
+ "display.on.consent.screen" : "false",
+ "token.response.type.bearer.lower-case" : "false",
+ "saml.onetimeuse.condition" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "33fd8faf-3ea6-4669-beea-45b9655cf6ab",
+ "name" : "Client IP Address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientAddress",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientAddress",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "aae7e2aa-72e7-4d29-ae68-a66b846d62ab",
+ "name" : "aud",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "false",
+ "access.token.claim" : "true",
+ "included.custom.audience" : "rabbitmq",
+ "userinfo.token.claim" : "false"
+ }
+ }, {
+ "id" : "f7e826de-e651-4080-8e97-feba46b8a0a2",
+ "name" : "Client ID",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientId",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientId",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "545a1d71-5dc8-491c-bf7b-1c672d50e606",
+ "name" : "Client Host",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientHost",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientHost",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "rabbitmq.tag:administrator", "rabbitmq.tag:management", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "3e96bddd-95f9-4277-b3ad-f8f6f5d5bb59",
+ "clientId" : "producer",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : false,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : true,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "saml.force.post.binding" : "false",
+ "saml.multivalued.roles" : "false",
+ "frontchannel.logout.session.required" : "false",
+ "post.logout.redirect.uris" : "+",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "backchannel.logout.revoke.offline.tokens" : "false",
+ "saml.server.signature.keyinfo.ext" : "false",
+ "use.refresh.tokens" : "true",
+ "oidc.ciba.grant.enabled" : "false",
+ "backchannel.logout.session.required" : "false",
+ "client_credentials.use_refresh_token" : "true",
+ "require.pushed.authorization.requests" : "false",
+ "saml.client.signature" : "false",
+ "saml.allow.ecp.flow" : "false",
+ "id.token.as.detached.signature" : "false",
+ "saml.assertion.signature" : "false",
+ "client.secret.creation.time" : "1652081901",
+ "saml.encrypt" : "false",
+ "saml.server.signature" : "false",
+ "exclude.session.state.from.auth.response" : "false",
+ "saml.artifact.binding" : "false",
+ "saml_force_name_id_format" : "false",
+ "acr.loa.map" : "{}",
+ "tls.client.certificate.bound.access.tokens" : "false",
+ "saml.authnstatement" : "false",
+ "display.on.consent.screen" : "false",
+ "token.response.type.bearer.lower-case" : "false",
+ "saml.onetimeuse.condition" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "72928dd9-10c9-4049-bfa7-4cc05e650f46",
+ "name" : "realm roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute" : "foo",
+ "access.token.claim" : "true",
+ "claim.name" : "realm_access.roles",
+ "jsonType.label" : "String",
+ "multivalued" : "true"
+ }
+ }, {
+ "id" : "4c3b3c28-795f-4056-a854-5cf119b36266",
+ "name" : "Client ID",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientId",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientId",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "56b7571c-3226-4c92-8615-c99b265a42fc",
+ "name" : "aud",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "false",
+ "access.token.claim" : "true",
+ "included.custom.audience" : "rabbitmq",
+ "userinfo.token.claim" : "false"
+ }
+ }, {
+ "id" : "4ca73107-b26b-46ee-985b-d2dcc099f21c",
+ "name" : "Client Host",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientHost",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientHost",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "26e5243a-3127-4528-9a54-8af324ac2392",
+ "name" : "client roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-client-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute" : "foo",
+ "access.token.claim" : "true",
+ "claim.name" : "resource_access.${client_id}.roles",
+ "jsonType.label" : "String",
+ "multivalued" : "true"
+ }
+ }, {
+ "id" : "d52cc6cb-08a1-4c2b-bf06-61f234a419d1",
+ "name" : "Client IP Address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientAddress",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientAddress",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "rabbitmq.read:*/*", "rabbitmq.write:*/*", "roles", "rabbitmq.configure:*/*" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "a57c9f6a-8b64-47dc-af53-d6ccc2d4aa60",
+ "clientId" : "rabbitmq",
+ "name" : "",
+ "description" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : true,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "oidc.ciba.grant.enabled" : "false",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "backchannel.logout.session.required" : "true",
+ "backchannel.logout.revoke.offline.tokens" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "e64b05d1-0d1c-4294-85f9-52ae098ecf1f",
+ "clientId" : "rabbitmq-client-code",
+ "name" : "",
+ "description" : "",
+ "rootUrl" : "http://localhost:15672/",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "http://localhost:15672/*" ],
+ "webOrigins" : [ "" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : true,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "saml.force.post.binding" : "false",
+ "saml.multivalued.roles" : "false",
+ "frontchannel.logout.session.required" : "false",
+ "post.logout.redirect.uris" : "+",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "backchannel.logout.revoke.offline.tokens" : "false",
+ "saml.server.signature.keyinfo.ext" : "false",
+ "use.refresh.tokens" : "true",
+ "oidc.ciba.grant.enabled" : "false",
+ "backchannel.logout.session.required" : "false",
+ "client_credentials.use_refresh_token" : "false",
+ "require.pushed.authorization.requests" : "false",
+ "saml.client.signature" : "false",
+ "saml.allow.ecp.flow" : "false",
+ "id.token.as.detached.signature" : "false",
+ "saml.assertion.signature" : "false",
+ "client.secret.creation.time" : "1652171962",
+ "saml.encrypt" : "false",
+ "saml.server.signature" : "false",
+ "exclude.session.state.from.auth.response" : "false",
+ "tls-client-certificate-bound-access-tokens" : "false",
+ "saml.artifact.binding" : "false",
+ "saml_force_name_id_format" : "false",
+ "acr.loa.map" : "{}",
+ "tls.client.certificate.bound.access.tokens" : "false",
+ "saml.authnstatement" : "false",
+ "display.on.consent.screen" : "false",
+ "token.response.type.bearer.lower-case" : "false",
+ "saml.onetimeuse.condition" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "e6905c3e-7ace-4b4f-9244-0f20a86da8ef",
+ "name" : "aud",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "false",
+ "access.token.claim" : "true",
+ "included.custom.audience" : "rabbitmq",
+ "userinfo.token.claim" : "false"
+ }
+ }, {
+ "id" : "548a2e70-5a2b-4959-8c72-97f6455ce478",
+ "name" : "realm roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute" : "foo",
+ "access.token.claim" : "true",
+ "claim.name" : "extra_scope",
+ "jsonType.label" : "String",
+ "multivalued" : "true"
+ }
+ }, {
+ "id" : "923edb6d-2188-4f23-a547-7e372d9cb5eb",
+ "name" : "username",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "username",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "user_name",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "rabbitmq.tag:administrator", "profile", "roles", "rabbitmq.tag:management", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "c265f3db-ed3a-4898-8800-af044b3c30f5",
+ "clientId" : "rabbitmq-proxy-client",
+ "name" : "",
+ "description" : "",
+ "rootUrl" : "",
+ "adminUrl" : "",
+ "baseUrl" : "",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "secret" : "nt6pmZMeyrgzYgkg2MLgZQZxLveRMW5M",
+ "redirectUris" : [ "http://0.0.0.0:4180/*" ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : true,
+ "serviceAccountsEnabled" : true,
+ "publicClient" : false,
+ "frontchannelLogout" : true,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "client.secret.creation.time" : "1677053168",
+ "post.logout.redirect.uris" : "+",
+ "oauth2.device.authorization.grant.enabled" : "false",
+ "backchannel.logout.revoke.offline.tokens" : "false",
+ "use.refresh.tokens" : "true",
+ "tls-client-certificate-bound-access-tokens" : "false",
+ "oidc.ciba.grant.enabled" : "false",
+ "backchannel.logout.session.required" : "true",
+ "client_credentials.use_refresh_token" : "false",
+ "acr.loa.map" : "{}",
+ "require.pushed.authorization.requests" : "false",
+ "display.on.consent.screen" : "false",
+ "token.response.type.bearer.lower-case" : "false"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : true,
+ "nodeReRegistrationTimeout" : -1,
+ "protocolMappers" : [ {
+ "id" : "e1c2389a-c5ca-4a81-a5c2-67f919f2368d",
+ "name" : "Client ID",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientId",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientId",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "54b12841-4524-4b8a-8dc0-bb6f9044e11d",
+ "name" : "Client IP Address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientAddress",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientAddress",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "a5c803da-af15-4fc8-ad7f-a4a900f0703b",
+ "name" : "Client Host",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.session.note" : "clientHost",
+ "userinfo.token.claim" : "true",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "clientHost",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "958d4a83-d5b3-4cca-af3e-fde9f9328eec",
+ "name" : "aud",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-audience-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "included.client.audience" : "rabbitmq-proxy-client",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "included.custom.audience" : "rabbitmq"
+ }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "rabbitmq.tag:administrator", "profile", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a",
+ "clientId" : "realm-management",
+ "name" : "${client_realm-management}",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ ],
+ "webOrigins" : [ ],
+ "notBefore" : 0,
+ "bearerOnly" : true,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : false,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ }, {
+ "id" : "16f67c5c-f86b-4334-93f4-fd26356cbb24",
+ "clientId" : "security-admin-console",
+ "name" : "${client_security-admin-console}",
+ "rootUrl" : "${authAdminUrl}",
+ "baseUrl" : "/admin/test/console/",
+ "surrogateAuthRequired" : false,
+ "enabled" : true,
+ "alwaysDisplayInConsole" : false,
+ "clientAuthenticatorType" : "client-secret",
+ "redirectUris" : [ "/admin/test/console/*" ],
+ "webOrigins" : [ "+" ],
+ "notBefore" : 0,
+ "bearerOnly" : false,
+ "consentRequired" : false,
+ "standardFlowEnabled" : true,
+ "implicitFlowEnabled" : false,
+ "directAccessGrantsEnabled" : false,
+ "serviceAccountsEnabled" : false,
+ "publicClient" : true,
+ "frontchannelLogout" : false,
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "post.logout.redirect.uris" : "+",
+ "pkce.code.challenge.method" : "S256"
+ },
+ "authenticationFlowBindingOverrides" : { },
+ "fullScopeAllowed" : false,
+ "nodeReRegistrationTimeout" : 0,
+ "protocolMappers" : [ {
+ "id" : "26e7deed-9c26-4a19-88fa-845bec2e5909",
+ "name" : "locale",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "locale",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "locale",
+ "jsonType.label" : "String"
+ }
+ } ],
+ "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ],
+ "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ]
+ } ],
+ "clientScopes" : [ {
+ "id" : "ec4e76a3-8597-41d4-aa7c-e4e1fee6a01a",
+ "name" : "profile",
+ "description" : "OpenID Connect built-in scope: profile",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${profileScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "db04690a-de25-4627-8e0c-78a018e86ce8",
+ "name" : "family name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "lastName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "family_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "e5c72df5-7fa9-43c6-8f13-c7c2d73fe89a",
+ "name" : "profile",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "profile",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "profile",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "5856da2a-7aa0-446c-be48-22112783e322",
+ "name" : "nickname",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "nickname",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "nickname",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "5a7a208b-70eb-4f8f-b8ff-2115a615d696",
+ "name" : "gender",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "gender",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "gender",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "b1cad309-90cd-4fed-8e62-c05dc4649b99",
+ "name" : "birthdate",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "birthdate",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "birthdate",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "b3a08e61-6e08-4aa3-aa71-212bc13bff5d",
+ "name" : "picture",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "picture",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "picture",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "bc59bb88-2cae-4c60-b09f-3c18fced603f",
+ "name" : "website",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "website",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "website",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "da32f964-8b0a-4cef-babc-8b90f31b20a7",
+ "name" : "given name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "firstName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "given_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "eb6b8e8c-1e03-497a-80b4-3e9c26a86d9a",
+ "name" : "username",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "username",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "preferred_username",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "334e47b2-5f74-4668-b04e-9ab55513c146",
+ "name" : "updated at",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "updatedAt",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "updated_at",
+ "jsonType.label" : "long"
+ }
+ }, {
+ "id" : "7dad1c25-6b18-4571-8d92-bfd698c5b94b",
+ "name" : "full name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-full-name-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "userinfo.token.claim" : "true"
+ }
+ }, {
+ "id" : "bf52c928-4d33-4c14-8e61-969b17bed2a5",
+ "name" : "zoneinfo",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "zoneinfo",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "zoneinfo",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "3a03eb21-7c20-4150-87f2-ca94c9df601c",
+ "name" : "middle name",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "middleName",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "middle_name",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "431c9682-e4ba-4348-9d07-f8d5415ca98b",
+ "name" : "locale",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "locale",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "locale",
+ "jsonType.label" : "String"
+ }
+ } ]
+ }, {
+ "id" : "b2ced9e2-289f-44b0-8567-5218a2eee3e6",
+ "name" : "rabbitmq.read:*/*",
+ "description" : "read all",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "46f5e514-9283-4f03-b2af-a7da506f0cbc",
+ "name" : "offline_access",
+ "description" : "OpenID Connect built-in scope: offline_access",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "consent.screen.text" : "${offlineAccessScopeConsentText}",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "a4ffacea-34a8-4eb2-961f-af78e50b1140",
+ "name" : "address",
+ "description" : "OpenID Connect built-in scope: address",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${addressScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "791b8544-4659-4d61-8fb8-8a18a687648d",
+ "name" : "address",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-address-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute.formatted" : "formatted",
+ "user.attribute.country" : "country",
+ "user.attribute.postal_code" : "postal_code",
+ "userinfo.token.claim" : "true",
+ "user.attribute.street" : "street",
+ "id.token.claim" : "true",
+ "user.attribute.region" : "region",
+ "access.token.claim" : "true",
+ "user.attribute.locality" : "locality"
+ }
+ } ]
+ }, {
+ "id" : "7a2981c1-d606-43f6-acbf-76a8124e59b7",
+ "name" : "microprofile-jwt",
+ "description" : "Microprofile - JWT built-in scope",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "false"
+ },
+ "protocolMappers" : [ {
+ "id" : "63a285df-f6d0-4e06-9f16-d4a578fce8bf",
+ "name" : "upn",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "username",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "upn",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "1665df09-6855-420b-a649-0f0afe054b51",
+ "name" : "groups",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "multivalued" : "true",
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "foo",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "groups",
+ "jsonType.label" : "String"
+ }
+ } ]
+ }, {
+ "id" : "b9c5af5d-59f3-445b-b899-c6574bc6191b",
+ "name" : "phone",
+ "description" : "OpenID Connect built-in scope: phone",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${phoneScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "e945c389-e953-431b-b3b4-882a50a8054e",
+ "name" : "phone number",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "phoneNumber",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "phone_number",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "52b07524-1521-48d6-be23-779f8e1f8a67",
+ "name" : "phone number verified",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-attribute-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "phoneNumberVerified",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "phone_number_verified",
+ "jsonType.label" : "boolean"
+ }
+ } ]
+ }, {
+ "id" : "4dfc0d4d-654d-4e1c-8b58-64a0e1126a19",
+ "name" : "rabbitmq.configure:*/*",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "93d154c5-e9fe-49ff-bca8-bc55a141a31e",
+ "name" : "rabbitmq.tag:management",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "cc214fa3-0a7f-4390-9c4a-8ae14512e4a4",
+ "name" : "web-origins",
+ "description" : "OpenID Connect scope for add allowed web origins to the access token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "false",
+ "display.on.consent.screen" : "false",
+ "consent.screen.text" : ""
+ },
+ "protocolMappers" : [ {
+ "id" : "e2ac8ddb-9c19-4088-bc72-c4176e0fac3f",
+ "name" : "allowed web origins",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-allowed-origins-mapper",
+ "consentRequired" : false,
+ "config" : { }
+ } ]
+ }, {
+ "id" : "53b1a2b2-085e-4e36-bb81-c88e8d846439",
+ "name" : "email",
+ "description" : "OpenID Connect built-in scope: email",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${emailScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "e1a850d9-6372-4521-8d0d-acee25245c90",
+ "name" : "email",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "email",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "email",
+ "jsonType.label" : "String"
+ }
+ }, {
+ "id" : "43a7220e-d94d-43e6-a5e7-1a12dbbb4460",
+ "name" : "email verified",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-property-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "userinfo.token.claim" : "true",
+ "user.attribute" : "emailVerified",
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "claim.name" : "email_verified",
+ "jsonType.label" : "boolean"
+ }
+ } ]
+ }, {
+ "id" : "2b745afc-cb92-4ac3-b314-8ef1d638b4b1",
+ "name" : "roles",
+ "description" : "OpenID Connect scope for add user roles to the access token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "false",
+ "display.on.consent.screen" : "true",
+ "consent.screen.text" : "${rolesScopeConsentText}"
+ },
+ "protocolMappers" : [ {
+ "id" : "2cac7c2c-7c9f-44e6-a76e-b8d3fad627ea",
+ "name" : "realm roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-realm-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute" : "foo",
+ "access.token.claim" : "true",
+ "claim.name" : "realm_access.roles",
+ "jsonType.label" : "String",
+ "multivalued" : "true"
+ }
+ }, {
+ "id" : "91c4a9bd-a9b9-402b-9eb6-762362d18c6b",
+ "name" : "client roles",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-usermodel-client-role-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "user.attribute" : "foo",
+ "access.token.claim" : "true",
+ "claim.name" : "resource_access.${client_id}.roles",
+ "jsonType.label" : "String",
+ "multivalued" : "true"
+ }
+ } ]
+ }, {
+ "id" : "8b53c714-89cf-4cfd-ac76-1b45bd841b58",
+ "name" : "acr",
+ "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "false",
+ "display.on.consent.screen" : "false"
+ },
+ "protocolMappers" : [ {
+ "id" : "6381b445-4f37-434e-b982-c34a6048913b",
+ "name" : "acr loa level",
+ "protocol" : "openid-connect",
+ "protocolMapper" : "oidc-acr-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "id.token.claim" : "true",
+ "access.token.claim" : "true",
+ "userinfo.token.claim" : "true"
+ }
+ } ]
+ }, {
+ "id" : "3331893c-c67c-4146-b00a-b4862200628c",
+ "name" : "role_list",
+ "description" : "SAML role list",
+ "protocol" : "saml",
+ "attributes" : {
+ "consent.screen.text" : "${samlRoleListScopeConsentText}",
+ "display.on.consent.screen" : "true"
+ },
+ "protocolMappers" : [ {
+ "id" : "863078ec-d37c-46fc-a70e-3fe6340fbeec",
+ "name" : "role list",
+ "protocol" : "saml",
+ "protocolMapper" : "saml-role-list-mapper",
+ "consentRequired" : false,
+ "config" : {
+ "single" : "false",
+ "attribute.nameformat" : "Basic",
+ "attribute.name" : "Role"
+ }
+ } ]
+ }, {
+ "id" : "2010b133-4bfe-4f5f-8d1a-33b2a7ad2e60",
+ "name" : "rabbitmq.write:*/*",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true"
+ }
+ }, {
+ "id" : "f6e6dd62-22bf-4421-910e-e6070908764c",
+ "name" : "rabbitmq.tag:administrator",
+ "protocol" : "openid-connect",
+ "attributes" : {
+ "include.in.token.scope" : "true",
+ "display.on.consent.screen" : "true"
+ }
+ } ],
+ "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr" ],
+ "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ],
+ "browserSecurityHeaders" : {
+ "contentSecurityPolicyReportOnly" : "",
+ "xContentTypeOptions" : "nosniff",
+ "xRobotsTag" : "none",
+ "xFrameOptions" : "SAMEORIGIN",
+ "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
+ "xXSSProtection" : "1; mode=block",
+ "strictTransportSecurity" : "max-age=31536000; includeSubDomains"
+ },
+ "smtpServer" : { },
+ "eventsEnabled" : false,
+ "eventsListeners" : [ "jboss-logging" ],
+ "enabledEventTypes" : [ ],
+ "adminEventsEnabled" : false,
+ "adminEventsDetailsEnabled" : false,
+ "identityProviders" : [ ],
+ "identityProviderMappers" : [ ],
+ "components" : {
+ "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ {
+ "id" : "72a90aea-e732-467d-ade9-34e73c993209",
+ "name" : "Allowed Client Scopes",
+ "providerId" : "allowed-client-templates",
+ "subType" : "authenticated",
+ "subComponents" : { },
+ "config" : {
+ "allow-default-scopes" : [ "true" ]
+ }
+ }, {
+ "id" : "5ed9083b-fdb5-4fc5-97b5-0b31189f8ad2",
+ "name" : "Max Clients Limit",
+ "providerId" : "max-clients",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "max-clients" : [ "200" ]
+ }
+ }, {
+ "id" : "4f81b0b9-6a42-4128-b7a1-4a814acf5875",
+ "name" : "Full Scope Disabled",
+ "providerId" : "scope",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : { }
+ }, {
+ "id" : "2c06a7de-50eb-439f-b592-6bbda10d6af3",
+ "name" : "Allowed Protocol Mapper Types",
+ "providerId" : "allowed-protocol-mappers",
+ "subType" : "authenticated",
+ "subComponents" : { },
+ "config" : {
+ "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-address-mapper" ]
+ }
+ }, {
+ "id" : "693f0625-c453-40c0-b38e-80b7b7deaefa",
+ "name" : "Consent Required",
+ "providerId" : "consent-required",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : { }
+ }, {
+ "id" : "7c971805-14f2-4eb0-b2af-90c2db4c2e41",
+ "name" : "Allowed Client Scopes",
+ "providerId" : "allowed-client-templates",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "allow-default-scopes" : [ "true" ]
+ }
+ }, {
+ "id" : "e4376815-05e5-4675-9b11-6e18d5712849",
+ "name" : "Allowed Protocol Mapper Types",
+ "providerId" : "allowed-protocol-mappers",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-full-name-mapper" ]
+ }
+ }, {
+ "id" : "bbadf932-a286-4841-be1b-ed845e2131cb",
+ "name" : "Trusted Hosts",
+ "providerId" : "trusted-hosts",
+ "subType" : "anonymous",
+ "subComponents" : { },
+ "config" : {
+ "host-sending-registration-request-must-match" : [ "true" ],
+ "client-uris-must-match" : [ "true" ]
+ }
+ } ],
+ "org.keycloak.keys.KeyProvider" : [ {
+ "id" : "307ede55-7647-498c-b1ba-4be80fb609cc",
+ "name" : "rsa-generated",
+ "providerId" : "rsa-generated",
+ "subComponents" : { },
+ "config" : {
+ "privateKey" : [ "MIIEogIBAAKCAQEAx5RCXJqU+e2646hYCduHBANJvpYN3Uv1gq15LjTlIXYqBCfm4SLUX1y1lOKDoOVl5j/flAgdgF4I9P3G4drc36NuaaocQguu3xWOsG9UZheiDD4wJANC7F6FMdWqSiBySA+EXyQ2zkoUBkNxKecqWhqVmaY9IVyxbQhdXsKQH0hBnvd3NQaem0RXuUeadUK5TGI9VqTe96sN7lLdE+T94n5cubqtBbc14kZ6YZsi1Pa+2xe/ZsDlXgMGAqTxgSy5l6cnxzxE+ndAp+pIR83BxfRFijo0LNKxyiZQ0X+QZ4Y3sTGadLCZzr6R/N/1QQnhOTXmTsVyNdmhQMr6A7lBFwIDAQABAoIBAAP7tYdbnnWOhRheF8O6mes+lY40OHqeNXwyMiT18UzFqvkCQt1jcJGmrAkYrD/d1DbQN4ogz1Xsiok5N2ryj033WRDK0F2RFiBlsb9buXeAKT/NTfCqD//fsxDXjtqD40QE60Nq2Z0sZVHqrquDbZj2xt2WL8omq3Pdot9tSqsVIQMbRIfH+I9+9kQ8Ob7t423I06AFiXJg5h3qjLx1jP6qQWsC4ippY6QmUve/d3PWqSd4GQ4sb2KQKvfT1VU4HPvIQdf+OurXaF/lPR+6XDU3RmA1qY61JS0O0ul+jTUGUHRxtgI51IU16+jcKiAzjWZ53HI9jLODP6gzyn+KC2ECgYEA/2SN4sNHM8aL/i7hdcbq+c+UFKK5AyWjewbMWGC2ZCzEzW/zqn2lmQHZK7hDAuAhvnEh6rvxxFKQw2PXb9N9IGIaAEJmGrJYg5QaLspNs5/4a3+GZh8lfZgwaEBetazrIFSOrVFhYb/pRz6m2x7oKNIpVXXUXdNr0DD4mDAwLqUCgYEAyA272k5rlTXj2IX/MvAtAuD2l7R6DfDirolzNdLVEXKLWpZwqdAuLWhDNGvNtmeAQcIuGFUvHhv5ZdnAwOKjbJsVDmr8vUCegCZJPuEGuOXjqZq+a1a84lhSSyWWiiz/yuIVh2Bnu8TD2Xb3igNFa0ipWga9nfZm7usifVumQAsCgYA/z+kfyrkkt6xM83vECNK3XmVajpn5rlLdr4IpZujLuN/nkNxqMgDJbUvM/7pGoqfrxKq70wAClLq1B2JR/57ZE4n5nJ2UeBFjtwKfxE6L3iGdAn0bURb1/avCsKq5bB5Hsrj/l0DkwqXP9liMkXlikbhgMRPB6cybdVD3/bpcPQKBgFxZIqeC3dzSNKymJokwH8cdbBfZwyIeWbXyU1G8UoksVDHsEnQBWt+xKpzPSvXxz14LslWfNRH0HeurWnRv6rre2BiAwMzoQIKtqdAx9nVyAecwPMi2EJl35f00i8qbPTU9qmyEzz35deM0LM7z9Z6xuyOIyw1ZSmjt+Ezf+t3DAoGAB7rT8E7lc036+L1RzKF+T8XwXReHcDkXhKt5V/RQBzRL5GY2mqQQ1rb037KlGXRHAwPkQqmYpMhY9ccRF5UqA05IT/KApvc36m7DAXPaNy1CwZHrr0l3rR6fhpUvtgrt1uyCXvaLJxUAd/5MTw2ffqOsMSxiuRCrpUC+dxCXtG8=" ],
+ "keyUse" : [ "SIG" ],
+ "certificate" : [ "MIIClzCCAX8CBgGAmWyoEzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUwNjEyNDkzNloXDTMyMDUwNjEyNTExNlowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeUQlyalPntuuOoWAnbhwQDSb6WDd1L9YKteS405SF2KgQn5uEi1F9ctZTig6DlZeY/35QIHYBeCPT9xuHa3N+jbmmqHEILrt8VjrBvVGYXogw+MCQDQuxehTHVqkogckgPhF8kNs5KFAZDcSnnKloalZmmPSFcsW0IXV7CkB9IQZ73dzUGnptEV7lHmnVCuUxiPVak3verDe5S3RPk/eJ+XLm6rQW3NeJGemGbItT2vtsXv2bA5V4DBgKk8YEsuZenJ8c8RPp3QKfqSEfNwcX0RYo6NCzSscomUNF/kGeGN7ExmnSwmc6+kfzf9UEJ4Tk15k7FcjXZoUDK+gO5QRcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAp2oy7aEJWJ1/BBUlFArzHqXZ/aYzuRUdyCTQn6+tjRsB9IrPwRI4p9sN4mCl0EUDvureNxCiY9XBHWmEteb5n952XWcSWi6tzAj5BQL4LHIFFzMLHr1+HYpmMwYHFgmR9MhoKFINEEGpOvCRokurEN2uU/tNcBX5HGnEWRc5ZNPRwQcJggnHHWxAmuNKIf73pPAECcffrTJO6cePt++TaU2j2qA6S5A/p/0Za10EtKcNeL1EIvwuFxewBjJQjXEqvmN4VlcVadj+pQ7AwtzujdtCuHvO/9zxRo5xq9KFl/VVMk4fUvwKA+vjcMwjDn2qXZwKmslX6YkV18gbmnSCOg==" ],
+ "priority" : [ "100" ]
+ }
+ }, {
+ "id" : "1ae80491-123b-4d2d-8018-7ceb6971d07d",
+ "name" : "aes-generated",
+ "providerId" : "aes-generated",
+ "subComponents" : { },
+ "config" : {
+ "kid" : [ "e43bf5a9-a6eb-46e6-a529-2174e96536fd" ],
+ "secret" : [ "2cQajcd-396pH2TSx6TC-Q" ],
+ "priority" : [ "100" ]
+ }
+ }, {
+ "id" : "b7b404ce-f1db-4fba-9037-d43bbd5fa584",
+ "name" : "hmac-generated",
+ "providerId" : "hmac-generated",
+ "subComponents" : { },
+ "config" : {
+ "kid" : [ "2a9eb5b7-3df3-4fe8-980b-93808456c392" ],
+ "secret" : [ "ghLjXQyrSvgpx1wi2-YYgGSOUH2-FteA8BN_FujK8wywdQ-LiMd-WDfnw-F6GexWHqNrv95VRjHzvlojRbDluQ" ],
+ "priority" : [ "100" ],
+ "algorithm" : [ "HS256" ]
+ }
+ }, {
+ "id" : "f7ed445f-b2bd-4cbd-9985-eba8b36ed733",
+ "name" : "rsa-enc-generated",
+ "providerId" : "rsa-enc-generated",
+ "subComponents" : { },
+ "config" : {
+ "privateKey" : [ "MIIEowIBAAKCAQEAjn/EV027lKv/NvYuCxCqUrAsVKTHpKLyFZCeJfrMcGn9XDTjMMYVx5/2wguPJFpX+nUzFSCjGuejRWamVFOoHiaXB+mrdPoAZbBNrgdeqwb2+7+G0iqsmU+Lfi/pxsubTTLZIZB1PBfV/4DmWz3vRU+uapiHo5pn+h0mbeOCLltuZmygiKlkzpTLfZxdmSXMqCwZv9J6Zdvrpio88Ca2F1dD4w3bPWvcbLmftAkpByucyHlo0v3jLPbq9LQF7fRD3WqHDGgQjLFJJKvN16tTnVkLWzFE4tumsQirCp8MoAuC3fKa5X5AKjZRarcrml9bmnFId+VGs764FY8THVuqtQIDAQABAoIBAC2FBOw6iYxRcSJWe5jPRwlI+7BCPwAJiTl4S/yn4/yY7vUwc86VyElPgRy1YpFjRq1cGOhL1651hkNaKhHP82lGPIKrkzi3z76sFfRcqFIL1IPQx7mFJkOHFHFHzu7RBZgggRnmsnxyxMpsm6lT3IYQkQ++D74kScis3STHQ0OZOVGkx9Sbznx1+i2rc7QUkWy8b7G8/ByVJsAu2SBLfbQ4wFAhJKtr+uDP6Tt2Cyn4GpzE68B5cA7htJI49uVvbGTBLInH5PXejXKyLfEJjbH2brx9hVStmaaV/sD1hroZ0sjhM54MamctPq2o0H33S2WFZJaUXwDWHzOSyUmY5SECgYEAxTJmhuCYz/P2AZP4DGDNwFHLar1K719m+RxUu0ndIDKeGhBtX3oSQXaKQS+JM+dmbF3M7ub18OgCHF+raPXAE6en1tdh5ZQJbRQEX9dnaXVpjCPFciZMgP0eJ1CpMuKo7qb3IXv7YtcN6pFjMNchz3MyVqbNBFN8ocQWoe4VJu0CgYEAuP3Z45ce6XI+7274yTXMEHqmCZU8krKanmppgIj39iJGbkEBo9QSB32XhG7dU0tWJhj4QtQcMKFCMcqckw9Jetyb68J2vreCVti9CqSrSLL4VFhMzVdoRPflzXB29gdJTe4TxBAiSryVICVblW3giM4UWhzNAuJOC8f5r1xrkekCgYBmKyXJreYeoBSOXr6+kw0nHnnZFLgVa4Vrfc08uBlUTEVz1Z0FQbbhqewZt+pLNRHxBWxfPtSf+2TUlJC3sdPRmySvgCodi2SS2jMmAPF4RzfnPsVWzhcHIZ2U2wq+7YZ/F4ylEZp+bFOue6M7s8q1s8aZ9JP2MNc67OCZB0R4RQKBgQC3YfZIVfuvwbA/3ItFs03KnDrSTx2P8vuxxJ03bRAZ8BpPm6OLi2QgBtFX2CsRMiKBe8lHPkt/rawX/dk/My1NXTo3+TuLjhDoFM05qsmdNMVVn37rJBXaIMCu6ikTdV+moDb56mCEI/PUvRPPyu+FznyAZAKbNEnYBfIvc3ezWQKBgEIw8dPvzmmbGvcb1K56VlyKTcejY0tPk8spRd+BKpi+VXlhV+1xLVmyB6lzveFm+0Da2arFGxFKgi/PfT5mXfheGyX2dxMbK5cMkYg7R+pHBbm/0XXAdQnaGZ4wz0O1R/DC8t0ZDp4wSlb3fVJpQoEKkqMY9bK2H4DvToOvLFck" ],
+ "keyUse" : [ "ENC" ],
+ "certificate" : [ "MIIClzCCAX8CBgGAmWypGDANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUwNjEyNDkzNloXDTMyMDUwNjEyNTExNlowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI5/xFdNu5Sr/zb2LgsQqlKwLFSkx6Si8hWQniX6zHBp/Vw04zDGFcef9sILjyRaV/p1MxUgoxrno0VmplRTqB4mlwfpq3T6AGWwTa4HXqsG9vu/htIqrJlPi34v6cbLm00y2SGQdTwX1f+A5ls970VPrmqYh6OaZ/odJm3jgi5bbmZsoIipZM6Uy32cXZklzKgsGb/SemXb66YqPPAmthdXQ+MN2z1r3Gy5n7QJKQcrnMh5aNL94yz26vS0Be30Q91qhwxoEIyxSSSrzderU51ZC1sxROLbprEIqwqfDKALgt3ymuV+QCo2UWq3K5pfW5pxSHflRrO+uBWPEx1bqrUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAThu6AxY4bqRbl4RtPH5Xcm033xwLssXmVXh01NuQUfowtkHi77lGUWXBmvwY6UIKC4D4eYrGVYVRTjHUEknmLrxSzUKi4g38kax4pBfwWWstWyVyo89dl7hA5ZlzdZ+SFB4HasGcXdhVFG2dwVvx6lnfxBIWZkgy5GAtIOpK4oIJOIutTiR1yuku4a9zkk6yumsxTKivAs1UMsvQXzcFSUDIrdj0vfdCAB6SvjpYvf8d0wO31bb+t3vWblv29RNV4qbuA/CZkrvWZZXZ+bal0qZd06Z+Hbc4iBgPSHs/HjyAJ8xac3ljg0IWolvZxRkFBR4VSB3pgCUKxf3V4mgbPw==" ],
+ "priority" : [ "100" ],
+ "algorithm" : [ "RSA-OAEP" ]
+ }
+ }, {
+ "id" : "66a592ec-8657-4f53-8870-1e1693ff266c",
+ "name" : "rsa",
+ "providerId" : "rsa",
+ "subComponents" : { },
+ "config" : {
+ "privateKey" : [ "MIIEpAIBAAKCAQEA2dP+vRn+Kj+S/oGd49kq6+CKNAduCC1raLfTH7B3qjmZYm45 yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhKIdcIWadhqDzdtn1hj/22iUwrhH0b d475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2B9q9KFBmo4Ahh/6+d4wM1rH9kxl0 RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF2cr3wQwCfF1qVu4eAVNVfxfy/uEv G3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgGQAvkknWitpRK8KVLypEj5WKej6CF 8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7VwIDAQABAoIBAFsB5FszYepa11o3 4zSPxgv4qyUjuYf3GfoNW0rRGp3nJLtoHAIYa0CcLX9kzsQfmLtxoY46mdppxr8Z 2qUZpBdRVO7ILNfyXhthdQKI2NuyFDhtYK1p8bx6BXe095HMcvm2ohjXzPdTP4Hq HrXAYXjUndUbClbjMJ82AnPF8pM70kBq7g733UqkdfrMuv6/d95Jiyw4cC7dGsI3 Ruz9DGhiAyCBtQ0tUB+6Kqn5DChSB+ccfMJjr6GnCVYmERxEQ5DJCTIX8am8C6KX mAxUwHMTsEGBU6GzhcUgAwUFEK3I9RptdlRFp7F8E/P0LxmPkFdgaBNUhrdnB7Y4 01n1R1kCgYEA/huFJgwVWSBSK/XIouFuQrxZOI9JbBbdmpFT7SBGCdFg26Or9y7j +N5HE7yuoZ9PkBh17zzosZdsJhGocRYvO0LSq8cXvKXKCwn2fTMM7uJ/oQe68sxG cF/fC0M/8LvRESWShH920rrERu0s161RuasdOPre0aXu7ZQzkQ68O6MCgYEA23NO DHKNblBOdFEWsvotLqV8DrIbQ4le7sSgQr56/bdn9GScZk2JU0f+pqzpiGUy9bIt 6uujvt5ar0IvpIQVdjf3dbp6Fy+Dwhd4yTR4dMdDECest7jL++/21x8Y0ywFhBIK yEd+QxpOLXP6qaSKTGxL2rnTXRjl8/g629xQPL0CgYEAkNNOh+jLIgjxzGxA9dRV 62M91qaTyi8eDkJV+wgx4taaxZP7Jt5qwCSvjegz/5m01wOZ88hbNxx+XxQhVJK4 SKZFO/I07Sfwh2oeOi0maeBdrYGiY09ZtiJuFRU3FBV3irZHU4zyRBh+VY5HyITX 12JXPWp+JC7WhkG5QiuLzNECgYEA15OBzICLpx6Es4clAVT6JaSzJcyZM9MyyuOl e2ubbrpJCK/9ZBIvIPzMj/e0wiSH1wzeRrSM+ud7tkcSfk6ytptsIN67KSOoD3b3 VNCStEU7ABe5eBG1cRzeI52MyYWpNYBzzyNMSacBvWz9hMD6ivCn44pAtGfNHclw KKNYvxECgYBOamf25md9Jy6rtQsJVEJWw+8sB4lBlKEEadc5qekR7ZQ0hwj8CnTm WOo856ynI28Sog62iw8F/do/z0B29RuGuxw+prkBkn3lg/VQXEitzqcYvota6osa 8XSfaPiTyQwWpzbFNZzzemlTsIDiF3UqwkHvWaMYPDf4Ng3cokPPxw==" ],
+ "certificate" : [ "MIICmDCCAYACCQC7YJWOo6LVaDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANq d3QwHhcNMjIwNTA2MTQzNjQ5WhcNMjIwNjA1MTQzNjQ5WjAOMQwwCgYDVQQDDANq d3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ0/69Gf4qP5L+gZ3j 2Srr4Io0B24ILWtot9MfsHeqOZlibjnIOX5eaAr0I2YdeSGj2q+aF2Sx3MNWx14O WEoh1whZp2GoPN22fWGP/baJTCuEfRt3jvmGUpyyJn6jL+x2AaADO+aZNCZ0yoRe pXYH2r0oUGajgCGH/r53jAzWsf2TGXRG8wAot751qggciOiTyEI7hwpBATD8SsF7 hIXZyvfBDAJ8XWpW7h4BU1V/F/L+4S8bdDvHTTk/dNwr5BxiAnFq/eWJy1KLl3JY uAZAC+SSdaK2lErwpUvKkSPlYp6PoIXyerfS62fXkVCDQmQeirPCIKqp56fwYg+1 4jtXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACJlWtWnQqepYiFCijVgy/eM5KL0 rFZOZ6HNefoJTrYY1QYZrWxRz3M4u9JpUy4fBvGHxElBElcr3fXLXDytH9EwMJm1 E5x3o3qkQyWdXYGW6ZF58dklcJTdejOxEO373qpywVwbCFGiuIt7s5v4v+r2HOg3 D4elb2bqxmRim04xIkVZufKo+h6a8dBb5JEU3UaxyGDBR0IdyjhyBo1+HhH+RqZs xQhQ7DhlIGWUYZNCu13fb1GNSMiNqspKnMpFdQ4Bfpsb7vOeEK+aqJjCKcYbuGa6 BiwBjbKYyEF5r01Tob50dcVPfIGOqO0lQ3IsV31n9LSoAAtaVqioPK1rvDo=" ],
+ "active" : [ "true" ],
+ "priority" : [ "101" ],
+ "enabled" : [ "true" ],
+ "algorithm" : [ "RS256" ]
+ }
+ } ]
+ },
+ "internationalizationEnabled" : false,
+ "supportedLocales" : [ ],
+ "authenticationFlows" : [ {
+ "id" : "d07a624b-8e59-43fa-8437-6a15d8ed75fb",
+ "alias" : "Account verification options",
+ "description" : "Method with which to verity the existing account",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-email-verification",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Verify Existing Account by Re-authentication",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "512a7da1-b2fa-4081-b724-dcb777dbcf55",
+ "alias" : "Authentication Options",
+ "description" : "Authentication options.",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "basic-auth",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "basic-auth-otp",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-spnego",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 30,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "72e840a1-ad4c-46da-b614-f62f632ce941",
+ "alias" : "Browser - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-otp-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "c7c7cdc9-027c-4bc9-9c54-7b021d0c2589",
+ "alias" : "Direct Grant - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "direct-grant-validate-otp",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "29397d8b-f6e1-4ec9-afac-565e3652a55e",
+ "alias" : "First broker login - Conditional OTP",
+ "description" : "Flow to determine if the OTP is required for the authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-otp-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "852b85a0-f8db-406c-82c4-8becac69b745",
+ "alias" : "Handle Existing Account",
+ "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-confirm-link",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Account verification options",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "13b6e45c-2a8c-48da-903f-edb567dcc2a9",
+ "alias" : "Reset - Conditional OTP",
+ "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "conditional-user-configured",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-otp",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "70315d81-39f1-465d-8498-de28b87ddb34",
+ "alias" : "User creation or linking",
+ "description" : "Flow for the existing/non-existing user alternatives",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticatorConfig" : "create unique user config",
+ "authenticator" : "idp-create-user-if-unique",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Handle Existing Account",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "a2852c66-3968-4f81-938c-85e41707e48d",
+ "alias" : "Verify Existing Account by Re-authentication",
+ "description" : "Reauthentication of existing account",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "idp-username-password-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "First broker login - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "b5959d71-2358-4bfc-90fc-d8bea212894e",
+ "alias" : "browser",
+ "description" : "browser based authentication",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "auth-cookie",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "auth-spnego",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "identity-provider-redirector",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 25,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 30,
+ "autheticatorFlow" : true,
+ "flowAlias" : "forms",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "b0ab14fa-7321-4158-8e0d-c49a2f3e25ae",
+ "alias" : "clients",
+ "description" : "Base authentication for clients",
+ "providerId" : "client-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "client-secret",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-jwt",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-secret-jwt",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 30,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "client-x509",
+ "authenticatorFlow" : false,
+ "requirement" : "ALTERNATIVE",
+ "priority" : 40,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "33e12607-e441-4d8f-b9fe-db29eff9aab7",
+ "alias" : "direct grant",
+ "description" : "OpenID Connect Resource Owner Grant",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "direct-grant-validate-username",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "direct-grant-validate-password",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 30,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Direct Grant - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "bc63c1bc-cb85-4f3c-ba1e-3d270d85244b",
+ "alias" : "docker auth",
+ "description" : "Used by Docker clients to authenticate against the IDP",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "docker-http-basic-authenticator",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "525b6455-c328-4166-a05d-af8e47b4fbd6",
+ "alias" : "first broker login",
+ "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticatorConfig" : "review profile config",
+ "authenticator" : "idp-review-profile",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "User creation or linking",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "1cba19e3-bef5-4b3a-b156-3539b81a20fd",
+ "alias" : "forms",
+ "description" : "Username, password, otp and other auth forms.",
+ "providerId" : "basic-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "auth-username-password-form",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Browser - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "45ce11b0-6595-42e3-be69-3c1d052371d7",
+ "alias" : "http challenge",
+ "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "no-cookie-redirect",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Authentication Options",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "8bc948ff-c5a6-4e5e-998c-6a0e77e7b16b",
+ "alias" : "registration",
+ "description" : "registration flow",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "registration-page-form",
+ "authenticatorFlow" : true,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : true,
+ "flowAlias" : "registration form",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "58022df6-ad48-4b0e-9b77-663ea0a19e9f",
+ "alias" : "registration form",
+ "description" : "registration form",
+ "providerId" : "form-flow",
+ "topLevel" : false,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "registration-user-creation",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "registration-profile-action",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 40,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "registration-password-action",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 50,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "registration-recaptcha-action",
+ "authenticatorFlow" : false,
+ "requirement" : "DISABLED",
+ "priority" : 60,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "c45b5402-d7cb-4973-87c9-36a8fba5af25",
+ "alias" : "reset credentials",
+ "description" : "Reset credentials for a user if they forgot their password or something",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "reset-credentials-choose-user",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-credential-email",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 20,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticator" : "reset-password",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 30,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ }, {
+ "authenticatorFlow" : true,
+ "requirement" : "CONDITIONAL",
+ "priority" : 40,
+ "autheticatorFlow" : true,
+ "flowAlias" : "Reset - Conditional OTP",
+ "userSetupAllowed" : false
+ } ]
+ }, {
+ "id" : "9cc342be-8b91-4559-8da5-d259c9a87903",
+ "alias" : "saml ecp",
+ "description" : "SAML ECP Profile Authentication Flow",
+ "providerId" : "basic-flow",
+ "topLevel" : true,
+ "builtIn" : true,
+ "authenticationExecutions" : [ {
+ "authenticator" : "http-basic-authenticator",
+ "authenticatorFlow" : false,
+ "requirement" : "REQUIRED",
+ "priority" : 10,
+ "autheticatorFlow" : false,
+ "userSetupAllowed" : false
+ } ]
+ } ],
+ "authenticatorConfig" : [ {
+ "id" : "8f1fdb89-ccc1-4238-bcf6-3c7f364ee017",
+ "alias" : "create unique user config",
+ "config" : {
+ "require.password.update.after.registration" : "false"
+ }
+ }, {
+ "id" : "6835b816-87ad-46a4-a6cd-d3e4650cb7b4",
+ "alias" : "review profile config",
+ "config" : {
+ "update.profile.on.first.login" : "missing"
+ }
+ } ],
+ "requiredActions" : [ {
+ "alias" : "CONFIGURE_TOTP",
+ "name" : "Configure OTP",
+ "providerId" : "CONFIGURE_TOTP",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 10,
+ "config" : { }
+ }, {
+ "alias" : "terms_and_conditions",
+ "name" : "Terms and Conditions",
+ "providerId" : "terms_and_conditions",
+ "enabled" : false,
+ "defaultAction" : false,
+ "priority" : 20,
+ "config" : { }
+ }, {
+ "alias" : "UPDATE_PASSWORD",
+ "name" : "Update Password",
+ "providerId" : "UPDATE_PASSWORD",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 30,
+ "config" : { }
+ }, {
+ "alias" : "UPDATE_PROFILE",
+ "name" : "Update Profile",
+ "providerId" : "UPDATE_PROFILE",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 40,
+ "config" : { }
+ }, {
+ "alias" : "VERIFY_EMAIL",
+ "name" : "Verify Email",
+ "providerId" : "VERIFY_EMAIL",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 50,
+ "config" : { }
+ }, {
+ "alias" : "delete_account",
+ "name" : "Delete Account",
+ "providerId" : "delete_account",
+ "enabled" : false,
+ "defaultAction" : false,
+ "priority" : 60,
+ "config" : { }
+ }, {
+ "alias" : "update_user_locale",
+ "name" : "Update User Locale",
+ "providerId" : "update_user_locale",
+ "enabled" : true,
+ "defaultAction" : false,
+ "priority" : 1000,
+ "config" : { }
+ } ],
+ "browserFlow" : "browser",
+ "registrationFlow" : "registration",
+ "directGrantFlow" : "direct grant",
+ "resetCredentialsFlow" : "reset credentials",
+ "clientAuthenticationFlow" : "clients",
+ "dockerAuthenticationFlow" : "docker auth",
+ "attributes" : {
+ "cibaBackchannelTokenDeliveryMode" : "poll",
+ "cibaExpiresIn" : "120",
+ "cibaAuthRequestedUserHint" : "login_hint",
+ "oauth2DeviceCodeLifespan" : "600",
+ "clientOfflineSessionMaxLifespan" : "0",
+ "oauth2DevicePollingInterval" : "5",
+ "clientSessionIdleTimeout" : "0",
+ "parRequestUriLifespan" : "60",
+ "clientSessionMaxLifespan" : "0",
+ "clientOfflineSessionIdleTimeout" : "0",
+ "cibaInterval" : "5",
+ "realmReusableOtpCode" : "false"
+ },
+ "keycloakVersion" : "20.0.3",
+ "userManagedAccessAllowed" : false,
+ "clientProfiles" : {
+ "profiles" : [ ]
+ },
+ "clientPolicies" : {
+ "policies" : [ ]
+ }
+}
diff --git a/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf b/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf
new file mode 100644
index 0000000000..207106a14f
--- /dev/null
+++ b/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf
@@ -0,0 +1,7 @@
+auth_backends.1 = rabbit_auth_backend_oauth2
+
+auth_oauth2.resource_server_id = rabbitmq
+auth_oauth2.preferred_username_claims.1 = username
+auth_oauth2.additional_scopes_key = permissions
+auth_oauth2.default_key = Gnl2ZlbRh3rAr6Wymc988_5cY7T5GuePd5dpJlXDJUk
+auth_oauth2.signing_keys.Gnl2ZlbRh3rAr6Wymc988_5cY7T5GuePd5dpJlXDJUk = /etc/rabbitmq/signing-key.pem
diff --git a/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem b/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem
new file mode 100644
index 0000000000..a49f2f41e6
--- /dev/null
+++ b/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
+6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
+IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
+B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
+2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
+QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
+VwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/projects/TestApplications/OAuth2/start-keycloak.sh b/projects/TestApplications/OAuth2/start-keycloak.sh
new file mode 100755
index 0000000000..18cc936c99
--- /dev/null
+++ b/projects/TestApplications/OAuth2/start-keycloak.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+source "$script_dir/common.sh"
+
+readonly docker_name="$docker_name_prefix-keycloak"
+
+docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net
+docker rm -f "$docker_name" 2>/dev/null || echo "[INFO] keycloak was not running"
+echo "[INFO] running $docker_name docker image ..."
+
+readonly signing_key_file="$script_dir/keycloak/signing-key/signing-key.pem"
+if [[ -f $signing_key_file ]]
+then
+ OAUTH2_RABBITMQ_EXTRA_MOUNTS="${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} --volume $signing_key_file:/etc/rabbitmq/signing-key.pem"
+fi
+
+# Note:
+# Variables left un-quoted so that words are actually split as args
+# shellcheck disable=SC2086
+docker run --detach --name "$docker_name" --net rabbitmq_net \
+ --publish 8080:8080 \
+ --env KEYCLOAK_ADMIN=admin \
+ --env KEYCLOAK_ADMIN_PASSWORD=admin \
+ --mount type=bind,source="$script_dir/keycloak/import/,target=/opt/keycloak/data/import/" \
+ $OAUTH2_RABBITMQ_EXTRA_MOUNTS \
+ quay.io/keycloak/keycloak:20.0 start-dev --import-realm
diff --git a/projects/TestApplications/OAuth2/start-rabbit.sh b/projects/TestApplications/OAuth2/start-rabbit.sh
new file mode 100755
index 0000000000..018d8aaf39
--- /dev/null
+++ b/projects/TestApplications/OAuth2/start-rabbit.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+readonly mode="${MODE:-keycloak}"
+readonly image_tag="${IMAGE_TAG:-3-management}"
+readonly image="${IMAGE:-rabbitmq}"
+
+source "$script_dir/common.sh"
+
+readonly docker_name="$docker_name_prefix-rabbitmq"
+
+readonly signing_key_file="$script_dir/$mode/signing-key/signing-key.pem"
+if [[ -f $signing_key_file ]]
+then
+ OAUTH2_RABBITMQ_EXTRA_MOUNTS="${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} --volume $signing_key_file:/etc/rabbitmq/signing-key.pem"
+fi
+
+readonly mount_rabbitmq_config="/etc/rabbitmq/rabbitmq.conf"
+readonly config_dir="$script_dir/$mode"
+
+docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net
+docker rm -f "$docker_name" 2>/dev/null || echo "[INFO] $docker_name was not running"
+
+echo "[INFO] running RabbitMQ ($image:$image_tag) with Idp $mode and configuration file $config_dir/rabbitmq.conf"
+
+# Note:
+# Variables left un-quoted so that words are actually split as args
+# shellcheck disable=SC2086
+docker run --detach --name "$docker_name" --net rabbitmq_net \
+ --publish '15672:15672' --publish '5672:5672' ${OAUTH2_RABBITMQ_EXTRA_PORTS:-} \
+ --volume "$config_dir/rabbitmq.conf:$mount_rabbitmq_config:ro" \
+ --volume "$script_dir/enabled_plugins:/etc/rabbitmq/enabled_plugins" \
+ --volume "$config_dir:/conf" ${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} "$image:$image_tag"
diff --git a/projects/TestApplications/OAuth2/start-test.sh b/projects/TestApplications/OAuth2/start-test.sh
new file mode 100755
index 0000000000..8cd9072ff3
--- /dev/null
+++ b/projects/TestApplications/OAuth2/start-test.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+readonly mode=${MODE:-keycloak}
+readonly client_id=producer
+
+if [[ $mode == keycloak ]]
+then
+ readonly client_secret=kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn
+ readonly token_endpoint="http://localhost:8080/realms/test/protocol/openid-connect/token"
+ readonly scope="rabbitmq:configure:*/* rabbitmq:read:*/* rabbitmq:write:*/*"
+else
+ readonly client_secret=producer_secret
+ readonly token_endpoint="http://localhost:8080/oauth/token"
+ readonly scope=""
+fi
+
+dotnet run --Name "$mode" \
+ --ClientId "$client_id" \
+ --ClientSecret "$client_secret" \
+ --Scope "$scope" \
+ --TokenEndpoint "$token_endpoint" \
+ --TokenExpiresInSeconds 60
diff --git a/projects/TestApplications/OAuth2/start-uaa.sh b/projects/TestApplications/OAuth2/start-uaa.sh
new file mode 100755
index 0000000000..a6546c1e8b
--- /dev/null
+++ b/projects/TestApplications/OAuth2/start-uaa.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+source "$script_dir/common.sh"
+
+readonly docker_name="$docker_name_prefix-uaa"
+
+readonly uaa_image_tag=${UAA_IMAGE_TAG:-75.21.0}
+readonly uaa_image_name=${UAA_IMAGE_NAME:-cloudfoundry/uaa}
+
+docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net
+docker rm --force "$docker_name" 2>/dev/null || echo "[INFO] $docker_name was not running"
+
+echo "[INFO] running ${uaa_image_name}:${uaa_image_tag} docker image"
+
+docker run --detach --name "$docker_name" --net rabbitmq_net \
+ --publish 8080:8080 \
+ --mount "type=bind,source=${script_dir}/uaa,target=/uaa" \
+ --env UAA_CONFIG_PATH="/uaa" \
+ --env JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom" \
+ "${uaa_image_name}:${uaa_image_tag}"
diff --git a/projects/TestApplications/OAuth2/stop-keycloak.sh b/projects/TestApplications/OAuth2/stop-keycloak.sh
new file mode 100755
index 0000000000..0dfd7f4d7d
--- /dev/null
+++ b/projects/TestApplications/OAuth2/stop-keycloak.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+source "$script_dir/common.sh"
+
+docker_stop keycloak
diff --git a/projects/TestApplications/OAuth2/stop-rabbit.sh b/projects/TestApplications/OAuth2/stop-rabbit.sh
new file mode 100755
index 0000000000..d04b956e50
--- /dev/null
+++ b/projects/TestApplications/OAuth2/stop-rabbit.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+source "$script_dir/common.sh"
+
+docker_stop rabbitmq
diff --git a/projects/TestApplications/OAuth2/stop-uaa.sh b/projects/TestApplications/OAuth2/stop-uaa.sh
new file mode 100755
index 0000000000..3337ce17b2
--- /dev/null
+++ b/projects/TestApplications/OAuth2/stop-uaa.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+readonly script_dir
+
+source "$script_dir/common.sh"
+
+docker_stop uaa
diff --git a/projects/TestApplications/OAuth2/uaa/log4j2.properties b/projects/TestApplications/OAuth2/uaa/log4j2.properties
new file mode 100644
index 0000000000..2e22eaa45d
--- /dev/null
+++ b/projects/TestApplications/OAuth2/uaa/log4j2.properties
@@ -0,0 +1,30 @@
+status = error
+dest = err
+name = UaaLog
+
+property.log_pattern=[%d{yyyy-MM-dd'T'HH:mm:ss.nnnnnn}{GMT+0}Z] uaa%X{context} - %pid [%t] .... %5p --- %c{1}: %replace{%m}{(?<=password=|client_secret=)([^&]*)}{}%n
+
+appender.uaaDefaultAppender.type = File
+appender.uaaDefaultAppender.name = UaaDefaultAppender
+appender.uaaDefaultAppender.fileName = logs/uaa.log
+appender.uaaDefaultAppender.layout.type = PatternLayout
+appender.uaaDefaultAppender.layout.pattern = ${log_pattern}
+
+appender.uaaAuditAppender.type = File
+appender.uaaAuditAppender.name = UaaAuditAppender
+appender.uaaAuditAppender.fileName = logs/uaa_events.log
+appender.uaaAuditAppender.layout.type = PatternLayout
+appender.uaaAuditAppender.layout.pattern = ${log_pattern}
+
+rootLogger.level = debug
+rootLogger.appenderRef.uaaDefaultAppender.ref = UaaDefaultAppender
+
+logger.UAAAudit.name = UAA.Audit
+logger.UAAAudit.level = info
+logger.UAAAudit.additivity = true
+logger.UAAAudit.appenderRef.auditEventLog.ref = UaaAuditAppender
+
+logger.cfIdentity.name = org.cloudfoundry.identity
+logger.cfIdentity.level = info
+logger.cfIdentity.additivity = false
+logger.cfIdentity.appenderRef.uaaDefaultAppender.ref = UaaDefaultAppender
diff --git a/projects/TestApplications/OAuth2/uaa/rabbitmq.conf b/projects/TestApplications/OAuth2/uaa/rabbitmq.conf
new file mode 100644
index 0000000000..b0a6d5b0f4
--- /dev/null
+++ b/projects/TestApplications/OAuth2/uaa/rabbitmq.conf
@@ -0,0 +1,15 @@
+auth_backends.1 = rabbit_auth_backend_oauth2
+management.oauth_enabled = true
+management.oauth_client_id = rabbit_client_code
+management.oauth_provider_url = http://localhost:8080/
+
+amqp1_0.default_user = none
+
+auth_oauth2.resource_server_id = rabbitmq
+auth_oauth2.additional_scopes_key = extra_scope
+auth_oauth2.default_key = legacy-token-key
+auth_oauth2.preferred_username_claims.1 = preferred_username
+auth_oauth2.preferred_username_claims.2 = user_name
+auth_oauth2.preferred_username_claims.3 = email
+
+auth_oauth2.signing_keys.legacy-token-key = /etc/rabbitmq/signing-key.pem
diff --git a/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem b/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem
new file mode 100644
index 0000000000..a49f2f41e6
--- /dev/null
+++ b/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq
+6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK
+IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2
+B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF
+2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG
+QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7
+VwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/projects/TestApplications/OAuth2/uaa/uaa.yml b/projects/TestApplications/OAuth2/uaa/uaa.yml
new file mode 100644
index 0000000000..2534d71b3a
--- /dev/null
+++ b/projects/TestApplications/OAuth2/uaa/uaa.yml
@@ -0,0 +1,171 @@
+logging:
+ config: /uaa/log4j2.properties
+
+issuer:
+ uri: http://local:8080/
+
+encryption:
+ active_key_label: CHANGE-THIS-KEY
+ encryption_keys:
+ - label: CHANGE-THIS-KEY
+ passphrase: CHANGEME
+
+logout:
+ redirect:
+ parameter:
+ disable: false
+ whitelist:
+ http://localhost:15672/*
+
+login:
+ serviceProviderKey: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5
+ L39WqS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vA
+ fpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQAB
+ AoGAVOj2Yvuigi6wJD99AO2fgF64sYCm/BKkX3dFEw0vxTPIh58kiRP554Xt5ges
+ 7ZCqL9QpqrChUikO4kJ+nB8Uq2AvaZHbpCEUmbip06IlgdA440o0r0CPo1mgNxGu
+ lhiWRN43Lruzfh9qKPhleg2dvyFGQxy5Gk6KW/t8IS4x4r0CQQD/dceBA+Ndj3Xp
+ ubHfxqNz4GTOxndc/AXAowPGpge2zpgIc7f50t8OHhG6XhsfJ0wyQEEvodDhZPYX
+ kKBnXNHzAkEAyCA76vAwuxqAd3MObhiebniAU3SnPf2u4fdL1EOm92dyFs1JxyyL
+ gu/DsjPjx6tRtn4YAalxCzmAMXFSb1qHfwJBAM3qx3z0gGKbUEWtPHcP7BNsrnWK
+ vw6By7VC8bk/ffpaP2yYspS66Le9fzbFwoDzMVVUO/dELVZyBnhqSRHoXQcCQQCe
+ A2WL8S5o7Vn19rC0GVgu3ZJlUrwiZEVLQdlrticFPXaFrn3Md82ICww3jmURaKHS
+ N+l4lnMda79eSp3OMmq9AkA0p79BvYsLshUJJnvbk76pCjR28PK4dV1gSDUEqQMB
+ qy45ptdwJLqLJCeNoR0JUcDNIRhOCuOPND7pcMtX6hI/
+ -----END RSA PRIVATE KEY-----
+ serviceProviderKeyPassword: password
+ serviceProviderCertificate: |
+ -----BEGIN CERTIFICATE-----
+ MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEO
+ MAwGA1UECBMFYXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEO
+ MAwGA1UECxMFYXJ1YmExDjAMBgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5h
+ cnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2MjdaFw0xNjExMTkyMjI2MjdaMHwx
+ CzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAM
+ BgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAb
+ BgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ ADCBiQKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39W
+ qS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOw
+ znoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQABo4Ha
+ MIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1syGDCBpwYDVR0jBIGfMIGc
+ gBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3MQ4wDAYD
+ VQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYD
+ VQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJh
+ QGFydWJhLmFyggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ
+ 0HOZbbHClXmGUjGs+GS+xC1FO/am2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxC
+ KdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3oePe84k8jm3A7EvH5wi5hvCkK
+ RpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=
+ -----END CERTIFICATE-----
+#The secret that an external login server will use to authenticate to the uaa using the id `login`
+LOGIN_SECRET: loginsecret
+
+jwt:
+ token:
+ policy:
+ # Will override global validity policies for the default zone only.
+ accessTokenValiditySeconds: 60
+ keys:
+ legacy-token-key:
+ signingKey: |
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIEpAIBAAKCAQEA2dP+vRn+Kj+S/oGd49kq6+CKNAduCC1raLfTH7B3qjmZYm45
+ yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhKIdcIWadhqDzdtn1hj/22iUwrhH0b
+ d475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2B9q9KFBmo4Ahh/6+d4wM1rH9kxl0
+ RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF2cr3wQwCfF1qVu4eAVNVfxfy/uEv
+ G3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgGQAvkknWitpRK8KVLypEj5WKej6CF
+ 8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7VwIDAQABAoIBAFsB5FszYepa11o3
+ 4zSPxgv4qyUjuYf3GfoNW0rRGp3nJLtoHAIYa0CcLX9kzsQfmLtxoY46mdppxr8Z
+ 2qUZpBdRVO7ILNfyXhthdQKI2NuyFDhtYK1p8bx6BXe095HMcvm2ohjXzPdTP4Hq
+ HrXAYXjUndUbClbjMJ82AnPF8pM70kBq7g733UqkdfrMuv6/d95Jiyw4cC7dGsI3
+ Ruz9DGhiAyCBtQ0tUB+6Kqn5DChSB+ccfMJjr6GnCVYmERxEQ5DJCTIX8am8C6KX
+ mAxUwHMTsEGBU6GzhcUgAwUFEK3I9RptdlRFp7F8E/P0LxmPkFdgaBNUhrdnB7Y4
+ 01n1R1kCgYEA/huFJgwVWSBSK/XIouFuQrxZOI9JbBbdmpFT7SBGCdFg26Or9y7j
+ +N5HE7yuoZ9PkBh17zzosZdsJhGocRYvO0LSq8cXvKXKCwn2fTMM7uJ/oQe68sxG
+ cF/fC0M/8LvRESWShH920rrERu0s161RuasdOPre0aXu7ZQzkQ68O6MCgYEA23NO
+ DHKNblBOdFEWsvotLqV8DrIbQ4le7sSgQr56/bdn9GScZk2JU0f+pqzpiGUy9bIt
+ 6uujvt5ar0IvpIQVdjf3dbp6Fy+Dwhd4yTR4dMdDECest7jL++/21x8Y0ywFhBIK
+ yEd+QxpOLXP6qaSKTGxL2rnTXRjl8/g629xQPL0CgYEAkNNOh+jLIgjxzGxA9dRV
+ 62M91qaTyi8eDkJV+wgx4taaxZP7Jt5qwCSvjegz/5m01wOZ88hbNxx+XxQhVJK4
+ SKZFO/I07Sfwh2oeOi0maeBdrYGiY09ZtiJuFRU3FBV3irZHU4zyRBh+VY5HyITX
+ 12JXPWp+JC7WhkG5QiuLzNECgYEA15OBzICLpx6Es4clAVT6JaSzJcyZM9MyyuOl
+ e2ubbrpJCK/9ZBIvIPzMj/e0wiSH1wzeRrSM+ud7tkcSfk6ytptsIN67KSOoD3b3
+ VNCStEU7ABe5eBG1cRzeI52MyYWpNYBzzyNMSacBvWz9hMD6ivCn44pAtGfNHclw
+ KKNYvxECgYBOamf25md9Jy6rtQsJVEJWw+8sB4lBlKEEadc5qekR7ZQ0hwj8CnTm
+ WOo856ynI28Sog62iw8F/do/z0B29RuGuxw+prkBkn3lg/VQXEitzqcYvota6osa
+ 8XSfaPiTyQwWpzbFNZzzemlTsIDiF3UqwkHvWaMYPDf4Ng3cokPPxw==
+ -----END RSA PRIVATE KEY-----
+
+scim:
+ users:
+ - rabbit_admin|rabbit_admin|scim.read,openid,rabbitmq.read:*/*,rabbitmq.write:*/*,rabbitmq.configure:*/*,rabbitmq.tag:administrator
+ - rabbitmq_management|rabbitmq_management|scim.read,openid,rabbitmq.read:*/*,rabbitmq.write:*/*,rabbitmq.configure:*/*,rabbitmq.tag:management
+ - rabbit_monitor|rabbit_monitor|scim.read,openid,rabbitmq.tag:monitoring
+ groups:
+ 'rabbitmq.read:*/*': Read all
+ 'rabbitmq.write:*/*': Write all
+ 'rabbitmq.configure:*/*': Configure all
+ 'rabbitmq.tag:management': Management
+ 'rabbitmq.tag:monitoring': Monitoring
+ 'rabbitmq.tag:administrator': Administrator
+
+oauth:
+ # Always override clients on startup
+ client:
+ override: true
+
+ # List of OAuth clients
+ clients:
+ admin:
+ id: admin
+ secret: adminsecret
+ authorized-grant-types: client_credentials
+ scope: none
+ authorities: uaa.admin,clients.admin,clients.read,clients.write,clients.secret,scim.write,scim.read,uaa.resource
+ mgt_api_client:
+ id: mgt_api_client
+ secret: mgt_api_client
+ authorized-grant-types: client_credentials
+ authorities: rabbitmq.tag:monitoring
+ rabbit_client_code:
+ id: rabbit_client_code
+ secret: rabbit_client_code
+ authorized-grant-types: authorization_code
+ scope: rabbitmq.*,openid,profile
+ authorities: uaa.resource,rabbitmq
+ redirect-uri: http://localhost:15672
+ allowpublic: true
+ mgt_api_client_2:
+ id: mgt_api_client_2
+ secret: mgt_api_client_2
+ authorized-grant-types: client_credentials
+ authorities: api://rabbitmq:management
+ producer:
+ id: producer
+ secret: producer_secret
+ authorities: rabbitmq.write:*/*,rabbitmq.read:*/*,rabbitmq.configure:*/*
+ authorized-grant-types: client_credentials
+ consumer:
+ id: consumer
+ secret: consumer_secret
+ authorities: rabbitmq.read:*/* rabbitmq.configure:*/* rabbitmq.write:*/x-* rabbitmq.write:*/q-*
+ authorized-grant-types: client_credentials
+ jms_producer:
+ id: jms_producer
+ secret: jms_producer_secret
+ authorities: rabbitmq.write:%2F/*,rabbitmq.read:%2F/*,rabbitmq.configure:%2F/*,rabbitmq.configure:*/jms.durable.queues,rabbitmq.write:*/jms.durable.queues,rabbitmq.read:*/jms.durable.queues
+ authorized-grant-types: client_credentials
+ jms_consumer:
+ id: jms_consumer
+ secret: jms_consumer_secret
+ authorities: rabbitmq.read:*/* rabbitmq.configure:*/* rabbitmq.write:*/x-* rabbitmq.write:*/q-* rabbitmq.write:*/jms.durable.queues
+ authorized-grant-types: client_credentials
+ producer_with_roles:
+ id: producer_with_roles
+ secret: producer_with_roles_secret
+ authorities: rabbitmq.*,api://rabbitmq:producer,api://rabbitmq:Administrator
+ authorized-grant-types: client_credentials
+ consumer_with_roles:
+ id: consumer_with_roles
+ secret: consumer_with_roles_secret
+ authorities: rabbitmq.* api://rabbitmq:Read.All api://rabbitmq:Configure.All api://rabbitmq:Write.All
+ authorized-grant-types: client_credentials
diff --git a/projects/Unit/APIApproval.cs b/projects/Unit/APIApproval.cs
index 14396c7703..a7cf60b4fd 100644
--- a/projects/Unit/APIApproval.cs
+++ b/projects/Unit/APIApproval.cs
@@ -29,40 +29,37 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
-using System.Reflection;
+using System;
using System.Threading.Tasks;
-
using PublicApiGenerator;
-
using VerifyTests;
-
using VerifyXunit;
-
using Xunit;
namespace RabbitMQ.Client.Unit
{
-
[UsesVerify]
public class APIApproval
{
- [Fact]
- public Task Approve()
+ private static readonly ApiGeneratorOptions opts = new ApiGeneratorOptions
{
- string publicApi = typeof(ConnectionFactory).Assembly.GeneratePublicApi(new ApiGeneratorOptions
+ ExcludeAttributes = new[]
{
- ExcludeAttributes = new[]
- {
- "System.Runtime.Versioning.TargetFrameworkAttribute",
+ "System.Runtime.Versioning.TargetFrameworkAttribute",
"System.Reflection.AssemblyMetadataAttribute"
- }
- });
+ }
+ };
+ [Theory]
+ [InlineData("RabbitMQ.Client", typeof(RabbitMQ.Client.ConnectionFactory))]
+ [InlineData("RabbitMQ.Client.OAuth2", typeof(RabbitMQ.Client.OAuth2.IOAuth2Client))]
+ public Task Approve(string dirName, Type apiType)
+ {
+ string publicApi = apiType.Assembly.GeneratePublicApi(opts);
var settings = new VerifySettings();
+ settings.UseDirectory(dirName);
settings.DisableDiff();
-
return Verifier.Verify(publicApi, settings);
}
}
}
-
diff --git a/projects/Unit/Fixtures.cs b/projects/Unit/Fixtures.cs
index 74d4716902..684dfe045a 100644
--- a/projects/Unit/Fixtures.cs
+++ b/projects/Unit/Fixtures.cs
@@ -110,17 +110,22 @@ internal AutorecoveringConnection CreateAutorecoveringConnection()
{
return CreateAutorecoveringConnection(RECOVERY_INTERVAL);
}
+ internal AutorecoveringConnection CreateAutorecoveringConnection(ICredentialsProvider credentialsProvider)
+ {
+ return CreateAutorecoveringConnection(RECOVERY_INTERVAL, credentialsProvider);
+ }
internal AutorecoveringConnection CreateAutorecoveringConnection(IList hostnames)
{
return CreateAutorecoveringConnection(RECOVERY_INTERVAL, hostnames);
}
- internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval)
+ internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval, ICredentialsProvider credentialsProvider = null)
{
var cf = new ConnectionFactory
{
AutomaticRecoveryEnabled = true,
+ CredentialsProvider = credentialsProvider,
NetworkRecoveryInterval = interval
};
return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}");
@@ -470,7 +475,42 @@ private static bool IsRunningInCI()
return true;
}
}
+ public sealed class IgnoreOnVersionsEarlierThan : FactAttribute
+ {
+ public IgnoreOnVersionsEarlierThan(int major, int minor)
+ {
+ if (!CheckMiniumVersion(new Version(major, minor)))
+ {
+ Skip = $"Skipped test. It requires RabbitMQ +{major}.{minor}";
+ }
+ }
+ private bool CheckMiniumVersion(Version miniumVersion)
+ {
+ using (var _conn = new ConnectionFactory().CreateConnection())
+ {
+ System.Collections.Generic.IDictionary properties = _conn.ServerProperties;
+
+ if (properties.TryGetValue("version", out object versionVal))
+ {
+ string versionStr = Encoding.UTF8.GetString((byte[])versionVal);
+
+ int dashIdx = Math.Max(versionStr.IndexOf('-'), versionStr.IndexOf('+'));
+ if (dashIdx > 0)
+ {
+ versionStr = versionStr.Remove(dashIdx);
+ }
+
+ if (Version.TryParse(versionStr, out Version version))
+ {
+ return version >= miniumVersion;
+ }
+ }
+
+ return false;
+ }
+ }
+ }
public class TimingFixture
{
public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300);
diff --git a/projects/Unit/Helper/RequestFormMatcher.cs b/projects/Unit/Helper/RequestFormMatcher.cs
new file mode 100644
index 0000000000..9691c2495f
--- /dev/null
+++ b/projects/Unit/Helper/RequestFormMatcher.cs
@@ -0,0 +1,63 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Collections.Specialized;
+using Xunit;
+
+namespace RabbitMQ.Client.Unit
+{
+ public class RequestFormMatcher
+ {
+ private NameValueCollection _expected = new NameValueCollection();
+
+ public RequestFormMatcher WithParam(string key, string value)
+ {
+ _expected[key] = value;
+ return this;
+ }
+
+ public Func Matcher()
+ {
+ return (body) =>
+ {
+ NameValueCollection actual = System.Web.HttpUtility.ParseQueryString(body);
+ Assert.Equal(actual.Count, _expected.Count);
+ foreach (string k in actual.Keys)
+ {
+ Assert.NotNull(_expected[k]);
+ Assert.Equal(actual[k], _expected[k]);
+ }
+ return true;
+ };
+ }
+ }
+}
diff --git a/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt b/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt
new file mode 100644
index 0000000000..d1a77036a1
--- /dev/null
+++ b/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt
@@ -0,0 +1,50 @@
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
+[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")]
+namespace RabbitMQ.Client.OAuth2
+{
+ public interface IOAuth2Client
+ {
+ RabbitMQ.Client.OAuth2.IToken RefreshToken(RabbitMQ.Client.OAuth2.IToken token);
+ RabbitMQ.Client.OAuth2.IToken RequestToken();
+ }
+ public interface IToken
+ {
+ string AccessToken { get; }
+ System.TimeSpan ExpiresIn { get; }
+ string RefreshToken { get; }
+ bool hasExpired { get; }
+ }
+ public class JsonToken
+ {
+ public JsonToken() { }
+ public JsonToken(string access_token, string refresh_token, long expires_in) { }
+ public JsonToken(string access_token, string refresh_token, System.TimeSpan expires_in_span) { }
+ public string access_token { get; set; }
+ public long expires_in { get; set; }
+ public string refresh_token { get; set; }
+ }
+ public class OAuth2ClientBuilder
+ {
+ public OAuth2ClientBuilder(string clientId, string clientSecret, System.Uri tokenEndpoint) { }
+ public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder AddRequestParameter(string param, string paramValue) { }
+ public RabbitMQ.Client.OAuth2.IOAuth2Client Build() { }
+ public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder SetHttpClientHandler(System.Net.Http.HttpClientHandler handler) { }
+ public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder SetScope(string scope) { }
+ }
+ public class OAuth2ClientCredentialsProvider : RabbitMQ.Client.ICredentialsProvider
+ {
+ public OAuth2ClientCredentialsProvider(string name, RabbitMQ.Client.OAuth2.IOAuth2Client oAuth2Client) { }
+ public string Name { get; }
+ public string Password { get; }
+ public string UserName { get; }
+ public System.TimeSpan? ValidUntil { get; }
+ public void Refresh() { }
+ }
+ public class Token : RabbitMQ.Client.OAuth2.IToken
+ {
+ public Token(RabbitMQ.Client.OAuth2.JsonToken json) { }
+ public string AccessToken { get; }
+ public System.TimeSpan ExpiresIn { get; }
+ public string RefreshToken { get; }
+ }
+}
\ No newline at end of file
diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt
similarity index 95%
rename from projects/Unit/APIApproval.Approve.verified.txt
rename to projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt
index b18b6eab38..a02180eca0 100644
--- a/projects/Unit/APIApproval.Approve.verified.txt
+++ b/projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt
@@ -53,6 +53,15 @@ namespace RabbitMQ.Client
public virtual System.Threading.Tasks.Task HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) { }
public virtual System.Threading.Tasks.Task OnCancel(params string[] consumerTags) { }
}
+ public class BasicCredentialsProvider : RabbitMQ.Client.ICredentialsProvider
+ {
+ public BasicCredentialsProvider(string name, string userName, string password) { }
+ public string Name { get; }
+ public string Password { get; }
+ public string UserName { get; }
+ public System.TimeSpan? ValidUntil { get; }
+ public void Refresh() { }
+ }
public sealed class BasicGetResult : System.IDisposable
{
public BasicGetResult(ulong deliveryTag, bool redelivered, string exchange, string routingKey, uint messageCount, in RabbitMQ.Client.ReadOnlyBasicProperties basicProperties, System.ReadOnlyMemory body) { }
@@ -131,6 +140,8 @@ namespace RabbitMQ.Client
}
public sealed class ConnectionConfig
{
+ public RabbitMQ.Client.ICredentialsProvider CredentialsProvider;
+ public RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher;
public System.Collections.Generic.IList AuthMechanisms { get; }
public System.Collections.Generic.IDictionary ClientProperties { get; }
public string? ClientProvidedName { get; }
@@ -160,6 +171,7 @@ namespace RabbitMQ.Client
public const string DefaultVHost = "/";
public static readonly System.Collections.Generic.IList DefaultAuthMechanisms;
public static readonly System.TimeSpan DefaultConnectionTimeout;
+ public static readonly RabbitMQ.Client.ICredentialsRefresher DefaultCredentialsRefresher;
public static readonly System.TimeSpan DefaultHeartbeat;
public ConnectionFactory() { }
public System.Security.Authentication.SslProtocols AmqpUriSslProtocols { get; set; }
@@ -169,6 +181,8 @@ namespace RabbitMQ.Client
public string ClientProvidedName { get; set; }
public int ConsumerDispatchConcurrency { get; set; }
public System.TimeSpan ContinuationTimeout { get; set; }
+ public RabbitMQ.Client.ICredentialsProvider CredentialsProvider { get; set; }
+ public RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher { get; set; }
public bool DispatchConsumersAsync { get; set; }
public RabbitMQ.Client.AmqpTcpEndpoint Endpoint { get; set; }
public System.Func, RabbitMQ.Client.IEndpointResolver> EndpointResolverFactory { get; set; }
@@ -512,6 +526,8 @@ namespace RabbitMQ.Client
string ClientProvidedName { get; set; }
int ConsumerDispatchConcurrency { get; set; }
System.TimeSpan ContinuationTimeout { get; set; }
+ RabbitMQ.Client.ICredentialsProvider CredentialsProvider { get; set; }
+ RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher { get; set; }
bool DispatchConsumersAsync { get; set; }
System.TimeSpan HandshakeContinuationTimeout { get; set; }
string Password { get; set; }
@@ -529,6 +545,20 @@ namespace RabbitMQ.Client
RabbitMQ.Client.IConnection CreateConnection(System.Collections.Generic.IList endpoints, string clientProvidedName);
RabbitMQ.Client.IConnection CreateConnection(System.Collections.Generic.IList hostnames, string clientProvidedName);
}
+ public interface ICredentialsProvider
+ {
+ string Name { get; }
+ string Password { get; }
+ string UserName { get; }
+ System.TimeSpan? ValidUntil { get; }
+ void Refresh();
+ }
+ public interface ICredentialsRefresher
+ {
+ RabbitMQ.Client.ICredentialsProvider Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback);
+ bool Unregister(RabbitMQ.Client.ICredentialsProvider provider);
+ public delegate void NotifyCredentialRefreshed(bool succesfully);
+ }
public interface IEndpointResolver
{
System.Collections.Generic.IEnumerable All();
@@ -734,6 +764,28 @@ namespace RabbitMQ.Client
public string ServerName { get; set; }
public System.Security.Authentication.SslProtocols Version { get; set; }
}
+ public class TimerBasedCredentialRefresher : RabbitMQ.Client.ICredentialsRefresher
+ {
+ public TimerBasedCredentialRefresher() { }
+ public RabbitMQ.Client.ICredentialsProvider Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback) { }
+ public bool Unregister(RabbitMQ.Client.ICredentialsProvider provider) { }
+ }
+ [System.Diagnostics.Tracing.EventSource(Name="TimerBasedCredentialRefresher")]
+ public class TimerBasedCredentialRefresherEventSource : System.Diagnostics.Tracing.EventSource
+ {
+ public TimerBasedCredentialRefresherEventSource() { }
+ public static RabbitMQ.Client.TimerBasedCredentialRefresherEventSource Log { get; }
+ [System.Diagnostics.Tracing.Event(5)]
+ public void RefreshedCredentials(string name, bool succesfully) { }
+ [System.Diagnostics.Tracing.Event(1)]
+ public void Registered(string name) { }
+ [System.Diagnostics.Tracing.Event(3)]
+ public void ScheduledTimer(string name, double interval) { }
+ [System.Diagnostics.Tracing.Event(4)]
+ public void TriggeredTimer(string name) { }
+ [System.Diagnostics.Tracing.Event(2)]
+ public void Unregistered(string name) { }
+ }
public class TopologyRecoveryExceptionHandler
{
public TopologyRecoveryExceptionHandler() { }
diff --git a/projects/Unit/RabbitMQCtl.cs b/projects/Unit/RabbitMQCtl.cs
index 057f917c28..7a12e97f51 100644
--- a/projects/Unit/RabbitMQCtl.cs
+++ b/projects/Unit/RabbitMQCtl.cs
@@ -236,5 +236,24 @@ public static void AwaitRabbitMQ()
{
ExecRabbitMQCtl("await_startup");
}
+
+ public static void AddUser(string username, string password)
+ {
+ ExecRabbitMQCtl($"add_user {username} {password}");
+ }
+ public static void ChangePassword(string username, string password)
+ {
+ ExecRabbitMQCtl($"change_password {username} {password}");
+ }
+
+ public static void SetPermissions(string username, string conf, string write, string read)
+ {
+ ExecRabbitMQCtl($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" ");
+ }
+
+ public static void DeleteUser(string username)
+ {
+ ExecRabbitMQCtl($"delete_user {username}");
+ }
}
}
diff --git a/projects/Unit/TestOAuth2Client.cs b/projects/Unit/TestOAuth2Client.cs
new file mode 100644
index 0000000000..4aebc9e393
--- /dev/null
+++ b/projects/Unit/TestOAuth2Client.cs
@@ -0,0 +1,168 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Net.Http;
+using System.Text.Json;
+using RabbitMQ.Client.OAuth2;
+using WireMock.RequestBuilders;
+using WireMock.ResponseBuilders;
+using WireMock.Server;
+using Xunit;
+
+namespace RabbitMQ.Client.Unit
+{
+ public class TestOAuth2Client
+ {
+ protected string _client_id = "producer";
+ protected string _client_secret = "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn";
+ protected WireMockServer _oauthServer;
+
+ protected IOAuth2Client _client;
+
+ public TestOAuth2Client()
+ {
+ _oauthServer = WireMockServer.Start();
+
+ _client = new OAuth2ClientBuilder(_client_id, _client_secret, new System.Uri(_oauthServer.Url + "/token")).Build();
+ }
+
+ private void expectTokenRequest(RequestFormMatcher expectedRequestBody, JsonToken expectedResponse)
+ {
+ _oauthServer
+ .Given(
+ Request.Create()
+ .WithPath("/token")
+ .WithBody(expectedRequestBody.Matcher())
+ .UsingPost()
+ )
+ .RespondWith(
+ Response.Create()
+ .WithStatusCode(200)
+ .WithHeader("Content-Type", "application/json;charset=UTF-8")
+ .WithBody(JsonSerializer.Serialize(expectedResponse))
+ );
+ }
+
+ [Fact]
+ public void TestRequestToken()
+ {
+ JsonToken expectedJsonToken = new JsonToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(10));
+ expectTokenRequest(new RequestFormMatcher()
+ .WithParam("client_id", _client_id)
+ .WithParam("client_secret", _client_secret)
+ .WithParam("grant_type", "client_credentials"),
+ expectedJsonToken);
+
+ IToken token = _client.RequestToken();
+ Assert.NotNull(token);
+ Assert.Equal(token.AccessToken, expectedJsonToken.access_token);
+ Assert.Equal(token.RefreshToken, expectedJsonToken.refresh_token);
+ Assert.Equal(token.ExpiresIn, TimeSpan.FromSeconds(expectedJsonToken.expires_in));
+ }
+
+ private void expectTokenRefresh(JsonToken expectedResponse)
+ {
+ _oauthServer
+ .Given(
+ Request.Create()
+ .WithPath("/token")
+ .WithParam("client_id", _client_id)
+ .WithParam("client_secret", _client_secret)
+ .WithParam("grant_type", "refresh_token")
+ .WithParam("refresh_token", expectedResponse.refresh_token)
+ .WithHeader("content_type", "application/x-www-form-urlencoded")
+ .UsingPost()
+ )
+ .RespondWith(
+ Response.Create()
+ .WithStatusCode(200)
+ .WithHeader("Content-Type", "application/json;charset=UTF-8")
+ .WithBody(JsonSerializer.Serialize(expectedResponse))
+ );
+ }
+
+ [Fact]
+ public void TestRefreshToken()
+ {
+ JsonToken expectedJsonToken = new JsonToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(10));
+ expectTokenRequest(new RequestFormMatcher()
+ .WithParam("client_id", _client_id)
+ .WithParam("client_secret", _client_secret)
+ .WithParam("grant_type", "client_credentials"),
+ expectedJsonToken);
+
+ IToken token = _client.RequestToken();
+ _oauthServer.Reset();
+
+ expectedJsonToken = new JsonToken("the_access_token2", "the_refresh_token", TimeSpan.FromSeconds(20));
+ expectTokenRequest(new RequestFormMatcher()
+ .WithParam("client_id", _client_id)
+ .WithParam("client_secret", _client_secret)
+ .WithParam("grant_type", "refresh_token")
+ .WithParam("refresh_token", "the_refresh_token"),
+ expectedJsonToken);
+
+ IToken refreshedToken = _client.RefreshToken(token);
+ Assert.False(refreshedToken == token);
+ Assert.NotNull(refreshedToken);
+ Assert.Equal(refreshedToken.AccessToken, expectedJsonToken.access_token);
+ Assert.Equal(refreshedToken.RefreshToken, expectedJsonToken.refresh_token);
+ Assert.Equal(token.ExpiresIn, TimeSpan.FromSeconds(expectedJsonToken.expires_in));
+ }
+
+ [Fact]
+ public void TestInvalidCredentials()
+ {
+ _oauthServer
+ .Given(
+ Request.Create()
+ .WithPath("/token")
+ .WithBody(new RequestFormMatcher()
+ .WithParam("client_id", _client_id)
+ .WithParam("client_secret", _client_secret)
+ .WithParam("grant_type", "client_credentials").Matcher())
+ .UsingPost()
+ )
+ .RespondWith(
+ Response.Create()
+ .WithStatusCode(401)
+ );
+
+ try
+ {
+ IToken token = _client.RequestToken();
+ Assert.Fail("Should have thrown Exception");
+ }
+ catch (HttpRequestException) { }
+ }
+ }
+}
diff --git a/projects/Unit/TestOAuth2ClientCredentialsProvider.cs b/projects/Unit/TestOAuth2ClientCredentialsProvider.cs
new file mode 100644
index 0000000000..c9149c96b7
--- /dev/null
+++ b/projects/Unit/TestOAuth2ClientCredentialsProvider.cs
@@ -0,0 +1,123 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+
+using Moq;
+using RabbitMQ.Client.OAuth2;
+using Xunit;
+
+namespace RabbitMQ.Client.Unit
+{
+ public class TestOAuth2CredentialsProvider
+ {
+ protected OAuth2ClientCredentialsProvider _provider;
+ protected Mock _oAuth2Client;
+
+ public TestOAuth2CredentialsProvider()
+ {
+ _oAuth2Client = new Mock();
+ _provider = new OAuth2ClientCredentialsProvider("aName", _oAuth2Client.Object);
+ }
+
+ [Fact]
+ public void shouldHaveAName()
+ {
+ Assert.Equal("aName", _provider.Name);
+ }
+
+ [Fact]
+ public void ShouldRequestTokenWhenAskToRefresh()
+ {
+ _oAuth2Client.Setup(p => p.RequestToken()).Returns(newToken("the_access_token", TimeSpan.FromSeconds(60)));
+ _provider.Refresh();
+ Assert.Equal("the_access_token", _provider.Password);
+ }
+
+ [Fact]
+ public void ShouldRequestTokenWhenGettingPasswordOrValidUntilForFirstTimeAccess()
+ {
+ IToken firstToken = newToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(1));
+ _oAuth2Client.Setup(p => p.RequestToken()).Returns(firstToken);
+ Assert.Equal(firstToken.AccessToken, _provider.Password);
+ Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value);
+ }
+
+ [Fact]
+ public void ShouldRefreshTokenUsingRefreshTokenWhenAvailable()
+ {
+ IToken firstToken = newToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(1));
+ IToken refreshedToken = newToken("the_access_token2", "the_refresh_token_2", TimeSpan.FromSeconds(60));
+ _oAuth2Client.Setup(p => p.RequestToken()).Returns(firstToken);
+ _provider.Refresh();
+ Assert.Equal(firstToken.AccessToken, _provider.Password);
+ Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value);
+ _oAuth2Client.Reset();
+ System.Threading.Thread.Sleep(1000);
+
+ _oAuth2Client.Setup(p => p.RefreshToken(firstToken)).Returns(refreshedToken);
+ _provider.Refresh();
+ Assert.Equal(refreshedToken.AccessToken, _provider.Password);
+ Assert.Equal(refreshedToken.ExpiresIn, _provider.ValidUntil.Value);
+
+ }
+
+ [Fact]
+ public void ShouldRequestTokenWhenRefreshTokenNotAvailable()
+ {
+ IToken firstToken = newToken("the_access_token", null, TimeSpan.FromSeconds(1));
+ IToken refreshedToken = newToken("the_access_token2", null, TimeSpan.FromSeconds(1));
+ _oAuth2Client.SetupSequence(p => p.RequestToken())
+ .Returns(firstToken)
+ .Returns(refreshedToken);
+ _provider.Refresh();
+ Assert.Equal(firstToken.AccessToken, _provider.Password);
+ Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value);
+
+ _provider.Refresh();
+ Assert.Equal(refreshedToken.AccessToken, _provider.Password);
+ Assert.Equal(refreshedToken.ExpiresIn, _provider.ValidUntil.Value);
+ Mock.Verify(_oAuth2Client);
+ }
+
+ private static Token newToken(string access_token, TimeSpan expiresIn)
+ {
+ var token = new JsonToken(access_token, string.Empty, expiresIn);
+ return new Token(token);
+ }
+
+ private static Token newToken(string access_token, string refresh_token, TimeSpan expiresIn)
+ {
+ JsonToken token = new JsonToken(access_token, refresh_token, expiresIn);
+ return new Token(token);
+ }
+ }
+}
diff --git a/projects/Unit/TestTimerBasedCredentialRefresher.cs b/projects/Unit/TestTimerBasedCredentialRefresher.cs
new file mode 100644
index 0000000000..ce5047ba37
--- /dev/null
+++ b/projects/Unit/TestTimerBasedCredentialRefresher.cs
@@ -0,0 +1,97 @@
+// This source code is dual-licensed under the Apache License, version
+// 2.0, and the Mozilla Public License, version 2.0.
+//
+// The APL v2.0:
+//
+//---------------------------------------------------------------------------
+// Copyright (c) 2007-2020 VMware, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//---------------------------------------------------------------------------
+//
+// The MPL v2.0:
+//
+//---------------------------------------------------------------------------
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+//
+// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
+//---------------------------------------------------------------------------
+
+using System;
+using System.Threading.Tasks;
+using Moq;
+using Xunit;
+
+namespace RabbitMQ.Client.Unit
+{
+ public class TestTimerBasedCredentialsRefresher
+ {
+ protected TimerBasedCredentialRefresher _refresher;
+ protected Mock _credentialsProvider;
+ protected Mock _callback = new Mock();
+
+ public TestTimerBasedCredentialsRefresher()
+ {
+ _refresher = new TimerBasedCredentialRefresher();
+ _credentialsProvider = new Mock();
+ }
+
+ [Fact]
+ public void TestRegister()
+ {
+ _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1));
+ Assert.True(_credentialsProvider.Object == _refresher.Register(_credentialsProvider.Object, _callback.Object));
+ Assert.True(_refresher.Unregister(_credentialsProvider.Object));
+ }
+
+ [Fact]
+ public void TestDoNotRegisterWhenHasNoExpiry()
+ {
+
+ _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.Zero);
+ _refresher.Register(_credentialsProvider.Object, _callback.Object);
+ Assert.False(_refresher.Unregister(_credentialsProvider.Object));
+ _credentialsProvider.Verify();
+ }
+
+ [Fact]
+ public async void TestRefreshToken()
+ {
+ _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1));
+ _credentialsProvider.Setup(p => p.Password).Returns("the-token").Verifiable();
+ _callback.Setup(p => p.Invoke(true));
+ _refresher.Register(_credentialsProvider.Object, _callback.Object);
+ await Task.Delay(1 * 1000);
+
+ _credentialsProvider.Verify();
+ _callback.Verify();
+ }
+
+ [Fact]
+ public async void TestRefreshTokenFailed()
+ {
+ _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1));
+ _credentialsProvider.SetupSequence(p => p.Password)
+ .Returns("the-token")
+ .Throws(new Exception());
+ _callback.Setup(p => p.Invoke(false));
+ _refresher.Register(_credentialsProvider.Object, _callback.Object);
+ await Task.Delay(1 * 1000);
+
+ _credentialsProvider.Verify();
+ _callback.Verify();
+ }
+ }
+}
diff --git a/projects/Unit/TestUpdateSecret.cs b/projects/Unit/TestUpdateSecret.cs
index 329e50446d..b81ef5ce15 100644
--- a/projects/Unit/TestUpdateSecret.cs
+++ b/projects/Unit/TestUpdateSecret.cs
@@ -29,56 +29,20 @@
// Copyright (c) 2007-2020 VMware, Inc. All rights reserved.
//---------------------------------------------------------------------------
-using System;
-using System.Text;
-using RabbitMQ.Client.Framing.Impl;
-using Xunit;
using Xunit.Abstractions;
namespace RabbitMQ.Client.Unit
{
-
public class TestUpdateSecret : IntegrationFixture
{
public TestUpdateSecret(ITestOutputHelper output) : base(output)
{
}
- [Fact]
+ [IgnoreOnVersionsEarlierThan(3, 8)]
public void TestUpdatingConnectionSecret()
{
- if (!RabbitMQ380OrHigher())
- {
- Console.WriteLine("Not connected to RabbitMQ 3.8 or higher, skipping test");
- return;
- }
-
_conn.UpdateSecret("new-secret", "Test Case");
-
- Assert.Equal("new-secret", ((AutorecoveringConnection)_conn).Password);
- }
-
- private bool RabbitMQ380OrHigher()
- {
- System.Collections.Generic.IDictionary properties = _conn.ServerProperties;
-
- if (properties.TryGetValue("version", out object versionVal))
- {
- string versionStr = Encoding.UTF8.GetString((byte[])versionVal);
-
- int dashIdx = versionStr.IndexOf('-');
- if (dashIdx > 0)
- {
- versionStr = versionStr.Remove(dashIdx);
- }
-
- if (Version.TryParse(versionStr, out Version version))
- {
- return version >= new Version(3, 8);
- }
- }
-
- return false;
}
}
}
diff --git a/projects/Unit/Unit.csproj b/projects/Unit/Unit.csproj
index d64fa1f8d8..949d0ab78d 100644
--- a/projects/Unit/Unit.csproj
+++ b/projects/Unit/Unit.csproj
@@ -17,6 +17,7 @@
+
@@ -27,12 +28,13 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
+
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
+
+
+