diff --git a/Samples/Client/Client_Connection_Samples.cs b/Samples/Client/Client_Connection_Samples.cs index 84bb3c536..7ade8764f 100644 --- a/Samples/Client/Client_Connection_Samples.cs +++ b/Samples/Client/Client_Connection_Samples.cs @@ -498,14 +498,24 @@ public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEv throw new InvalidOperationException("Wrong authentication method"); } - await eventArgs.SendAsync("initial context token"u8.ToArray()); + var sendOptions = new SendMqttEnhancedAuthenticationDataOptions + { + Data = "initial context token"u8.ToArray() + }; + + await eventArgs.SendAsync(sendOptions); var response = await eventArgs.ReceiveAsync(CancellationToken.None); Console.WriteLine($"Received AUTH data from server: {Encoding.UTF8.GetString(response.AuthenticationData)}"); // No further data is required, but we have to fulfil the exchange. - await eventArgs.SendAsync([], CancellationToken.None); + sendOptions = new SendMqttEnhancedAuthenticationDataOptions + { + Data = [] + }; + + await eventArgs.SendAsync(sendOptions, CancellationToken.None); } } } \ No newline at end of file diff --git a/Source/MQTTnet.Tests/Clients/MqttClient/MqttClient_Connection_Tests.cs b/Source/MQTTnet.Tests/Clients/MqttClient/MqttClient_Connection_Tests.cs index 0b92dea89..49268a921 100644 --- a/Source/MQTTnet.Tests/Clients/MqttClient/MqttClient_Connection_Tests.cs +++ b/Source/MQTTnet.Tests/Clients/MqttClient/MqttClient_Connection_Tests.cs @@ -54,7 +54,7 @@ public async Task ConnectTimeout_Throws_Exception() var disconnectHandlerCalled = false; try { - client.DisconnectedAsync += args => + client.DisconnectedAsync += _ => { disconnectHandlerCalled = true; return CompletedTask.Instance; @@ -164,14 +164,24 @@ public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEv throw new InvalidOperationException("Wrong authentication method"); } - await eventArgs.SendAsync("initial context token"u8.ToArray()); + var sendOptions = new SendMqttEnhancedAuthenticationDataOptions + { + Data = "initial context token"u8.ToArray() + }; - var response = await eventArgs.ReceiveAsync(CancellationToken.None); + await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken); + + var response = await eventArgs.ReceiveAsync(eventArgs.CancellationToken); Assert.AreEqual(Encoding.UTF8.GetString(response.AuthenticationData), "reply context token"); // No further data is required, but we have to fulfil the exchange. - await eventArgs.SendAsync([], CancellationToken.None); + sendOptions = new SendMqttEnhancedAuthenticationDataOptions + { + Data = [] + }; + + await eventArgs.SendAsync(sendOptions, eventArgs.CancellationToken); } } @@ -248,10 +258,7 @@ public async Task Return_Non_Success() server.ValidatingConnectionAsync += args => { - args.ResponseUserProperties = new List - { - new MqttUserProperty("Property", "Value") - }; + args.ResponseUserProperties = [new("Property", "Value")]; args.ReasonCode = MqttConnectReasonCode.QuotaExceeded; diff --git a/Source/MQTTnet/MqttClient.cs b/Source/MQTTnet/MqttClient.cs index 2f46c2626..bb3248ff9 100644 --- a/Source/MQTTnet/MqttClient.cs +++ b/Source/MQTTnet/MqttClient.cs @@ -445,7 +445,7 @@ async Task Authenticate(IMqttChannelAdapter channelAdap if (receivedPacket is MqttAuthPacket authPacket) { - await HandleEnhancedAuthentication(authPacket); + await HandleEnhancedAuthentication(authPacket, cancellationToken); continue; } @@ -473,9 +473,9 @@ async Task Authenticate(IMqttChannelAdapter channelAdap return result; } - async Task HandleEnhancedAuthentication(MqttAuthPacket authPacket) + async Task HandleEnhancedAuthentication(MqttAuthPacket authPacket, CancellationToken cancellationToken) { - var eventArgs = new MqttEnhancedAuthenticationEventArgs(authPacket, _adapter); + var eventArgs = new MqttEnhancedAuthenticationEventArgs(authPacket, _adapter, cancellationToken); await Options.EnhancedAuthenticationHandler.HandleEnhancedAuthenticationAsync(eventArgs); } @@ -655,7 +655,7 @@ Task OnConnected(MqttClientConnectResult connectResult) return _events.ConnectedEvent.InvokeAsync(eventArgs); } - Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket) + Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket, CancellationToken cancellationToken) { if (Options.EnhancedAuthenticationHandler == null) { @@ -667,12 +667,13 @@ Task ProcessReceivedAuthPacket(MqttAuthPacket authPacket) { Reason = MqttClientDisconnectOptionsReason.ImplementationSpecificError, ReasonString = "Unable to handle AUTH packet" - }); + }, + cancellationToken); return CompletedTask.Instance; } - var eventArgs = new MqttEnhancedAuthenticationEventArgs(authPacket, _adapter); + var eventArgs = new MqttEnhancedAuthenticationEventArgs(authPacket, _adapter, cancellationToken); return Options.EnhancedAuthenticationHandler.HandleEnhancedAuthenticationAsync(eventArgs); } @@ -981,7 +982,7 @@ async Task TryProcessReceivedPacket(MqttPacket packet, CancellationToken cancell 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); diff --git a/Source/MQTTnet/Options/MqttEnhancedAuthenticationEventArgs.cs b/Source/MQTTnet/Options/MqttEnhancedAuthenticationEventArgs.cs index a175eac83..0b9ac404f 100644 --- a/Source/MQTTnet/Options/MqttEnhancedAuthenticationEventArgs.cs +++ b/Source/MQTTnet/Options/MqttEnhancedAuthenticationEventArgs.cs @@ -16,43 +16,41 @@ namespace MQTTnet; public class MqttEnhancedAuthenticationEventArgs : EventArgs { readonly IMqttChannelAdapter _channelAdapter; + readonly MqttAuthPacket _initialAuthPacket; - public MqttEnhancedAuthenticationEventArgs(MqttAuthPacket initialAuthPacket, IMqttChannelAdapter channelAdapter) + public MqttEnhancedAuthenticationEventArgs(MqttAuthPacket initialAuthPacket, IMqttChannelAdapter channelAdapter, CancellationToken cancellationToken) { - ArgumentNullException.ThrowIfNull(initialAuthPacket); - + _initialAuthPacket = initialAuthPacket ?? throw new ArgumentNullException(nameof(initialAuthPacket)); _channelAdapter = channelAdapter ?? throw new ArgumentNullException(nameof(channelAdapter)); - ReasonCode = initialAuthPacket.ReasonCode; - ReasonString = initialAuthPacket.ReasonString; - AuthenticationMethod = initialAuthPacket.AuthenticationMethod; - AuthenticationData = initialAuthPacket.AuthenticationData; - UserProperties = initialAuthPacket.UserProperties; + CancellationToken = cancellationToken; } /// /// Gets the authentication data. /// Hint: MQTT 5 feature only. /// - public byte[] AuthenticationData { get; } + public byte[] AuthenticationData => _initialAuthPacket.AuthenticationData; /// /// Gets the authentication method. /// Hint: MQTT 5 feature only. /// - public string AuthenticationMethod { get; } + public string AuthenticationMethod => _initialAuthPacket.AuthenticationMethod; + + public CancellationToken CancellationToken { get; } /// /// Gets the reason code. /// Hint: MQTT 5 feature only. /// - public MqttAuthenticateReasonCode ReasonCode { get; } + public MqttAuthenticateReasonCode ReasonCode => _initialAuthPacket.ReasonCode; /// /// Gets the reason string. /// Hint: MQTT 5 feature only. /// - public string ReasonString { get; } + public string ReasonString => _initialAuthPacket.ReasonString; /// /// Gets the user properties. @@ -63,18 +61,25 @@ public MqttEnhancedAuthenticationEventArgs(MqttAuthPacket initialAuthPacket, IMq /// The feature is very similar to the HTTP header concept. /// Hint: MQTT 5 feature only. /// - public List UserProperties { get; } + public List UserProperties => _initialAuthPacket.UserProperties; - public async Task ReceiveAsync(CancellationToken cancellationToken = default) + public async Task ReceiveAsync(CancellationToken cancellationToken = default) { var receivedPacket = await _channelAdapter.ReceivePacketAsync(cancellationToken).ConfigureAwait(false); if (receivedPacket is MqttAuthPacket authPacket) { - return authPacket; + return new ReceiveMqttEnhancedAuthenticationDataResult + { + AuthenticationData = authPacket.AuthenticationData, + AuthenticationMethod = authPacket.AuthenticationMethod, + ReasonString = authPacket.ReasonString, + ReasonCode = authPacket.ReasonCode, + UserProperties = authPacket.UserProperties + }; } - if (receivedPacket is MqttConnAckPacket connAckPacket) + if (receivedPacket is MqttConnAckPacket) { throw new InvalidOperationException("The enhanced authentication handler must not wait for the CONNACK packet."); } @@ -82,15 +87,22 @@ public async Task ReceiveAsync(CancellationToken cancellationTok throw new MqttProtocolViolationException("Received other packet than AUTH while authenticating."); } - public Task SendAsync(byte[] authenticationData, CancellationToken cancellationToken = default) + public Task SendAsync(SendMqttEnhancedAuthenticationDataOptions options, CancellationToken cancellationToken = default) { - return _channelAdapter.SendPacketAsync( - new MqttAuthPacket - { - ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication, - AuthenticationMethod = AuthenticationMethod, - AuthenticationData = authenticationData - }, - cancellationToken); + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + var authPacket = new MqttAuthPacket + { + ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication, + AuthenticationMethod = AuthenticationMethod, + AuthenticationData = options.Data, + UserProperties = options.UserProperties, + ReasonString = options.ReasonString + }; + + return _channelAdapter.SendPacketAsync(authPacket, cancellationToken); } } \ No newline at end of file diff --git a/Source/MQTTnet/Options/ReceiveMqttEnhancedAuthenticationDataResult.cs b/Source/MQTTnet/Options/ReceiveMqttEnhancedAuthenticationDataResult.cs new file mode 100644 index 000000000..f2dc8cd20 --- /dev/null +++ b/Source/MQTTnet/Options/ReceiveMqttEnhancedAuthenticationDataResult.cs @@ -0,0 +1,22 @@ +// 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 MQTTnet.Packets; +using MQTTnet.Protocol; + +namespace MQTTnet; + +public sealed class ReceiveMqttEnhancedAuthenticationDataResult +{ + public byte[] AuthenticationData { get; init; } + + public string AuthenticationMethod { get; init; } + + public MqttAuthenticateReasonCode ReasonCode { get; init; } + + public string ReasonString { get; init; } + + public List UserProperties { get; init; } +} \ No newline at end of file diff --git a/Source/MQTTnet/Options/SendMqttEnhancedAuthenticationDataOptions.cs b/Source/MQTTnet/Options/SendMqttEnhancedAuthenticationDataOptions.cs new file mode 100644 index 000000000..282b189d5 --- /dev/null +++ b/Source/MQTTnet/Options/SendMqttEnhancedAuthenticationDataOptions.cs @@ -0,0 +1,17 @@ +// 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 MQTTnet.Packets; + +namespace MQTTnet; + +public sealed class SendMqttEnhancedAuthenticationDataOptions +{ + public byte[] Data { get; init; } + + public string ReasonString { get; init; } + + public List UserProperties { get; init; } +} \ No newline at end of file