mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Backport of: 2132 Fix support for enhanced authentication (#2171)
This commit is contained in:
@@ -1 +1,3 @@
|
||||
* Client: Fixed wrong timeout for keep alive check (thanks to @Erw1nT, #2129)
|
||||
* Client: Fixed wrong timeout for keep alive check (thanks to @Erw1nT, #2129)
|
||||
* Client: Fixed extended authentication (thanks to @chkr1011, #2132).
|
||||
* Server: Fixed extended authentication (thanks to @chkr1011, #2132).
|
||||
@@ -8,10 +8,13 @@
|
||||
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Extensions.WebSocket4Net;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Samples.Helpers;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
@@ -100,6 +103,105 @@ public static class Client_Connection_Samples
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_Extended_Authentication()
|
||||
{
|
||||
/*
|
||||
* This sample uses enhanced (aka extended) authentication (Kerberos) when creating the connection.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Server part...
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttFactory();
|
||||
|
||||
var serverOptions = mqttFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
var server = mqttFactory.CreateMqttServer(serverOptions);
|
||||
|
||||
server.ValidatingConnectionAsync += async args =>
|
||||
{
|
||||
if (args.AuthenticationMethod == "GS2-KRB5")
|
||||
{
|
||||
var result = await args.SendExtendedAuthenticationExchangeDataAsync(new MqttExtendedAuthenticationExchangeOptions());
|
||||
|
||||
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
|
||||
|
||||
var authOptions = mqttFactory.CreateMqttExtendedAuthenticationExchangeOptionsBuilder().WithAuthenticationData("reply context token").Build();
|
||||
|
||||
result = await args.SendExtendedAuthenticationExchangeDataAsync(authOptions);
|
||||
|
||||
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
|
||||
|
||||
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
|
||||
|
||||
// Authentication DONE!
|
||||
args.ReasonCode = MqttConnectReasonCode.Success; // Also the default!
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
||||
}
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
/*
|
||||
* Client part...
|
||||
*/
|
||||
|
||||
var mqttClientFactory = new MqttFactory();
|
||||
|
||||
// Use Kerberos sample from the MQTT RFC.
|
||||
var kerberosAuthenticationHandler = new SampleClientKerberosAuthenticationHandler();
|
||||
|
||||
var clientOptions = mqttClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithExtendedAuthenticationExchangeHandler(kerberosAuthenticationHandler)
|
||||
.WithAuthentication("GS2-KRB5", Array.Empty<byte>())
|
||||
.Build();
|
||||
|
||||
var client = mqttClientFactory.CreateMqttClient();
|
||||
|
||||
var result = await client.ConnectAsync(clientOptions);
|
||||
|
||||
Console.WriteLine($"Client connect result: {result.ResultCode}");
|
||||
}
|
||||
|
||||
class SampleClientKerberosAuthenticationHandler : IMqttExtendedAuthenticationExchangeHandler
|
||||
{
|
||||
private readonly Queue<byte[]> clientTokens = new Queue<byte[]>();
|
||||
|
||||
public SampleClientKerberosAuthenticationHandler()
|
||||
{
|
||||
// The Kerberos sample from the MQTT RFC sends two client tokens after the initial empty token.
|
||||
clientTokens.Enqueue(Encoding.UTF8.GetBytes("initial context token"));
|
||||
clientTokens.Enqueue(Array.Empty<byte>());
|
||||
}
|
||||
|
||||
public async Task HandleRequestAsync(MqttExtendedAuthenticationExchangeContext context)
|
||||
{
|
||||
if (context.AuthenticationMethod != "GS2-KRB5")
|
||||
{
|
||||
throw new InvalidOperationException("Wrong authentication method");
|
||||
}
|
||||
|
||||
Console.WriteLine($"Received AUTH data from server: {Encoding.UTF8.GetString(context.AuthenticationData)}");
|
||||
|
||||
if (!clientTokens.TryDequeue(out var clientToken))
|
||||
{
|
||||
throw new InvalidOperationException("No more client tokens available");
|
||||
}
|
||||
|
||||
var sendOptions = new MqttExtendedAuthenticationExchangeData
|
||||
{
|
||||
AuthenticationData = clientToken
|
||||
};
|
||||
|
||||
await context.Client.SendExtendedAuthenticationExchangeDataAsync(sendOptions);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Connect_Client_Using_MQTTv5()
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -167,6 +168,91 @@ namespace MQTTnet.Tests.Clients.MqttClient
|
||||
}
|
||||
}
|
||||
|
||||
class TestClientKerberosAuthenticationHandler : IMqttExtendedAuthenticationExchangeHandler
|
||||
{
|
||||
public static IReadOnlyList<string> ServerTokens { get; } = new List<string>()
|
||||
{
|
||||
string.Empty,
|
||||
"reply context token"
|
||||
};
|
||||
|
||||
public static IReadOnlyList<string> ClientTokens { get; } = new List<string>()
|
||||
{
|
||||
string.Empty,
|
||||
"initial context token",
|
||||
string.Empty
|
||||
};
|
||||
|
||||
int tokenCursor;
|
||||
|
||||
public async Task HandleRequestAsync(MqttExtendedAuthenticationExchangeContext context)
|
||||
{
|
||||
if (context.AuthenticationMethod != "GS2-KRB5")
|
||||
{
|
||||
throw new InvalidOperationException("Wrong authentication method");
|
||||
}
|
||||
|
||||
Assert.IsTrue(tokenCursor < ServerTokens.Count, "No more server tokens were expected.");
|
||||
var expectedToken = ServerTokens[tokenCursor++];
|
||||
Assert.AreEqual(expectedToken, Encoding.UTF8.GetString(context.AuthenticationData));
|
||||
|
||||
var sendOptions = new MqttExtendedAuthenticationExchangeData
|
||||
{
|
||||
AuthenticationData = Encoding.UTF8.GetBytes(ClientTokens[tokenCursor])
|
||||
};
|
||||
|
||||
await context.Client.SendExtendedAuthenticationExchangeDataAsync(sendOptions, context.CancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task Use_Extended_Authentication()
|
||||
{
|
||||
// Use Kerberos sample from the MQTT RFC.
|
||||
var kerberosAuthenticationHandler = new TestClientKerberosAuthenticationHandler();
|
||||
|
||||
using (var testEnvironment = CreateTestEnvironment(MqttProtocolVersion.V500))
|
||||
{
|
||||
var server = await testEnvironment.StartServer();
|
||||
|
||||
server.ValidatingConnectionAsync += async args =>
|
||||
{
|
||||
if (args.AuthenticationMethod == "GS2-KRB5")
|
||||
{
|
||||
var authenticationData = args.AuthenticationData;
|
||||
string expectedToken;
|
||||
int tokenCursor;
|
||||
for (tokenCursor = 0; tokenCursor < TestClientKerberosAuthenticationHandler.ServerTokens.Count; tokenCursor++)
|
||||
{
|
||||
expectedToken = TestClientKerberosAuthenticationHandler.ClientTokens[tokenCursor];
|
||||
Assert.AreEqual(expectedToken, Encoding.UTF8.GetString(authenticationData), "The received client token is not correct.");
|
||||
|
||||
var serverToken = TestClientKerberosAuthenticationHandler.ServerTokens[tokenCursor];
|
||||
var authOptions = testEnvironment.Factory.CreateMqttExtendedAuthenticationExchangeOptionsBuilder().WithAuthenticationData(serverToken).Build();
|
||||
var result = await args.SendExtendedAuthenticationExchangeDataAsync(authOptions);
|
||||
|
||||
authenticationData = result.AuthenticationData;
|
||||
}
|
||||
|
||||
expectedToken = TestClientKerberosAuthenticationHandler.ClientTokens[tokenCursor];
|
||||
Assert.AreEqual(expectedToken, Encoding.UTF8.GetString(authenticationData), "The received client token is not correct.");
|
||||
args.ResponseAuthenticationData = Encoding.UTF8.GetBytes("outcome of authentication");
|
||||
args.ReasonCode = MqttConnectReasonCode.Success;
|
||||
args.ReasonString = "Authentication successful";
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
||||
}
|
||||
};
|
||||
|
||||
var clientOptions = testEnvironment.CreateDefaultClientOptionsBuilder().WithAuthentication("GS2-KRB5", new byte[0]).WithExtendedAuthenticationExchangeHandler(kerberosAuthenticationHandler);
|
||||
var client = await testEnvironment.ConnectClient(clientOptions);
|
||||
|
||||
Assert.IsTrue(client.IsConnected);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task No_Unobserved_Exception()
|
||||
{
|
||||
|
||||
+8
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
@@ -11,7 +12,7 @@ namespace MQTTnet.Client
|
||||
{
|
||||
public class MqttExtendedAuthenticationExchangeContext
|
||||
{
|
||||
public MqttExtendedAuthenticationExchangeContext(MqttAuthPacket authPacket, MqttClient client)
|
||||
public MqttExtendedAuthenticationExchangeContext(MqttAuthPacket authPacket, MqttClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
if (authPacket == null) throw new ArgumentNullException(nameof(authPacket));
|
||||
|
||||
@@ -22,6 +23,7 @@ namespace MQTTnet.Client
|
||||
UserProperties = authPacket.UserProperties;
|
||||
|
||||
Client = client ?? throw new ArgumentNullException(nameof(client));
|
||||
CancellationToken = cancellationToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,5 +60,10 @@ namespace MQTTnet.Client
|
||||
public List<MqttUserProperty> UserProperties { get; }
|
||||
|
||||
public MqttClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The cancellation token passed to the operation being authenticated (e.g. ConnectAsync).
|
||||
/// </summary>
|
||||
public CancellationToken CancellationToken { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ namespace MQTTnet.Client
|
||||
|
||||
public bool IsConnected => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connected;
|
||||
|
||||
bool IsConnecting => (MqttClientConnectionStatus)_connectionStatus == MqttClientConnectionStatus.Connecting;
|
||||
|
||||
public MqttClientOptions Options { get; private set; }
|
||||
|
||||
public async Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default)
|
||||
@@ -304,7 +306,7 @@ namespace MQTTnet.Client
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotConnected();
|
||||
ThrowIfNotConnectedOrConnecting();
|
||||
|
||||
var authPacket = new MqttAuthPacket
|
||||
{
|
||||
@@ -454,27 +456,30 @@ namespace MQTTnet.Client
|
||||
var connectPacket = MqttPacketFactories.Connect.Create(options);
|
||||
await Send(connectPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var receivedPacket = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
switch (receivedPacket)
|
||||
for (; ; )
|
||||
{
|
||||
case MqttConnAckPacket connAckPacket:
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var receivedPacket = await Receive(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (receivedPacket is MqttAuthPacket authPacket)
|
||||
{
|
||||
await ProcessReceivedAuthPacket(authPacket, cancellationToken).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (receivedPacket is MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
result = MqttClientResultFactory.ConnectResult.Create(connAckPacket, channelAdapter.PacketFormatterAdapter.ProtocolVersion);
|
||||
break;
|
||||
}
|
||||
case MqttAuthPacket _:
|
||||
|
||||
if (receivedPacket != null)
|
||||
{
|
||||
throw new NotSupportedException("Extended authentication handler is not yet supported");
|
||||
}
|
||||
case null:
|
||||
{
|
||||
throw new MqttCommunicationException("Connection closed.");
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException($"Received an unexpected MQTT packet ({receivedPacket}).");
|
||||
throw new MqttProtocolViolationException($"Received other packet than CONNACK or AUTH while connecting ({receivedPacket}).");
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException("Connection closed.");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -678,10 +683,37 @@ namespace MQTTnet.Client
|
||||
return _events.ConnectedEvent.InvokeAsync(eventArgs);
|
||||
}
|
||||
|
||||
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket)
|
||||
Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket, CancellationToken cancellationToken)
|
||||
{
|
||||
var extendedAuthenticationExchangeHandler = Options.ExtendedAuthenticationExchangeHandler;
|
||||
return extendedAuthenticationExchangeHandler != null ? extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this)) : CompletedTask.Instance;
|
||||
if (extendedAuthenticationExchangeHandler == null)
|
||||
{
|
||||
// From RFC:
|
||||
// The Client can close the connection at any point in this process. It MAY send a DISCONNECT packet before doing so.
|
||||
// The Server can reject the authentication at any point in this process. It MAY send a CONNACK with a Reason Code of
|
||||
// 0x80 or above as described in section 4.13, and MUST close the Network Connection [MQTT-4.12.0-4].
|
||||
//
|
||||
// If the re-authentication fails, the Client or Server SHOULD send DISCONNECT with an appropriate Reason Code
|
||||
// as described in section 4.13, and MUST close the Network Connection [MQTT-4.12.1-2].
|
||||
//
|
||||
// Since we have no handler there is no chance to fulfil the initial authentication or re-authentication requests.
|
||||
|
||||
if (IsConnecting)
|
||||
{
|
||||
throw new MqttCommunicationException("No handler was provided to handle the extended authentication exchange.");
|
||||
}
|
||||
|
||||
_ = DisconnectAsync(new MqttClientDisconnectOptions
|
||||
{
|
||||
Reason = MqttClientDisconnectOptionsReason.ImplementationSpecificError,
|
||||
ReasonString = "Unable to handle AUTH packet"
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
return CompletedTask.Instance;
|
||||
}
|
||||
|
||||
return extendedAuthenticationExchangeHandler.HandleRequestAsync(new MqttExtendedAuthenticationExchangeContext(authPacket, this, cancellationToken));
|
||||
}
|
||||
|
||||
Task ProcessReceivedDisconnectPacket(MqttDisconnectPacket disconnectPacket)
|
||||
@@ -935,6 +967,14 @@ namespace MQTTnet.Client
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfNotConnectedOrConnecting()
|
||||
{
|
||||
if (!(IsConnected || IsConnecting))
|
||||
{
|
||||
ThrowNotConnectedOrConnecting();
|
||||
}
|
||||
}
|
||||
|
||||
static void ThrowIfOptionsInvalid(MqttClientOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
@@ -958,6 +998,11 @@ namespace MQTTnet.Client
|
||||
throw new MqttClientNotConnectedException();
|
||||
}
|
||||
|
||||
static void ThrowNotConnectedOrConnecting()
|
||||
{
|
||||
throw new MqttCommunicationException("The MQTT client is not connected or connecting.");
|
||||
}
|
||||
|
||||
void TryInitiateDisconnect()
|
||||
{
|
||||
lock (_disconnectLock)
|
||||
@@ -992,7 +1037,7 @@ namespace MQTTnet.Client
|
||||
await ProcessReceivedDisconnectPacket(disconnectPacket).ConfigureAwait(false);
|
||||
break;
|
||||
case MqttAuthPacket authPacket:
|
||||
await ProcessReceivedAuthPacket(authPacket).ConfigureAwait(false);
|
||||
await ProcessReceivedAuthPacket(authPacket, cancellationToken).ConfigureAwait(false);
|
||||
break;
|
||||
case MqttPingRespPacket _:
|
||||
_packetDispatcher.TryDispatch(packet);
|
||||
|
||||
@@ -68,8 +68,13 @@ namespace MQTTnet.Client
|
||||
Throw(nameof(options.AuthenticationMethod));
|
||||
}
|
||||
|
||||
if (options.ExtendedAuthenticationExchangeHandler != null)
|
||||
{
|
||||
Throw(nameof(options.ExtendedAuthenticationExchangeHandler));
|
||||
}
|
||||
|
||||
// Will relevant properties.
|
||||
|
||||
|
||||
if (options.WillPayloadFormatIndicator != MqttPayloadFormatIndicator.Unspecified)
|
||||
{
|
||||
Throw(nameof(options.WillPayloadFormatIndicator));
|
||||
|
||||
@@ -135,6 +135,11 @@ namespace MQTTnet
|
||||
return new MqttClient(clientAdapterFactory, logger);
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory CreateMqttExtendedAuthenticationExchangeOptionsBuilder()
|
||||
{
|
||||
return new MqttExtendedAuthenticationExchangeOptionsFactory();
|
||||
}
|
||||
|
||||
public MqttServer CreateMqttServer(MqttServerOptions options)
|
||||
{
|
||||
return CreateMqttServer(options, DefaultLogger);
|
||||
|
||||
@@ -7,11 +7,14 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
@@ -189,5 +192,54 @@ namespace MQTTnet.Server
|
||||
/// A value of 0 indicates that the value is not used.
|
||||
/// </summary>
|
||||
public uint WillDelayInterval => _connectPacket.WillDelayInterval;
|
||||
|
||||
|
||||
public async Task<MqttExtendedAuthenticationExchangeResult> SendExtendedAuthenticationExchangeDataAsync(
|
||||
MqttExtendedAuthenticationExchangeOptions options,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
var requestAuthPacket = new MqttAuthPacket
|
||||
{
|
||||
// From RFC: If the initial CONNECT packet included an Authentication Method property then all AUTH packets,
|
||||
// and any successful CONNACK packet MUST include an Authentication Method Property with the same value as in the CONNECT packet [MQTT-4.12.0-5].
|
||||
AuthenticationMethod = AuthenticationMethod,
|
||||
|
||||
// The reason code will stay at continue all the time when connecting. The server will respond with the
|
||||
// CONNACK packet when authentication is done!
|
||||
ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication,
|
||||
|
||||
AuthenticationData = options.AuthenticationData,
|
||||
ReasonString = options.ReasonString,
|
||||
UserProperties = options.UserProperties
|
||||
};
|
||||
|
||||
await ChannelAdapter.SendPacketAsync(requestAuthPacket, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var responsePacket = await ChannelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (responsePacket == null)
|
||||
{
|
||||
throw new MqttCommunicationException("The client closed the connection.");
|
||||
}
|
||||
|
||||
if (responsePacket is MqttAuthPacket responseAuthPacket)
|
||||
{
|
||||
if (!string.Equals(AuthenticationMethod, responseAuthPacket.AuthenticationMethod, StringComparison.Ordinal))
|
||||
{
|
||||
throw new MqttProtocolViolationException("The authentication method cannot change while authenticating the client.");
|
||||
}
|
||||
|
||||
return MqttExtendedAuthenticationExchangeResultFactory.Create(responseAuthPacket);
|
||||
}
|
||||
|
||||
if (responsePacket is MqttDisconnectPacket disconnectPacket)
|
||||
{
|
||||
return MqttExtendedAuthenticationExchangeResultFactory.Create(disconnectPacket);
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Received other packet than AUTH while authenticating.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Packets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttExtendedAuthenticationExchangeOptions
|
||||
{
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttExtendedAuthenticationExchangeOptionsFactory
|
||||
{
|
||||
readonly MqttExtendedAuthenticationExchangeOptions _options = new MqttExtendedAuthenticationExchangeOptions();
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptions Build()
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory WithAuthenticationData(byte[] authenticationData)
|
||||
{
|
||||
_options.AuthenticationData = authenticationData;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory WithAuthenticationData(string authenticationData)
|
||||
{
|
||||
if (authenticationData == null)
|
||||
{
|
||||
_options.AuthenticationData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_options.AuthenticationData = Encoding.UTF8.GetBytes(authenticationData);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory WithReasonString(string reasonString)
|
||||
{
|
||||
_options.ReasonString = reasonString;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory WithUserProperties(List<MqttUserProperty> userProperties)
|
||||
{
|
||||
_options.UserProperties = userProperties;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public MqttExtendedAuthenticationExchangeOptionsFactory WithUserProperty(string name, string value)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (_options.UserProperties == null)
|
||||
{
|
||||
_options.UserProperties = new List<MqttUserProperty>();
|
||||
}
|
||||
|
||||
_options.UserProperties.Add(new MqttUserProperty(name, value));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Packets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public sealed class MqttExtendedAuthenticationExchangeResult
|
||||
{
|
||||
public string ReasonString { get; set; }
|
||||
|
||||
public List<MqttUserProperty> UserProperties { get; set; }
|
||||
|
||||
public byte[] AuthenticationData { get; set; }
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Packets;
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Server
|
||||
{
|
||||
public static class MqttExtendedAuthenticationExchangeResultFactory
|
||||
{
|
||||
public static MqttExtendedAuthenticationExchangeResult Create(MqttAuthPacket authPacket)
|
||||
{
|
||||
if (authPacket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(authPacket));
|
||||
}
|
||||
|
||||
return new MqttExtendedAuthenticationExchangeResult
|
||||
{
|
||||
AuthenticationData = authPacket.AuthenticationData,
|
||||
|
||||
ReasonString = authPacket.ReasonString,
|
||||
UserProperties = authPacket.UserProperties
|
||||
};
|
||||
}
|
||||
|
||||
public static MqttExtendedAuthenticationExchangeResult Create(MqttDisconnectPacket disconnectPacket)
|
||||
{
|
||||
if (disconnectPacket == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(disconnectPacket));
|
||||
}
|
||||
|
||||
return new MqttExtendedAuthenticationExchangeResult
|
||||
{
|
||||
AuthenticationData = null,
|
||||
ReasonString = disconnectPacket.ReasonString,
|
||||
UserProperties = disconnectPacket.UserProperties
|
||||
|
||||
// SessionExpiryInterval makes no sense because the connection is not yet made!
|
||||
// ServerReferences makes no sense when the client initiated a DISCONNECT!
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user