diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Auth/AutoLoginAuthenticationHandler.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Auth/AutoLoginAuthenticationHandler.cs index 7e130725..0ef99825 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Security/Auth/AutoLoginAuthenticationHandler.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Security/Auth/AutoLoginAuthenticationHandler.cs @@ -15,7 +15,7 @@ namespace ZB.MOM.WW.OtOpcUa.Security.Auth; /// The minted principal mirrors the shape the real login (AuthEndpoints) produces. /// public sealed class AutoLoginAuthenticationHandler - : AuthenticationHandler + : AuthenticationHandler, IAuthenticationSignInHandler { private readonly AuthDisableLoginOptions _opts; @@ -32,6 +32,12 @@ public sealed class AutoLoginAuthenticationHandler : base(options, logger, encoder) => _opts = disableLoginOptions.Value; + /// No-op: auto-login writes no cookie, so an explicit sign-in has nothing to persist. + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties) => Task.CompletedTask; + + /// No-op: there is no auth cookie to clear; the next request re-authenticates via this handler. + public Task SignOutAsync(AuthenticationProperties? properties) => Task.CompletedTask; + /// protected override Task HandleAuthenticateAsync() { diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AutoLoginAuthenticationHandlerTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AutoLoginAuthenticationHandlerTests.cs index 145044ec..8f3e9f52 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AutoLoginAuthenticationHandlerTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests/AutoLoginAuthenticationHandlerTests.cs @@ -1,3 +1,4 @@ +using System.Security.Claims; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; @@ -12,7 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Security.Tests; public class AutoLoginAuthenticationHandlerTests { - private static async Task AuthenticateAsync(string user = "multi-role-test") + private static async Task BuildHandlerAsync(string user = "multi-role-test") { var schemeOpts = new StubOptionsMonitor(new AuthenticationSchemeOptions()); var disableOpts = Options.Create(new AuthDisableLoginOptions { DisableLogin = true, User = user }); @@ -23,6 +24,12 @@ public class AutoLoginAuthenticationHandlerTests new AuthenticationScheme( CookieAuthenticationDefaults.AuthenticationScheme, null, typeof(AutoLoginAuthenticationHandler)), new DefaultHttpContext()); + return handler; + } + + private static async Task AuthenticateAsync(string user = "multi-role-test") + { + var handler = await BuildHandlerAsync(user); return await handler.AuthenticateAsync(); } @@ -48,6 +55,17 @@ public class AutoLoginAuthenticationHandlerTests result.Principal!.Identity!.Name.ShouldBe("custom-dev"); } + [Fact] + public async Task SignIn_and_SignOut_are_noops_and_do_not_throw() + { + var handler = await BuildHandlerAsync(); + await Should.NotThrowAsync(async () => + { + await ((IAuthenticationSignInHandler)handler).SignInAsync(new ClaimsPrincipal(), null); + await ((IAuthenticationSignOutHandler)handler).SignOutAsync(null); + }); + } + /// /// Minimal stub returning a fixed value for any /// name. The test project does not reference Moq, so the scheme-options monitor the base