diff --git a/Source/MQTTnet.Server/MQTTnet.Server.csproj b/Source/MQTTnet.Server/MQTTnet.Server.csproj index edf5e27..bbd3ceb 100644 --- a/Source/MQTTnet.Server/MQTTnet.Server.csproj +++ b/Source/MQTTnet.Server/MQTTnet.Server.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 diff --git a/Source/MQTTnet.Server/Web/BasicAuthenticationHandler.cs b/Source/MQTTnet.Server/Web/BasicAuthenticationHandler.cs index 2525451..8b5ed21 100644 --- a/Source/MQTTnet.Server/Web/BasicAuthenticationHandler.cs +++ b/Source/MQTTnet.Server/Web/BasicAuthenticationHandler.cs @@ -18,7 +18,12 @@ namespace MQTTnet.Server.Web { private readonly ILogger _logger; - public AuthenticationHandler(IOptionsMonitor options, ILoggerFactory loggerFactory, UrlEncoder encoder, ISystemClock clock, ILogger logger) + public AuthenticationHandler( + IOptionsMonitor options, + ILoggerFactory loggerFactory, + UrlEncoder encoder, + ISystemClock clock, + ILogger logger) : base(options, loggerFactory, encoder, clock) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -26,11 +31,9 @@ namespace MQTTnet.Server.Web protected override async Task HandleAuthenticateAsync() { - await Task.CompletedTask; - if (!Request.Headers.ContainsKey(HeaderNames.Authorization)) { - return AuthenticateResult.Fail("Missing Authorization Header"); + return AuthenticateResult.NoResult(); } try @@ -83,6 +86,10 @@ namespace MQTTnet.Server.Web return AuthenticateResult.Fail(exception); } + finally + { + await Task.CompletedTask; + } } private bool ValidateUser(PythonDictionary context) diff --git a/Source/MQTTnet.Server/Web/Startup.cs b/Source/MQTTnet.Server/Web/Startup.cs index 9c0c06e..d9db0fb 100644 --- a/Source/MQTTnet.Server/Web/Startup.cs +++ b/Source/MQTTnet.Server/Web/Startup.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; @@ -15,6 +17,7 @@ using MQTTnet.Server.Logging; using MQTTnet.Server.Mqtt; using MQTTnet.Server.Scripting; using MQTTnet.Server.Scripting.DataSharing; +using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerUI; namespace MQTTnet.Server.Web @@ -115,17 +118,22 @@ namespace MQTTnet.Server.Web services.AddSwaggerGen(c => { c.DescribeAllEnumsAsStrings(); - c.AddSecurityDefinition("Basic", new OpenApiSecurityScheme + + var securityScheme = new OpenApiSecurityScheme { Scheme = "Basic", Name = HeaderNames.Authorization, Type = SecuritySchemeType.Http, In = ParameterLocation.Header - }); + }; + + c.AddSecurityDefinition("Swagger", securityScheme); + c.AddSecurityRequirement(new OpenApiSecurityRequirement { - [new OpenApiSecurityScheme { Name = "Basic" }] = new List() + [securityScheme] = new List() }); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "MQTTnet.Server API", @@ -145,7 +153,9 @@ namespace MQTTnet.Server.Web }); }); - services.AddAuthentication("Basic").AddScheme("Basic", null); + services.AddAuthentication("Basic") + .AddScheme("Basic", null) + .AddCookie(); } private void ReadMqttSettings(IServiceCollection services) @@ -200,7 +210,7 @@ namespace MQTTnet.Server.Web } else { - context.Response.StatusCode = 400; + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } } else diff --git a/Source/MQTTnet.Server/Web/authorization_handler.py b/Source/MQTTnet.Server/Web/authorization_handler.py index 28cff2c..1cda11f 100644 --- a/Source/MQTTnet.Server/Web/authorization_handler.py +++ b/Source/MQTTnet.Server/Web/authorization_handler.py @@ -2,9 +2,19 @@ """ This function is invoked whenever a user tries to access protected HTTP resources. This function must exist and return a proper value. Otherwise the request is denied. + All authentication data must be sent via using the "Authorization" header. This header + will be parsed and all values will be exposed to the context. """ + header_value = context["header_value"] # The untouched header value. - username = context["username"] - password = context["password"] + scheme = context["scheme"] + parameter = context["parameter"] - context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials. \ No newline at end of file + username = context["username"] # Only set if proper "Basic" authorization is used. + password = context["password"] # Only set if proper "Basic" authorization is used. + + context["is_authenticated"] = True # Change this to _False_ in case of invalid credentials. + + # Example for an API key with proper format. + if header_value == "APIKey 123456": + context["is_authenticated"] = True \ No newline at end of file diff --git a/Source/MQTTnet.Server/appsettings.json b/Source/MQTTnet.Server/appsettings.json index b6bc55e..1008a13 100644 --- a/Source/MQTTnet.Server/appsettings.json +++ b/Source/MQTTnet.Server/appsettings.json @@ -38,10 +38,10 @@ }, "CommunicationTimeout": 15, // In seconds. "ConnectionBacklog": 10, // Set 0 to disable - "EnablePersistentSessions": false, + "EnablePersistentSessions": true, "MaxPendingMessagesPerClient": 250, "RetainedApplicationMessages": { - "Persist": false, + "Persist": true, "Path": "RetainedApplicationMessages.json", "WriteInterval": 10 // In seconds. },