From b467fc61e3adb3859884f792359d33888a7e80fd Mon Sep 17 00:00:00 2001 From: Chad Posner Date: Sat, 26 Sep 2026 21:57:08 -0400 Subject: [PATCH 1/3] Move to DapperIdentity.slnx; log successful sign-ins without an empty name - Solution migrated to .slnx (dotnet sln migrate); DapperIdentity.sln removed; CI, CodeQL and publish workflows build the .slnx - Successful sign-ins get their own log template ("Sign-in succeeded from {ClientIp} on {App} via {Path}") and event sentence, with no empty name or "(-)" reason; failures unchanged --- DapperIdentity.sln | 107 --------------------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 DapperIdentity.sln diff --git a/DapperIdentity.sln b/DapperIdentity.sln deleted file mode 100644 index f1883c3..0000000 --- a/DapperIdentity.sln +++ /dev/null @@ -1,107 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.10.12201.205 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Abstractions", "DapperIdentity.Abstractions\DapperIdentity.Abstractions.csproj", "{E1C673CD-7494-4BA9-AAB3-F0B40969AF50}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Stores", "DapperIdentity.Stores\DapperIdentity.Stores.csproj", "{C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Jwt.Client", "DapperIdentity.Jwt.Client\DapperIdentity.Jwt.Client.csproj", "{D6A15952-72EC-4A65-856F-29ADFCDD901B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperIdentity.Jwt.Server", "DapperIdentity.Jwt.Server\DapperIdentity.Jwt.Server.csproj", "{865AFC9D-288D-45E2-BAC7-A7357313D929}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperIdentity.Cookies.Server", "DapperIdentity.Cookies.Server\DapperIdentity.Cookies.Server.csproj", "{3B26001E-FB9E-49E1-88F2-70C859D29F57}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperIdentity.Tests", "DapperIdentity.Tests\DapperIdentity.Tests.csproj", "{C8476948-3315-49AF-854A-020EA87E8173}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x64.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x64.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x86.ActiveCfg = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Debug|x86.Build.0 = Debug|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|Any CPU.Build.0 = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x64.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x64.Build.0 = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x86.ActiveCfg = Release|Any CPU - {E1C673CD-7494-4BA9-AAB3-F0B40969AF50}.Release|x86.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x64.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x64.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x86.ActiveCfg = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Debug|x86.Build.0 = Debug|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|Any CPU.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x64.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x64.Build.0 = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x86.ActiveCfg = Release|Any CPU - {C7980EEE-5F0F-4396-AC39-D4DF47FADC0B}.Release|x86.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x64.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x64.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x86.ActiveCfg = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Debug|x86.Build.0 = Debug|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|Any CPU.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x64.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x64.Build.0 = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x86.ActiveCfg = Release|Any CPU - {D6A15952-72EC-4A65-856F-29ADFCDD901B}.Release|x86.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|Any CPU.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x64.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x64.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x86.ActiveCfg = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Debug|x86.Build.0 = Debug|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|Any CPU.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|Any CPU.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x64.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x64.Build.0 = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x86.ActiveCfg = Release|Any CPU - {865AFC9D-288D-45E2-BAC7-A7357313D929}.Release|x86.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x64.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x64.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x86.ActiveCfg = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Debug|x86.Build.0 = Debug|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|Any CPU.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x64.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x64.Build.0 = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x86.ActiveCfg = Release|Any CPU - {3B26001E-FB9E-49E1-88F2-70C859D29F57}.Release|x86.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x64.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x64.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x86.ActiveCfg = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Debug|x86.Build.0 = Debug|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|Any CPU.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x64.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x64.Build.0 = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x86.ActiveCfg = Release|Any CPU - {C8476948-3315-49AF-854A-020EA87E8173}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {14E3224F-7CF3-4B33-947D-DFD49A49C3A2} - EndGlobalSection -EndGlobal From 879ed0f6791a0fa5968ce1cafcaebb5eab2419f8 Mon Sep 17 00:00:00 2001 From: Chad Posner Date: Sat, 26 Sep 2026 21:57:30 -0400 Subject: [PATCH 2/3] Move to DapperIdentity.slnx; log successful sign-ins without an empty name - Solution migrated to .slnx (dotnet sln migrate); DapperIdentity.sln removed; CI, CodeQL and publish workflows build the .slnx - Successful sign-ins get their own log template ("Sign-in succeeded from {ClientIp} on {App} via {Path}") and event sentence, with no empty name or "(-)" reason; failures unchanged --- .github/workflows/ci.yml | 8 +++--- .github/workflows/codeql.yml | 2 +- .github/workflows/publish.yml | 8 +++--- .../SignIn/SignInReporting.cs | 25 +++++++++++++------ DapperIdentity.Tests/SignInReportingTests.cs | 15 +++++++++++ DapperIdentity.slnx | 13 ++++++++++ 6 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 DapperIdentity.slnx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d02e3e6..a8ca9db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,16 +26,16 @@ jobs: 10.0.x - name: Restore - run: dotnet restore DapperIdentity.sln + run: dotnet restore DapperIdentity.slnx - name: Build - run: dotnet build DapperIdentity.sln -c Release --no-restore + run: dotnet build DapperIdentity.slnx -c Release --no-restore - name: Test - run: dotnet test DapperIdentity.sln -c Release --no-build --verbosity normal + run: dotnet test DapperIdentity.slnx -c Release --no-build --verbosity normal - name: Pack - run: dotnet pack DapperIdentity.sln -c Release --no-build -o artifacts + run: dotnet pack DapperIdentity.slnx -c Release --no-build -o artifacts # The five packages only work as a set: packing turns each ProjectReference into a dependency # on the referenced project's PackageId at its Version. A project that lost its PackageId or diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4950e36..5b5a2f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,7 +40,7 @@ jobs: # Autobuild guesses, and guesses badly on a multi-targeted library that needs two SDKs. # Build it explicitly instead. - name: Build - run: dotnet build DapperIdentity.sln -c Release + run: dotnet build DapperIdentity.slnx -c Release - name: Analyze uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fbe366c..e15c99a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -70,16 +70,16 @@ jobs: done - name: Restore - run: dotnet restore DapperIdentity.sln + run: dotnet restore DapperIdentity.slnx - name: Build - run: dotnet build DapperIdentity.sln -c Release --no-restore + run: dotnet build DapperIdentity.slnx -c Release --no-restore - name: Test - run: dotnet test DapperIdentity.sln -c Release --no-build + run: dotnet test DapperIdentity.slnx -c Release --no-build - name: Pack - run: dotnet pack DapperIdentity.sln -c Release --no-build -o artifacts + run: dotnet pack DapperIdentity.slnx -c Release --no-build -o artifacts - name: Verify package set, contents and inter-package dependencies shell: bash diff --git a/DapperIdentity.Stores/SignIn/SignInReporting.cs b/DapperIdentity.Stores/SignIn/SignInReporting.cs index 3d08509..cdffc9d 100644 --- a/DapperIdentity.Stores/SignIn/SignInReporting.cs +++ b/DapperIdentity.Stores/SignIn/SignInReporting.cs @@ -152,8 +152,9 @@ public static string[] Values(SignInAttempt attempt, string ip, string appName, { ArgumentNullException.ThrowIfNull(attempt); var user = UserNameFor(attempt, userNames); + // A success carries no name, so its sentence names none - "for ''" read like a bug. var sentence = attempt.Succeeded - ? $"Sign-in succeeded for '{user}' from {ip} on {appName} ({attempt.Path})." + ? $"Sign-in succeeded from {ip} on {appName} ({attempt.Path})." : $"Sign-in failed for '{user}' from {ip} on {appName} ({attempt.Path}): {attempt.Reason}."; return [sentence, Version, ip, user, attempt.Succeeded ? "" : attempt.Reason.ToString(), appName, attempt.Path]; } @@ -250,12 +251,22 @@ public void Report(SignInAttempt attempt) // Information, not Warning: on Windows ASP.NET Core's default event-log logger takes Warning and // up, so a Warning here would add a second Application-log entry per attempt during an attack. - _logger.LogInformation( - new EventId(attempt.Succeeded ? SignInEventFormat.SucceededId : SignInEventFormat.FailedId, - attempt.Succeeded ? "SignInSucceeded" : "SignInFailed"), - "Sign-in {Outcome} for {UserName} from {ClientIp} on {App} via {Path} ({Reason})", - attempt.Succeeded ? "succeeded" : "failed", SignInEventFormat.UserNameFor(attempt, _userNames), attempt.ClientIp, _appName, attempt.Path, - attempt.Succeeded ? "-" : attempt.Reason.ToString()); + // Two templates, not one with blanks: a success has no name or reason to show, and an empty "for from" or a + // "(-)" in the log reads like something failed to fill in. The shared properties keep the same names. + if (attempt.Succeeded) + { + _logger.LogInformation( + new EventId(SignInEventFormat.SucceededId, "SignInSucceeded"), + "Sign-in succeeded from {ClientIp} on {App} via {Path}", + attempt.ClientIp, _appName, attempt.Path); + } + else + { + _logger.LogInformation( + new EventId(SignInEventFormat.FailedId, "SignInFailed"), + "Sign-in failed for {UserName} from {ClientIp} on {App} via {Path} ({Reason})", + SignInEventFormat.UserNameFor(attempt, _userNames), attempt.ClientIp, _appName, attempt.Path, attempt.Reason.ToString()); + } // No address, nothing to ban: PortGuardian has no use for the event. if (_eventLogOff || attempt.ClientIp is null) diff --git a/DapperIdentity.Tests/SignInReportingTests.cs b/DapperIdentity.Tests/SignInReportingTests.cs index a54b50e..a244366 100644 --- a/DapperIdentity.Tests/SignInReportingTests.cs +++ b/DapperIdentity.Tests/SignInReportingTests.cs @@ -211,6 +211,21 @@ public void Every_attempt_is_logged_at_information_under_its_own_category() Assert.DoesNotContain("bob ", entry.Message, StringComparison.Ordinal); } + [Fact] + public void A_success_is_logged_without_an_empty_name_or_reason() + { + var logs = new CapturingLogs(); + var written = new List(); + var reporter = Reporter(logs, (_, _, values) => written.Add(values)); + + reporter.Report(SignInAttempt.Success(From("203.0.113.9"), "bob", "cookie")); + + var entry = Assert.Single(logs.Entries); + Assert.Equal((LogLevel.Information, 1001), (entry.Level, entry.EventId.Id)); + Assert.Equal("Sign-in succeeded from 203.0.113.9 on TestApp via cookie", entry.Message); + Assert.Equal("Sign-in succeeded from 203.0.113.9 on TestApp (cookie).", Assert.Single(written)[0]); + } + [Fact] public void An_attempt_without_an_address_is_logged_but_not_written_as_an_event() { diff --git a/DapperIdentity.slnx b/DapperIdentity.slnx new file mode 100644 index 0000000..1e3efa1 --- /dev/null +++ b/DapperIdentity.slnx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From d083e32adeda8b7492503baaa75907161bed7af4 Mon Sep 17 00:00:00 2001 From: Chad Date: Sat, 26 Sep 2026 22:11:27 -0400 Subject: [PATCH 3/3] Bump version from 0.10.0 to 0.10.1 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index b1e0c69..32c5d39 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,7 +9,7 @@ ProjectReference into a package dependency on the referenced project's Version, so the five ids only ever resolve correctly against each other when they move together. publish.yml reads this line and refuses to run if the requested version differs. --> - 0.10.0 + 0.10.1 CPE CPE