diff --git a/src/WART-Client/WART-Client.csproj b/src/WART-Client/WART-Client.csproj
index 43da9fe..6cee0fe 100755
--- a/src/WART-Client/WART-Client.csproj
+++ b/src/WART-Client/WART-Client.csproj
@@ -21,10 +21,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/WART-Core/Authentication/JWT/JwtServiceCollectionExtension.cs b/src/WART-Core/Authentication/JWT/JwtServiceCollectionExtension.cs
index 0e77b41..553dc96 100755
--- a/src/WART-Core/Authentication/JWT/JwtServiceCollectionExtension.cs
+++ b/src/WART-Core/Authentication/JWT/JwtServiceCollectionExtension.cs
@@ -27,9 +27,12 @@ public static class JwtServiceCollectionExtension
///
/// The service collection to add the middleware to.
/// The secret key used to sign and validate the JWT tokens.
+ /// Optional. When provided, JWT issuer validation is enabled and tokens must match this value.
+ /// Optional. When provided, JWT audience validation is enabled and tokens must match this value.
/// The updated service collection.
/// Thrown if the token key is null or empty.
- public static IServiceCollection AddJwtMiddleware(this IServiceCollection services, string tokenKey)
+ public static IServiceCollection AddJwtMiddleware(this IServiceCollection services, string tokenKey,
+ string validIssuer = null, string validAudience = null)
{
// Validate that the token key is provided
if (string.IsNullOrEmpty(tokenKey))
@@ -64,8 +67,10 @@ public static IServiceCollection AddJwtMiddleware(this IServiceCollection servic
new TokenValidationParameters
{
LifetimeValidator = (before, expires, token, parameters) => expires != null && expires > DateTime.UtcNow,
- ValidateAudience = false,
- ValidateIssuer = false,
+ ValidateAudience = !string.IsNullOrEmpty(validAudience),
+ ValidAudience = validAudience,
+ ValidateIssuer = !string.IsNullOrEmpty(validIssuer),
+ ValidIssuer = validIssuer,
ValidateActor = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
diff --git a/src/WART-Core/Enum/HubType.cs b/src/WART-Core/Enum/HubType.cs
index 2b407a0..cbf3cb2 100755
--- a/src/WART-Core/Enum/HubType.cs
+++ b/src/WART-Core/Enum/HubType.cs
@@ -1,5 +1,7 @@
-// (c) 2021 Francesco Del Re
+// (c) 2021 Francesco Del Re
// This code is licensed under MIT license (see LICENSE.txt for details)
+using System;
+
namespace WART_Core.Enum
{
///
@@ -8,8 +10,19 @@ namespace WART_Core.Enum
public enum HubType
{
///
- /// Simple SignalR hub without authentication
+ /// Simple SignalR hub without authentication.
///
+ ///
+ /// SECURITY WARNING: This mode maps an unauthenticated SignalR hub that broadcasts
+ /// live API request and response payloads to every connected client with no credential check.
+ /// Any anonymous network client can connect and receive sensitive data (request bodies,
+ /// response bodies, HTTP paths). Use or
+ /// instead.
+ /// See https://github.com/engineering87/WART/security/advisories
+ ///
+ [Obsolete("NoAuthentication broadcasts all API events to any anonymous client and is insecure. " +
+ "Use HubType.JwtAuthentication or HubType.CookieAuthentication instead. " +
+ "See https://github.com/engineering87/WART/security/advisories")]
NoAuthentication,
///
diff --git a/src/WART-Core/Hubs/WartHub.cs b/src/WART-Core/Hubs/WartHub.cs
index f069ca6..51dd5d9 100755
--- a/src/WART-Core/Hubs/WartHub.cs
+++ b/src/WART-Core/Hubs/WartHub.cs
@@ -5,10 +5,16 @@
namespace WART_Core.Hubs
{
///
- /// The WART SignalR hub.
+ /// The WART SignalR hub (unauthenticated).
+ /// Group subscriptions are not permitted on this hub because connections
+ /// carry no verified identity. Use or
+ /// when group-scoped event delivery is required.
///
public class WartHub : WartHubBase
{
public WartHub(ILogger logger) : base(logger) { }
+
+ ///
+ protected override bool IsGroupSubscriptionAllowed() => false;
}
}
diff --git a/src/WART-Core/Hubs/WartHubBase.cs b/src/WART-Core/Hubs/WartHubBase.cs
index 2c638d3..1ba9453 100644
--- a/src/WART-Core/Hubs/WartHubBase.cs
+++ b/src/WART-Core/Hubs/WartHubBase.cs
@@ -52,7 +52,16 @@ public override async Task OnConnectedAsync()
if (!string.IsNullOrEmpty(wartGroup))
{
- await AddToGroup(wartGroup);
+ if (IsGroupSubscriptionAllowed())
+ {
+ await AddToGroup(wartGroup);
+ }
+ else
+ {
+ _logger?.LogWarning(
+ "Group subscription denied for ConnectionId={ConnectionId}, User={User}: hub does not permit group subscriptions.",
+ Context.ConnectionId, LogSanitizer.Sanitize(userName));
+ }
}
_logger?.LogInformation("OnConnected: ConnectionId={ConnectionId}, User={User}",
@@ -86,6 +95,17 @@ public override Task OnDisconnectedAsync(Exception exception)
return base.OnDisconnectedAsync(exception);
}
+ ///
+ /// Determines whether the current connection is allowed to subscribe to a SignalR group.
+ /// Authenticated hubs return true only when the connecting principal is authenticated.
+ /// The default unauthenticated overrides this to return false.
+ ///
+ /// true if group subscription is permitted; otherwise false.
+ protected virtual bool IsGroupSubscriptionAllowed()
+ {
+ return Context.User?.Identity?.IsAuthenticated == true;
+ }
+
///
/// Adds the current connection to a specified SignalR group.
///
diff --git a/src/WART-Core/Middleware/WartApplicationBuilderExtension.cs b/src/WART-Core/Middleware/WartApplicationBuilderExtension.cs
index d1ffffa..827942c 100755
--- a/src/WART-Core/Middleware/WartApplicationBuilderExtension.cs
+++ b/src/WART-Core/Middleware/WartApplicationBuilderExtension.cs
@@ -1,4 +1,4 @@
-// (c) 2021 Francesco Del Re
+// (c) 2021 Francesco Del Re
// This code is licensed under MIT license (see LICENSE.txt for details)
using Microsoft.AspNetCore.Builder;
using System;
@@ -31,6 +31,16 @@ private static IReadOnlyList GetDistinctPaths(IEnumerable hubNam
///
/// The IApplicationBuilder to configure the middleware pipeline.
/// The updated IApplicationBuilder to continue configuration.
+ ///
+ /// SECURITY WARNING: This overload maps an unauthenticated SignalR hub that broadcasts
+ /// live API request and response payloads to every connected client with no credential check.
+ /// Use with
+ /// HubType.JwtAuthentication or HubType.CookieAuthentication instead.
+ /// See https://github.com/engineering87/WART/security/advisories
+ ///
+ [Obsolete("UseWartMiddleware() without authentication broadcasts all API events to any anonymous client and is insecure. " +
+ "Use UseWartMiddleware(HubType.JwtAuthentication) or UseWartMiddleware(HubType.CookieAuthentication) instead. " +
+ "See https://github.com/engineering87/WART/security/advisories")]
public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app)
{
app.UseForwardedHeaders();
@@ -63,6 +73,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
switch(hubType)
{
default:
+#pragma warning disable CS0618 // Internal routing — intentional fallback to no-auth mode
case HubType.NoAuthentication:
{
app.UseEndpoints(endpoints =>
@@ -72,6 +83,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
});
break;
}
+#pragma warning restore CS0618
case HubType.JwtAuthentication:
{
app.UseJwtMiddleware();
@@ -105,6 +117,16 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
/// The custom SignalR hub name (URL path).
/// The updated IApplicationBuilder to continue configuration.
/// Thrown when the hub name is null or empty.
+ ///
+ /// SECURITY WARNING: This overload maps an unauthenticated SignalR hub that broadcasts
+ /// live API request and response payloads to every connected client with no credential check.
+ /// Use with
+ /// HubType.JwtAuthentication or HubType.CookieAuthentication instead.
+ /// See https://github.com/engineering87/WART/security/advisories
+ ///
+ [Obsolete("UseWartMiddleware(string) without authentication broadcasts all API events to any anonymous client and is insecure. " +
+ "Use UseWartMiddleware(hubName, HubType.JwtAuthentication) or UseWartMiddleware(hubName, HubType.CookieAuthentication) instead. " +
+ "See https://github.com/engineering87/WART/security/advisories")]
public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app, string hubName)
{
if (string.IsNullOrWhiteSpace(hubName))
@@ -132,6 +154,16 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
/// The list of custom SignalR hub names (URL paths).
/// The updated IApplicationBuilder to continue configuration.
/// Thrown when the hub name list is null.
+ ///
+ /// SECURITY WARNING: This overload maps unauthenticated SignalR hubs that broadcast
+ /// live API request and response payloads to every connected client with no credential check.
+ /// Use with
+ /// HubType.JwtAuthentication or HubType.CookieAuthentication instead.
+ /// See https://github.com/engineering87/WART/security/advisories
+ ///
+ [Obsolete("UseWartMiddleware(IEnumerable) without authentication broadcasts all API events to any anonymous client and is insecure. " +
+ "Use UseWartMiddleware(hubNameList, HubType.JwtAuthentication) or UseWartMiddleware(hubNameList, HubType.CookieAuthentication) instead. " +
+ "See https://github.com/engineering87/WART/security/advisories")]
public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app, IEnumerable hubNameList)
{
ArgumentNullException.ThrowIfNull(hubNameList);
@@ -177,6 +209,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
switch (hubType)
{
default:
+#pragma warning disable CS0618 // Internal routing — intentional fallback to no-auth mode
case HubType.NoAuthentication:
{
app.UseEndpoints(endpoints =>
@@ -186,6 +219,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
});
break;
}
+#pragma warning restore CS0618
case HubType.JwtAuthentication:
{
app.UseJwtMiddleware();
@@ -235,6 +269,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
switch (hubType)
{
default:
+#pragma warning disable CS0618 // Internal routing — intentional fallback to no-auth mode
case HubType.NoAuthentication:
app.UseEndpoints(endpoints =>
{
@@ -243,6 +278,7 @@ public static IApplicationBuilder UseWartMiddleware(this IApplicationBuilder app
endpoints.MapHub(path);
});
break;
+#pragma warning restore CS0618
case HubType.JwtAuthentication:
app.UseJwtMiddleware();
diff --git a/src/WART-Core/Middleware/WartServiceCollectionExtension.cs b/src/WART-Core/Middleware/WartServiceCollectionExtension.cs
index 1dfd14f..b4b66d6 100755
--- a/src/WART-Core/Middleware/WartServiceCollectionExtension.cs
+++ b/src/WART-Core/Middleware/WartServiceCollectionExtension.cs
@@ -1,4 +1,4 @@
-// (c) 2021 Francesco Del Re
+// (c) 2021 Francesco Del Re
// This code is licensed under MIT license (see LICENSE.txt for details)
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HttpOverrides;
@@ -26,6 +26,17 @@ public static class WartServiceCollectionExtension
///
/// The IServiceCollection to configure.
/// The updated IServiceCollection with WART middleware dependencies.
+ ///
+ /// SECURITY WARNING: This overload configures an unauthenticated SignalR hub that
+ /// broadcasts live API request and response payloads to every connected client with no
+ /// credential check. Any anonymous network client can connect and receive sensitive data.
+ /// Use with
+ /// HubType.JwtAuthentication or HubType.CookieAuthentication instead.
+ /// See https://github.com/engineering87/WART/security/advisories
+ ///
+ [Obsolete("AddWartMiddleware() without authentication broadcasts all API events to any anonymous client and is insecure. " +
+ "Use AddWartMiddleware(HubType.JwtAuthentication, tokenKey) or AddWartMiddleware(HubType.CookieAuthentication) instead. " +
+ "See https://github.com/engineering87/WART/security/advisories")]
public static IServiceCollection AddWartMiddleware(this IServiceCollection services)
{
// Configure forwarded headers to support proxy scenarios (X-Forwarded-* headers).
@@ -81,12 +92,14 @@ public static IServiceCollection AddWartMiddleware(this IServiceCollection servi
switch(hubType)
{
default:
+#pragma warning disable CS0618 // Internal routing — intentional fallback to no-auth mode
case HubType.NoAuthentication:
{
// If no authentication is required, configure WART middleware without authentication.
services.AddWartMiddleware();
break;
}
+#pragma warning restore CS0618
case HubType.JwtAuthentication:
{
// If authentication is required, configure JWT middleware for authentication.
diff --git a/src/WART-Core/WART-Core.csproj b/src/WART-Core/WART-Core.csproj
index e48acaf..71a4f36 100644
--- a/src/WART-Core/WART-Core.csproj
+++ b/src/WART-Core/WART-Core.csproj
@@ -12,7 +12,7 @@
https://github.com/engineering87/WART
LICENSE.txt
- 7.0.0
+ 7.1.0
icon.png
README.md
@@ -20,7 +20,7 @@
-
+
diff --git a/src/WART-MinimalApi/WART-MinimalApi.csproj b/src/WART-MinimalApi/WART-MinimalApi.csproj
index b6c4782..cdf53e7 100644
--- a/src/WART-MinimalApi/WART-MinimalApi.csproj
+++ b/src/WART-MinimalApi/WART-MinimalApi.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/src/WART-Tests/WART-Tests.csproj b/src/WART-Tests/WART-Tests.csproj
index 8e80022..c3383c0 100644
--- a/src/WART-Tests/WART-Tests.csproj
+++ b/src/WART-Tests/WART-Tests.csproj
@@ -10,18 +10,18 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/src/WART-WebApiRealTime/WART-Api.csproj b/src/WART-WebApiRealTime/WART-Api.csproj
index 49aaaee..2e7fba9 100755
--- a/src/WART-WebApiRealTime/WART-Api.csproj
+++ b/src/WART-WebApiRealTime/WART-Api.csproj
@@ -8,7 +8,7 @@
-
+