Publish - 2026-04-01 08:20:45 UTC

This commit is contained in:
Duende Bot 2026-04-01 08:22:06 +00:00
parent 1c35015337
commit 50f1a072b3
83 changed files with 267 additions and 321 deletions

3
.gitignore vendored
View file

@ -24,6 +24,7 @@ x86/
bld/
[Bb]in/
[Oo]bj/
[Tt]emp/
# Visual Studio 2015 cache/options directory
.vs/
@ -176,7 +177,7 @@ Logging.g.cs
LoggerMessage.g.cs
PublicTopLevelProgram.Generated.g.cs
RegexGenerator.g.cs
**/Generated/
**/Generated/Microsoft.CodeAnalysis.Razor.Compiler/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,

View file

@ -13,7 +13,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- <Nullable>enable</Nullable> -->
<Nullable>enable</Nullable>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

View file

@ -101,7 +101,7 @@
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.TextWriter" Version="3.0.0" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SimpleExec" Version="12.0.0" />
<PackageVersion Include="SimpleExec" Version="12.1.0" />
<PackageVersion Include="SimpleFeedReader" Version="2.0.4" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.53.1" />
<PackageVersion Include="Spectre.Console.Json" Version="0.54.0" />

View file

@ -1,26 +0,0 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "aspnetcore-authentication-jwtbearer/aspnetcore-authentication-jwtbearer.slnf");
const string TestsAspNetCoreAuthenticationJwtBearerTests = "tests-asp-net-core-authentication-jwt-bearer-tests";
Targets.Test(TestsAspNetCoreAuthenticationJwtBearerTests, "aspnetcore-authentication-jwtbearer/test/AspNetCore.Authentication.JwtBearer.Tests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.CheckSortedSlnf,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsAspNetCoreAuthenticationJwtBearerTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>Duende.AspNetCore.Authentication.JwtBearer</RootNamespace>
<AssemblyName>Duende.AspNetCore.Authentication.JwtBearer</AssemblyName>
</PropertyGroup>

View file

@ -226,7 +226,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
try
{
var tvp = context.Options.ProofTokenValidationParameters;
var tvp = context.Options.ProofTokenValidationParameters.Clone();
tvp.IssuerSigningKey = new JsonWebKey(result.JsonWebKey);
var handler = new JsonWebTokenHandler();

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<AssemblyName>Duende.AspNetCore.Authentication.JwtBearer.Tests</AssemblyName>
<RootNameSpace>Duende.AspNetCore.Authentication.JwtBearer</RootNameSpace>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>

View file

@ -0,0 +1,115 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text.Json;
using Duende.IdentityModel;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
namespace Duende.AspNetCore.Authentication.JwtBearer.DPoP;
/// <summary>
/// Regression tests for GitHub issue #1667: concurrent DPoP proof validation
/// must not corrupt the shared TokenValidationParameters.IssuerSigningKey.
/// </summary>
public sealed class ConcurrentTokenValidationTests : DPoPProofValidatorTestBase
{
[Fact]
public async Task ConcurrentValidationsWithDifferentKeysShouldAllSucceed()
{
// Arrange generate distinct RSA key pairs to simulate different DPoP clients
const int keyCount = 10;
const int requestsPerKey = 20;
var keys = Enumerable.Range(0, keyCount)
.Select(_ => GenerateRsaKeyPair())
.ToList();
// Build (context, result) pairs each uses a proof token signed by a different key
var validations = keys.SelectMany(key =>
Enumerable.Range(0, requestsPerKey).Select(_ =>
{
var proofToken = CreateDPoPProofTokenForKey(key.PrivateJwk, key.PublicJwkPayload);
var result = new DPoPProofValidationResult();
var context = Context with { ProofToken = proofToken };
// Pre-populate the JWK on the result, as ValidateJwk normally does
ProofValidator.ValidateJwk(context, result);
return (Context: context, Result: result);
}))
.ToList();
// Act run all validations concurrently against the shared Options.
// Use a gate to ensure all tasks start simultaneously on the thread pool,
// maximizing overlap to expose any race conditions.
using var gate = new ManualResetEventSlim(false);
var tasks = validations.Select(v => Task.Run(async () =>
{
gate.Wait();
await ProofValidator.ValidateToken(v.Context, v.Result);
return v.Result;
})).ToArray();
gate.Set();
var results = await Task.WhenAll(tasks);
// Assert every validation must succeed; any failure indicates a race condition
var failures = results.Where(r => r.IsError).ToList();
failures.Count.ShouldBe(0,
$"{failures.Count}/{results.Length} validations failed. " +
$"First error: {failures.FirstOrDefault()?.ErrorDescription}");
}
private static (string PrivateJwk, Dictionary<string, string> PublicJwkPayload) GenerateRsaKeyPair()
{
using var rsa = RSA.Create(2048);
var parameters = rsa.ExportParameters(includePrivateParameters: true);
var privateJwkJson = JsonSerializer.Serialize(new
{
kty = "RSA",
n = Base64UrlEncoder.Encode(parameters.Modulus!),
e = Base64UrlEncoder.Encode(parameters.Exponent!),
d = Base64UrlEncoder.Encode(parameters.D!),
p = Base64UrlEncoder.Encode(parameters.P!),
q = Base64UrlEncoder.Encode(parameters.Q!),
dp = Base64UrlEncoder.Encode(parameters.DP!),
dq = Base64UrlEncoder.Encode(parameters.DQ!),
qi = Base64UrlEncoder.Encode(parameters.InverseQ!)
});
var publicJwkPayload = new Dictionary<string, string>
{
["kty"] = "RSA",
["n"] = Base64UrlEncoder.Encode(parameters.Modulus!),
["e"] = Base64UrlEncoder.Encode(parameters.Exponent!)
};
return (privateJwkJson, publicJwkPayload);
}
private static string CreateDPoPProofTokenForKey(
string privateJwkJson,
Dictionary<string, string> publicJwkPayload)
{
var handler = new JsonWebTokenHandler();
var signingKey = new JsonWebKey(privateJwkJson);
var descriptor = new SecurityTokenDescriptor
{
TokenType = "dpop+jwt",
IssuedAt = DateTime.UtcNow,
AdditionalHeaderClaims = new Dictionary<string, object>
{
{ JwtClaimTypes.JsonWebKey, publicJwkPayload }
},
Subject = new ClaimsIdentity(),
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256)
};
return handler.CreateToken(descriptor);
}
}

View file

@ -2,4 +2,16 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../test.props" />
<PropertyGroup>
<!-- TODO: Fix these in test code and remove suppressions -->
<NoWarn>$(NoWarn);CA1051</NoWarn><!-- Do not declare visible instance fields -->
<NoWarn>$(NoWarn);CA1305</NoWarn><!-- Specify IFormatProvider -->
<NoWarn>$(NoWarn);CA1310</NoWarn><!-- Specify StringComparison for correctness -->
<NoWarn>$(NoWarn);CA1707</NoWarn><!-- Identifiers should not contain underscores -->
<NoWarn>$(NoWarn);CA1805</NoWarn><!-- Do not initialize unnecessarily -->
<NoWarn>$(NoWarn);CA1822</NoWarn><!-- Mark members as static -->
<NoWarn>$(NoWarn);CA1852</NoWarn><!-- Seal internal types -->
<NoWarn>$(NoWarn);CA1866</NoWarn><!-- Use char overload -->
<NoWarn>$(NoWarn);CA2201</NoWarn><!-- Do not raise reserved exception types -->
</PropertyGroup>
</Project>

View file

@ -1,29 +0,0 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "bff/bff.slnf");
const string TestsBffTests = "tests-bff-tests";
const string TestsHostsTests = "tests-hosts-tests";
Targets.Test(TestsBffTests, "bff/test/Bff.Tests", repoRoot);
Targets.Test(TestsHostsTests, "bff/test/Hosts.Tests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.CheckSortedSlnf,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsBffTests,
TestsHostsTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
</PropertyGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
</PropertyGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>616547e2-3a28-4c9d-8685-f4ac02581162</UserSecretsId>

View file

@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<RootNamespace>Bff.DPoP</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<RootNamespace>Bff.EF</RootNamespace>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />

View file

@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<RootNamespace>Bff</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>

View file

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
<NoWarn>$(NoWarn);IDE0130</NoWarn><!-- Namespace does not match folder structure --><!-- TODO: remove? -->
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

View file

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.AspNetCore.Authentication.JwtBearer" />

View file

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityModel" />

View file

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<ItemGroup>

View file

@ -5,5 +5,6 @@
<RootNamespace>$(AssemblyName)</RootNamespace>
<AssemblyName>Duende.$(MSBuildProjectName)</AssemblyName>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
</Project>

View file

@ -3,7 +3,6 @@
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<WarningsAsErrors>false</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View file

@ -4,7 +4,6 @@
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblyName>Duende.BFF.Blazor.Client</AssemblyName>
</PropertyGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblyName>Duende.BFF.Blazor</AssemblyName>
</PropertyGroup>

View file

@ -5,7 +5,6 @@
<Import Project="../../src.props" />
<PropertyGroup>
<Nullable>enable</Nullable>
<PackageTags>OAuth 2.0;OpenID Connect;Security;BFF;IdentityServer;ASP.NET Core;SPA;Blazor</PackageTags>
<Product>Duende BFF</Product>
<MinVerTagPrefix>bff-</MinVerTagPrefix>

View file

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.BFF.Yarp" Version="3.0.0" />

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<NoWarn>$(NoWarn);IDE0130</NoWarn><!-- Namespace does not match folder structure --><!-- TODO: remove? -->
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>

View file

@ -5,11 +5,33 @@
<AssemblyName>Duende.$(MSBuildProjectName)</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<!-- RS0026: Do not add multiple overloads with optional parameters -->
<NoWarn>$(NoWarn);RS0026</NoWarn>
<!-- RS0027: API with optional parameter(s) should have the most parameters amongst its public overloads -->
<NoWarn>$(NoWarn);RS0027</NoWarn>
<!-- TODO: Fix these in bff test code and remove suppressions -->
<NoWarn>$(NoWarn);RS0026</NoWarn><!-- Do not add multiple overloads with optional parameters -->
<NoWarn>$(NoWarn);RS0027</NoWarn><!-- API with optional parameter(s) should have the most parameters amongst its public overloads -->
<NoWarn>$(NoWarn);CA1000</NoWarn><!-- Do not declare static members on generic types -->
<NoWarn>$(NoWarn);CA1001</NoWarn><!-- Types that own disposable fields should be disposable -->
<NoWarn>$(NoWarn);CA1051</NoWarn><!-- Do not declare visible instance fields -->
<NoWarn>$(NoWarn);CA1304</NoWarn><!-- Specify CultureInfo -->
<NoWarn>$(NoWarn);CA1305</NoWarn><!-- Specify IFormatProvider -->
<NoWarn>$(NoWarn);CA1310</NoWarn><!-- Specify StringComparison for correctness -->
<NoWarn>$(NoWarn);CA1311</NoWarn><!-- Specify a culture or use an invariant version -->
<NoWarn>$(NoWarn);CA1707</NoWarn><!-- Identifiers should not contain underscores -->
<NoWarn>$(NoWarn);CA1711</NoWarn><!-- Identifiers should not have incorrect suffix -->
<NoWarn>$(NoWarn);CA1725</NoWarn><!-- Parameter names should match base declaration -->
<NoWarn>$(NoWarn);CA1805</NoWarn><!-- Do not initialize unnecessarily -->
<NoWarn>$(NoWarn);CA1816</NoWarn><!-- Call GC.SuppressFinalize correctly -->
<NoWarn>$(NoWarn);CA1822</NoWarn><!-- Mark members as static -->
<NoWarn>$(NoWarn);CA1829</NoWarn><!-- Use Length/Count property instead of Count() -->
<NoWarn>$(NoWarn);CA1848</NoWarn><!-- Use the LoggerMessage delegates -->
<NoWarn>$(NoWarn);CA1852</NoWarn><!-- Seal internal types -->
<NoWarn>$(NoWarn);CA1859</NoWarn><!-- Use concrete types when possible for improved performance -->
<NoWarn>$(NoWarn);CA1860</NoWarn><!-- Avoid using Enumerable.Any() extension method -->
<NoWarn>$(NoWarn);CA1861</NoWarn><!-- Avoid constant arrays as arguments -->
<NoWarn>$(NoWarn);CA1866</NoWarn><!-- Use char overload -->
<NoWarn>$(NoWarn);CA1869</NoWarn><!-- Cache and reuse JsonSerializerOptions instances -->
<NoWarn>$(NoWarn);CA2016</NoWarn><!-- Forward CancellationToken to methods that accept it -->
<NoWarn>$(NoWarn);CA2201</NoWarn><!-- Do not raise reserved exception types -->
<NoWarn>$(NoWarn);CA2254</NoWarn><!-- Template should be a static expression -->
</PropertyGroup>
<Import Project="../../test.props" />
</Project>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<Configurations>Debug;Release;Debug_ncrunch</Configurations>
<RootNamespace>Hosts.Tests</RootNamespace>

View file

@ -1,26 +0,0 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "conformance-report/conformance-report.slnf");
const string TestsConformanceReportTests = "tests-conformance-report-tests";
Targets.Test(TestsConformanceReportTests, "conformance-report/test/ConformanceReport.Tests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.CheckSortedSlnf,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsConformanceReportTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -122,8 +122,6 @@ internal class OAuth21Assessor(ConformanceReportServerOptions options)
return findings;
}
#region Server-Level Assessments
private Finding AssessParAvailability()
{
var parEnabled = options.PushedAuthorizationEndpointEnabled;
@ -240,10 +238,6 @@ internal class OAuth21Assessor(ConformanceReportServerOptions options)
Recommendation = options.UseHttp303Redirects ? null : "Set UseHttp303Redirects = true in IdentityServerOptions."
};
#endregion
#region Client-Level Assessments
private static Finding AssessAllowedGrantTypes(ConformanceReportClient client)
{
var allowedGrants = new HashSet<string>
@ -654,5 +648,4 @@ internal class OAuth21Assessor(ConformanceReportServerOptions options)
};
}
#endregion
}
}

View file

@ -2,7 +2,6 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../src.props" />
<PropertyGroup>
<Nullable>enable</Nullable>
<AssemblyName>Duende.ConformanceReport.$(MSBuildProjectName)</AssemblyName>
<PackageId>Duende.ConformanceReport.$(MSBuildProjectName)</PackageId>
<RootNamespace>Duende.ConformanceReport</RootNamespace>

View file

@ -1,7 +1,4 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../test.props" />
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -1,21 +0,0 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "docs-mcp/docs-mcp.slnf");
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.CheckSortedSlnf,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts
]);
await RunTargetsAndExitAsync(args);

View file

@ -9,6 +9,5 @@
<Product>Duende Documentation MCP Server</Product>
<MinVerTagPrefix>dmcp-</MinVerTagPrefix>
<MinVerMinimumMajorMinor>1.0</MinVerMinimumMajorMinor>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>b86a3528-3d86-4514-b57f-9839f472ef31</UserSecretsId>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
</PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

View file

@ -1,32 +0,0 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "identity-server/identity-server.slnf");
const string TestsIdentityServerUnitTests = "tests-identity-server-unit-tests";
const string TestsIdentityServerIntegrationTests = "tests-identity-server-integration-tests";
const string TestsIdentityServerEndToEndTests = "tests-identity-server-end-to-end-tests";
Targets.Test(TestsIdentityServerUnitTests, "identity-server/test/IdentityServer.UnitTests", repoRoot);
Targets.Test(TestsIdentityServerIntegrationTests, "identity-server/test/IdentityServer.IntegrationTests", repoRoot);
Targets.Test(TestsIdentityServerEndToEndTests, "identity-server/test/IdentityServer.EndToEndTests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.CheckSortedSlnf,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsIdentityServerUnitTests,
TestsIdentityServerIntegrationTests,
TestsIdentityServerEndToEndTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -4,5 +4,6 @@
<AnalysisMode>None</AnalysisMode>
<IsIdSrvProject>true</IsIdSrvProject>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
</Project>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<RootNamespace>IdentityServerHost</RootNamespace>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<RootNamespace>IdentityServerHost</RootNamespace>
<Nullable>enable</Nullable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

View file

@ -4,7 +4,6 @@
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<RootNamespace>IdentityServerHost</RootNamespace>
<UserSecretsId>e60c119c-8b86-4016-9d44-80e25948dbba</UserSecretsId>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>

View file

@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Duende.IdentityServer.Hosts.Shared</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<RootNamespace>Duende.IdentityServer.UI.AspNetIdentity</RootNamespace>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<RootNamespace>Duende.IdentityServer.UI.EntityFramework</RootNamespace>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<RootNamespace>Duende.IdentityServer.UI</RootNamespace>

View file

@ -4,5 +4,6 @@
<AnalysisMode>None</AnalysisMode>
<IsIdSrvProject>true</IsIdSrvProject>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
</Project>

View file

@ -2,5 +2,6 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<PropertyGroup>
<AnalysisMode>None</AnalysisMode>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
</Project>

View file

@ -9,6 +9,7 @@
<MinVerTagPrefix>is-</MinVerTagPrefix>
<MinVerMinimumMajorMinor>8.0</MinVerMinimumMajorMinor>
<IsIdSrvProject>true</IsIdSrvProject>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
</PropertyGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA1002;CA1008;CA1031;CA1051;CA1054;CA1055;CA1056;CA1062;CA1716;CA1724;CA1725;CA1727;CA1819;CA1848;CA1851;CA2201;CA2208;CA2227;CA2234</NoWarn>

View file

@ -159,7 +159,7 @@ public class PersistedGrantStore : Duende.IdentityServer.Stores.IPersistedGrantS
private static IQueryable<PersistedGrant> Filter(IQueryable<PersistedGrant> query, PersistedGrantFilter filter)
{
if (filter.ClientIds != null)
if (filter.ClientIds.Count > 0)
{
var ids = filter.ClientIds.ToList();
if (!string.IsNullOrWhiteSpace(filter.ClientId))
@ -182,7 +182,7 @@ public class PersistedGrantStore : Duende.IdentityServer.Stores.IPersistedGrantS
query = query.Where(x => x.SubjectId == filter.SubjectId);
}
if (filter.Types != null)
if (filter.Types.Count > 0)
{
var types = filter.Types.ToList();
if (!string.IsNullOrWhiteSpace(filter.Type))

View file

@ -119,7 +119,7 @@ public class LocalApiAuthenticationHandler : AuthenticationHandler<LocalApiAuthe
}
var proofToken = Context.Request.Headers[OidcConstants.HttpHeaders.DPoP].FirstOrDefault();
var validationContext = new DPoPProofValidatonContext
var validationContext = new DPoPProofValidationContext
{
ProofToken = proofToken,
Method = Context.Request.Method,

View file

@ -61,10 +61,7 @@ public class DefaultSessionManagementService : ISessionManagementService
SessionId = context.SessionId,
};
if (context.ClientIds != null)
{
grantFilter.ClientIds = context.ClientIds;
}
grantFilter.ClientIds = context.ClientIds ?? [];
if (!context.RevokeTokens || !context.RevokeConsents)
{

View file

@ -83,7 +83,7 @@ public class InMemoryPersistedGrantStore : IPersistedGrantStore
from item in _repository
select item.Value;
if (filter.ClientIds != null)
if (filter.ClientIds.Count > 0)
{
var ids = filter.ClientIds.ToList();
if (!string.IsNullOrWhiteSpace(filter.ClientId))
@ -106,7 +106,7 @@ public class InMemoryPersistedGrantStore : IPersistedGrantStore
query = query.Where(x => x.SubjectId == filter.SubjectId);
}
if (filter.Types != null)
if (filter.Types.Count > 0)
{
var types = filter.Types.ToList();
if (!string.IsNullOrWhiteSpace(filter.Type))

View file

@ -12,7 +12,7 @@ namespace Duende.IdentityServer.Validation;
/// <summary>
/// Models the context for validaing DPoP proof tokens.
/// </summary>
public class DPoPProofValidatonContext
public class DPoPProofValidationContext
{
/// <summary>
/// Enum setting to control validation for the DPoP proof token expiration.

View file

@ -69,9 +69,9 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
}
/// <inheritdoc/>
public async Task<DPoPProofValidatonResult> ValidateAsync(DPoPProofValidatonContext context, Ct ct)
public async Task<DPoPProofValidationResult> ValidateAsync(DPoPProofValidationContext context, Ct ct)
{
var result = new DPoPProofValidatonResult() { IsError = false };
var result = new DPoPProofValidationResult() { IsError = false };
try
{
@ -120,7 +120,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the header.
/// </summary>
protected virtual Task ValidateHeaderAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual Task ValidateHeaderAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
JsonWebToken token;
var handler = new JsonWebTokenHandler();
@ -242,7 +242,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the signature.
/// </summary>
protected virtual async Task ValidateSignatureAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual async Task ValidateSignatureAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
Microsoft.IdentityModel.Tokens.TokenValidationResult tokenValidationResult;
@ -284,7 +284,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the payload.
/// </summary>
protected virtual async Task ValidatePayloadAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result, Ct ct)
protected virtual async Task ValidatePayloadAsync(DPoPProofValidationContext context, DPoPProofValidationResult result, Ct ct)
{
if (context.ValidateAccessToken)
{
@ -379,9 +379,9 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
}
/// <summary>
/// Validates is the token has been replayed.
/// Validates if the token has been replayed.
/// </summary>
protected virtual async Task ValidateReplayAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result, Ct ct)
protected virtual async Task ValidateReplayAsync(DPoPProofValidationContext context, DPoPProofValidationResult result, Ct ct)
{
if (await ReplayCache.ExistsAsync(ReplayCachePurpose, result.TokenId, ct))
{
@ -416,7 +416,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the freshness.
/// </summary>
protected virtual async Task ValidateFreshnessAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual async Task ValidateFreshnessAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
var validateIat = (context.ExpirationValidationMode & DPoPTokenExpirationValidationMode.Iat) == DPoPTokenExpirationValidationMode.Iat;
if (validateIat)
@ -442,7 +442,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the freshness of the iat value.
/// </summary>
protected virtual Task ValidateIatAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual Task ValidateIatAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
if (IsExpired(context, result, context.ClientClockSkew, result.IssuedAt.Value))
{
@ -457,7 +457,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// <summary>
/// Validates the freshness of the nonce value.
/// </summary>
protected virtual async Task ValidateNonceAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual async Task ValidateNonceAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
if (result.Nonce.IsMissing())
{
@ -496,7 +496,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// Creates a nonce value to return to the client.
/// </summary>
/// <returns></returns>
protected virtual string CreateNonce(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual string CreateNonce(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
var now = TimeProvider.GetUtcNow().ToUnixTimeSeconds();
return DataProtector.Protect(now.ToString(CultureInfo.InvariantCulture));
@ -506,7 +506,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// Reads the time the nonce was created.
/// </summary>
/// <returns></returns>
protected virtual ValueTask<long> GetUnixTimeFromNonceAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected virtual ValueTask<long> GetUnixTimeFromNonceAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
try
{
@ -528,7 +528,7 @@ public class DefaultDPoPProofValidator : IDPoPProofValidator
/// Validates the expiration of the DPoP proof.
/// Returns true if the time is beyond the allowed limits, false otherwise.
/// </summary>
protected virtual bool IsExpired(DPoPProofValidatonContext context, DPoPProofValidatonResult result, TimeSpan clockSkew, long issuedAtTime)
protected virtual bool IsExpired(DPoPProofValidationContext context, DPoPProofValidationResult result, TimeSpan clockSkew, long issuedAtTime)
{
var now = TimeProvider.GetUtcNow().ToUnixTimeSeconds();
var start = now + (int)clockSkew.TotalSeconds;

View file

@ -87,7 +87,7 @@ internal class PushedAuthorizationRequestValidator(
// validate proof token
var parUrl = context.ClientCertificate == null ? serverUrls.BaseUrl.EnsureTrailingSlash() + ProtocolRoutePaths.PushedAuthorization : mtlsEndpointGenerator.GetMtlsEndpointPath(ProtocolRoutePaths.PushedAuthorization);
var dpopContext = new DPoPProofValidatonContext
var dpopContext = new DPoPProofValidationContext
{
ProofToken = context.DPoPProofToken,
ExpirationValidationMode = context.Client.DPoPValidationMode,

View file

@ -253,7 +253,7 @@ internal class TokenRequestValidator : ITokenRequestValidator
}
var tokenUrl = context.ClientCertificate == null ? _serverUrls.BaseUrl.EnsureTrailingSlash() + ProtocolRoutePaths.Token : _mtlsEndpointGenerator.GetMtlsEndpointPath(ProtocolRoutePaths.Token);
var dpopContext = new DPoPProofValidatonContext
var dpopContext = new DPoPProofValidationContext
{
ExpirationValidationMode = _validatedRequest.Client.DPoPValidationMode,
ClientClockSkew = _validatedRequest.Client.DPoPClockSkew,

View file

@ -16,5 +16,5 @@ public interface IDPoPProofValidator
/// </summary>
/// <param name="context">The validation context.</param>
/// <param name="ct">The cancellation token.</param>
Task<DPoPProofValidatonResult> ValidateAsync(DPoPProofValidatonContext context, Ct ct);
Task<DPoPProofValidationResult> ValidateAsync(DPoPProofValidationContext context, Ct ct);
}

View file

@ -9,7 +9,7 @@ namespace Duende.IdentityServer.Validation;
/// <summary>
/// Models the result of DPoP proof validation.
/// </summary>
public class DPoPProofValidatonResult : ValidationResult
public class DPoPProofValidationResult : ValidationResult
{
/// <summary>
/// The serialized JWK from the validated DPoP proof token.

View file

@ -20,11 +20,11 @@ public static class PersistedGrantFilterExtensions
ArgumentNullException.ThrowIfNull(filter);
if (string.IsNullOrWhiteSpace(filter.ClientId) &&
filter.ClientIds == null &&
filter.ClientIds.Count == 0 &&
string.IsNullOrWhiteSpace(filter.SessionId) &&
string.IsNullOrWhiteSpace(filter.SubjectId) &&
string.IsNullOrWhiteSpace(filter.Type) &&
filter.Types == null)
filter.Types.Count == 0)
{
throw new ArgumentException("No filter values set.", nameof(filter));
}

View file

@ -31,7 +31,7 @@ public class PersistedGrantFilter
/// <summary>
/// Client ids the grant was issued to.
/// </summary>
public IReadOnlyCollection<string>? ClientIds { get; set; }
public IReadOnlyCollection<string> ClientIds { get; set; } = [];
/// <summary>
/// The type of grant.
@ -41,5 +41,5 @@ public class PersistedGrantFilter
/// <summary>
/// The types of grants.
/// </summary>
public IReadOnlyCollection<string>? Types { get; set; }
public IReadOnlyCollection<string> Types { get; set; } = [];
}

View file

@ -5,5 +5,39 @@
<Import Project="../identity-server.props" />
<PropertyGroup>
<IsIdSrvProject>true</IsIdSrvProject>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
<!-- TODO: Fix these in identity-server test code and remove suppressions -->
<NoWarn>$(NoWarn);CA1001</NoWarn><!-- Types that own disposable fields should be disposable -->
<NoWarn>$(NoWarn);CA1041</NoWarn><!-- Provide ObsoleteAttribute message -->
<NoWarn>$(NoWarn);CA1051</NoWarn><!-- Do not declare visible instance fields -->
<NoWarn>$(NoWarn);CA1304</NoWarn><!-- Specify CultureInfo -->
<NoWarn>$(NoWarn);CA1305</NoWarn><!-- Specify IFormatProvider -->
<NoWarn>$(NoWarn);CA1309</NoWarn><!-- Use ordinal StringComparison -->
<NoWarn>$(NoWarn);CA1310</NoWarn><!-- Specify StringComparison for correctness -->
<NoWarn>$(NoWarn);CA1311</NoWarn><!-- Specify a culture or use an invariant version -->
<NoWarn>$(NoWarn);CA1707</NoWarn><!-- Identifiers should not contain underscores -->
<NoWarn>$(NoWarn);CA1708</NoWarn><!-- Identifiers should differ by more than case -->
<NoWarn>$(NoWarn);CA1711</NoWarn><!-- Identifiers should not have incorrect suffix -->
<NoWarn>$(NoWarn);CA1716</NoWarn><!-- Identifiers should not match keywords -->
<NoWarn>$(NoWarn);CA1725</NoWarn><!-- Parameter names should match base declaration -->
<NoWarn>$(NoWarn);CA1805</NoWarn><!-- Do not initialize unnecessarily -->
<NoWarn>$(NoWarn);CA1806</NoWarn><!-- Do not ignore method results -->
<NoWarn>$(NoWarn);CA1816</NoWarn><!-- Call GC.SuppressFinalize correctly -->
<NoWarn>$(NoWarn);CA1822</NoWarn><!-- Mark members as static -->
<NoWarn>$(NoWarn);CA1825</NoWarn><!-- Avoid zero-length array allocations -->
<NoWarn>$(NoWarn);CA1829</NoWarn><!-- Use Length/Count property instead of Count() -->
<NoWarn>$(NoWarn);CA1835</NoWarn><!-- Prefer Memory-based overloads for ReadAsync/WriteAsync -->
<NoWarn>$(NoWarn);CA1850</NoWarn><!-- Prefer static HashData method over ComputeHash -->
<NoWarn>$(NoWarn);CA1852</NoWarn><!-- Seal internal types -->
<NoWarn>$(NoWarn);CA1859</NoWarn><!-- Use concrete types when possible for improved performance -->
<NoWarn>$(NoWarn);CA1860</NoWarn><!-- Avoid using Enumerable.Any() extension method -->
<NoWarn>$(NoWarn);CA1861</NoWarn><!-- Avoid constant arrays as arguments -->
<NoWarn>$(NoWarn);CA1863</NoWarn><!-- Cache and reuse CompositeFormat instances -->
<NoWarn>$(NoWarn);CA1864</NoWarn><!-- Prefer IDictionary.TryAdd(TKey, TValue) -->
<NoWarn>$(NoWarn);CA1869</NoWarn><!-- Cache and reuse JsonSerializerOptions instances -->
<NoWarn>$(NoWarn);CA1872</NoWarn><!-- Prefer Convert.ToHexString and Convert.ToHexStringLower -->
<NoWarn>$(NoWarn);CA2201</NoWarn><!-- Do not raise reserved exception types -->
<NoWarn>$(NoWarn);CA2211</NoWarn><!-- Non-constant fields should not be visible -->
<NoWarn>$(NoWarn);CA5350</NoWarn><!-- Do not use weak cryptographic algorithms -->
</PropertyGroup>
</Project>

View file

@ -444,7 +444,7 @@ public class DPoPTokenEndpointTests : DPoPEndpointTestBase
public string ServerIssuedNonce { get; set; }
protected override async Task ValidateFreshnessAsync(DPoPProofValidatonContext context, DPoPProofValidatonResult result)
protected override async Task ValidateFreshnessAsync(DPoPProofValidationContext context, DPoPProofValidationResult result)
{
if (ServerIssuedNonce.IsPresent())
{

View file

@ -43,7 +43,7 @@ public class DPoPProofValidatorTests
}
}
private DPoPProofValidatonContext _context = new DPoPProofValidatonContext
private DPoPProofValidationContext _context = new DPoPProofValidationContext
{
ClientClockSkew = TimeSpan.Zero,
Url = "https://identityserver/connect/token",

View file

@ -5,7 +5,6 @@
<Import Project="../../src.props" />
<PropertyGroup>
<Nullable>enable</Nullable>
<Product>Duende IgnoreThis</Product>
<MinVerTagPrefix>it-</MinVerTagPrefix>
<MinVerMinimumMajorMinor>0.1</MinVerMinimumMajorMinor>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageId>Duende.IgnoreThis</PackageId>
<AssemblyName>$(PackageId)</AssemblyName>

View file

@ -1,7 +1,4 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../test.props" />
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -11,12 +11,6 @@
<Folder Name="/aspnetcore-authentication-jwtbearer/test/">
<Project Path="aspnetcore-authentication-jwtbearer/test/AspNetCore.Authentication.JwtBearer.Tests/AspNetCore.Authentication.JwtBearer.Tests.csproj" />
</Folder>
<Folder Name="/conformance-report/src/">
<Project Path="conformance-report/src/ConformanceReport/ConformanceReport.csproj" />
</Folder>
<Folder Name="/conformance-report/test/">
<Project Path="conformance-report/test/ConformanceReport.Tests/ConformanceReport.Tests.csproj" />
</Folder>
<Folder Name="/bff/hosts/">
<Project Path="bff/hosts/Hosts.AppHost/Hosts.AppHost.csproj" />
<Project Path="bff/hosts/Hosts.Bff.DPoP/Hosts.Bff.DPoP.csproj" />
@ -43,6 +37,10 @@
<Folder Name="/bff/migrations/">
<Project Path="bff/migrations/UserSessionDb/UserSessionDb.csproj" />
</Folder>
<Folder Name="/bff/performance/">
<Project Path="bff/performance/Bff.Benchmarks/Bff.Benchmarks.csproj" />
<Project Path="bff/performance/Bff.Performance/Bff.Performance.csproj" />
</Folder>
<Folder Name="/bff/src/">
<Project Path="bff/src/Bff.Blazor.Client/Bff.Blazor.Client.csproj" />
<Project Path="bff/src/Bff.Blazor/Bff.Blazor.csproj" />
@ -50,10 +48,6 @@
<Project Path="bff/src/Bff.Yarp/Bff.Yarp.csproj" />
<Project Path="bff/src/Bff/Bff.csproj" />
</Folder>
<Folder Name="/bff/performance/">
<Project Path="bff/performance/Bff.Benchmarks/Bff.Benchmarks.csproj" />
<Project Path="bff/performance/Bff.Performance/Bff.Performance.csproj" />
</Folder>
<Folder Name="/bff/templates/src/">
<Project Path="bff/templates/src/BffLocalApi/BffLocalApi.csproj" />
<Project Path="bff/templates/src/BffRemoteApi/BffRemoteApi.csproj" />
@ -66,9 +60,15 @@
<Project Path="bff/test/Bff.Tests/Bff.Tests.csproj" />
<Project Path="bff/test/Hosts.Tests/Hosts.Tests.csproj" />
</Folder>
<Folder Name="/conformance-report/src/">
<Project Path="conformance-report/src/ConformanceReport/ConformanceReport.csproj" />
</Folder>
<Folder Name="/conformance-report/test/">
<Project Path="conformance-report/test/ConformanceReport.Tests/ConformanceReport.Tests.csproj" />
</Folder>
<Folder Name="/docs-mcp/">
<File Path="docs-mcp/README.md" />
<Project Path="docs-mcp/src/Documentation.Mcp/Documentation.Mcp.csproj" />
<File Path="docs-mcp/README.md" />
</Folder>
<Folder Name="/docs-mcp/.config/">
<File Path="docs-mcp/.config/dotnet-tools.json" />

View file

@ -3,7 +3,6 @@
<PropertyGroup>
<AnalysisMode>None</AnalysisMode>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
<Nullable>enable</Nullable>
<IsTestProject>false</IsTestProject>
<RootNamespace>Shouldly</RootNamespace>
</PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AnalysisMode>None</AnalysisMode>
<Nullable>enable</Nullable>
<AssemblyName>Duende.Xunit.Playwright</AssemblyName>
<RootNamespace>Duende.Xunit.Playwright</RootNamespace>
<IsTestProject>false</IsTestProject>

View file

@ -1,33 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<IsTestProject>false</IsTestProject>
<IsPackable>true</IsPackable>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<!--NuGet-->
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<!-- TODO - Verify that license is included in nuget packages -->
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://github.com/duendesoftware/products</PackageProjectUrl>
<PackageReleaseNotes>https://github.com/duendesoftware/products/releases</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
<!--Minver-->
<BUILD_NUMBER Condition="'$(BUILD_NUMBER)' == ''">0</BUILD_NUMBER>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath><!-- TODO: move to Directory.Build.props -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles><!-- TODO: move to Directory.Build.props -->
<IsPackable>true</IsPackable>
<MinVerBuildMetadata>build.$(BUILD_NUMBER)</MinVerBuildMetadata>
<MinVerAutoIncrement>patch</MinVerAutoIncrement>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile><!-- TODO: verify license is included in packages -->
<PackageProjectUrl>https://github.com/duendesoftware/products</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReadmePath>../../README.md</PackageReadmePath>
<PackageReleaseNotes>https://github.com/duendesoftware/products/releases</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<!-- TODO: check which of these properties are actually required -->
<PropertyGroup Label="SourceLink">
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);RS0016</NoWarn><!-- Add public types and members to the declared API -->
@ -41,8 +34,7 @@
<ItemGroup>
<None Include="../../../icon.png" Pack="true" Visible="false" PackagePath="" />
<None Include="$(PackageReadmePath)" Pack="true" PackagePath="" />
<None Include="../../../LICENSE" Pack="true" PackagePath="" />
<None Include="$(PackageReadmePath)" Pack="true" PackagePath="" />
</ItemGroup>
</Project>

View file

@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);CA1303</NoWarn>
</PropertyGroup>

View file

@ -8,6 +8,7 @@
<IsTestProject>false</IsTestProject>
<IsPackable>true</IsPackable>
<Nullable>disable</Nullable><!-- TODO: enable nullable -->
<PackageId>Duende.Templates</PackageId>
<Description>Templates for Duende Identity Server and Duende BFF </Description>

View file

@ -8,66 +8,10 @@
<OutputType>exe</OutputType>
<!-- Enable Microsoft Testing Platform command line experience for code coverage support -->
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<!-- Suppress analyzer rules inappropriate for test projects -->
<!-- CA1000: No static members on generic types — generic test fixtures commonly use static helpers -->
<!-- CA1001: Types owning disposable fields should be disposable — test classes rely on xunit lifecycle -->
<!-- CA1002: Use Collection<T> instead of List<T> — test fixtures use List<T> for convenience -->
<!-- CA1003: Use generic EventHandler — test infrastructure uses Action delegates -->
<!-- CA1012: Abstract types should not have public constructors — test base classes need public ctors for xunit -->
<!-- CA1024: Use properties where appropriate — test helpers use Get methods for clarity -->
<!-- CA1031: Catch more specific exception — test infrastructure intentionally catches all exceptions -->
<!-- CA1033: Explicit interface implementation — test mocks use explicit interface impls without sealing -->
<!-- CA1041: Provide ObsoleteAttribute message — test methods use [Obsolete] to skip without message -->
<!-- CA1051: Do not declare visible instance fields — test base classes expose fields for subclasses -->
<!-- CA1052: Static holder types should be static — test classes with only static [Fact] methods are common -->
<!-- CA1054/CA1055/CA1056: URI parameters/return/properties should not be strings — test helpers use strings -->
<!-- CA1062: Validate public method parameters — test code relies on xunit for argument validation -->
<!-- CA1063: Implement IDisposable correctly — test infrastructure uses simplified disposal patterns -->
<!-- CA1304/CA1305/CA1307/CA1308/CA1309/CA1310/CA1311: Locale/culture/ordinal rules — not critical in test code -->
<!-- CA1508: Avoid dead conditional code — tests intentionally verify always-true/false conditions -->
<!-- CA1515: Make class internal — test classes must be public for xunit discovery -->
<!-- CA1707: Identifiers should not contain underscores — snake_case test method names are conventional -->
<!-- CA1708: Namespace names differ only by case — test namespaces mirror SUT folder structure (V2 vs v2) -->
<!-- CA1711: Identifiers should not have incorrect suffix — xunit [CollectionDefinition] requires 'Collection' suffix -->
<!-- CA1716: Reserved language keyword in namespace — test namespaces use 'Default' to mirror SUT structure -->
<!-- CA1724: Type names should not match namespaces — test types like TestHost commonly shadow framework namespaces -->
<!-- CA1725: Parameter names should match base — test overrides use abbreviated names (ct) -->
<!-- CA1805: Do not initialize unnecessarily — clarity preferred in test code -->
<!-- CA1806: Return value not used / object instantiated but not used — tests verify ctor exceptions -->
<!-- CA1810: Initialize static fields when declared — test classes use static ctors for complex initialization -->
<!-- CA1812: Internal class never instantiated — test classes instantiated via DI/reflection -->
<!-- CA1816: Call GC.SuppressFinalize — test infrastructure uses simplified disposal patterns -->
<!-- CA1820: Test for empty strings using Length — test code uses == "" for readability -->
<!-- CA1822: Mark members as static — test helper methods may be non-static by convention -->
<!-- CA1823: Unused fields — test fixture fields may be assigned but not directly referenced -->
<!-- CA1825: Avoid zero-length array allocations — test code uses new T[0] for clarity -->
<!-- CA1829: Use Count property — test code prioritizes readability over perf -->
<!-- CA1835: Use Memory-based overloads — test code uses simpler byte[] overloads -->
<!-- CA1848: Use LoggerMessage delegates — test infrastructure uses simple logging -->
<!-- CA1849: Call async methods in async context — test infrastructure may use sync APIs intentionally -->
<!-- CA1850: Prefer static hash methods — test code uses instance SHA256 for readability -->
<!-- CA1851: Possible multiple enumerations — test assertions may enumerate multiple times intentionally -->
<!-- CA1852: Type can be sealed — test infrastructure classes are not sealed for flexibility -->
<!-- CA1859: Change return type for perf — test code prioritizes interface-based design over perf -->
<!-- CA1860: Use Count > 0 instead of Any() — test code prioritizes readability -->
<!-- CA1861: Prefer static readonly arrays — test code prioritizes inline readability -->
<!-- CA1863: Cache CompositeFormat — test code uses string.Format with inline format strings -->
<!-- CA1864: Use TryAdd — test code uses ContainsKey+Add for clarity -->
<!-- CA1866: Use char overload — not critical in test code -->
<!-- CA1869: Cache JsonSerializerOptions — test code creates options inline for clarity -->
<!-- CA1872: Prefer Convert.ToHexString — test code uses BitConverter.ToString for readability -->
<!-- CA2000: Dispose objects before losing scope — test code manages disposal differently -->
<!-- CA2016: Forward CancellationToken — test infrastructure may intentionally not propagate -->
<!-- CA2201: Do not raise reserved exception types — test infrastructure may throw Exception -->
<!-- CA2211: Non-constant visible fields — test base classes expose mutable static fields -->
<!-- CA2213: Dispose IDisposable fields — test classes rely on xunit lifecycle for cleanup -->
<!-- CA2227: Collection properties should be read only — test DTOs need setters -->
<!-- CA2234: Pass Uri instead of string — test code uses string URLs for readability -->
<!-- CA2254: Logging template should not vary — test infrastructure builds templates dynamically -->
<!-- CA5350: Weak cryptographic algorithm — test code uses TripleDES to validate SAML interop scenarios -->
<NoWarn>$(NoWarn);CA1000;CA1001;CA1002;CA1003;CA1012;CA1024;CA1031;CA1033;CA1041;CA1051;CA1052;CA1054;CA1055;CA1056;CA1062;CA1063;CA1304;CA1305;CA1307;CA1308;CA1309;CA1310;CA1311;CA1508;CA1515;CA1707;CA1708;CA1711;CA1716;CA1724;CA1725;CA1805;CA1806;CA1810;CA1812;CA1816;CA1820;CA1822;CA1823;CA1825;CA1829;CA1835;CA1848;CA1849;CA1850;CA1851;CA1852;CA1859;CA1860;CA1861;CA1863;CA1864;CA1866;CA1869;CA1872;CA2000;CA2016;CA2201;CA2211;CA2213;CA2227;CA2234;CA2254;CA5350</NoWarn>
</PropertyGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA1707</NoWarn><!-- Identifiers should not contain underscores -->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="MartinCostello.Logging.XUnit.v3" />