Skip to content

Use public/private key in bit Boilerplate for data protection and jwt issue/validation (#12120)#12121

Merged
yasmoradi merged 12 commits intobitfoundation:developfrom
yasmoradi:12120
Feb 23, 2026
Merged

Use public/private key in bit Boilerplate for data protection and jwt issue/validation (#12120)#12121
yasmoradi merged 12 commits intobitfoundation:developfrom
yasmoradi:12120

Conversation

@yasmoradi
Copy link
Copy Markdown
Member

@yasmoradi yasmoradi commented Feb 22, 2026

closes #12120

Summary by CodeRabbit

  • New Features

    • JWTs now use RSA asymmetric signing and a standard JWT Bearer authentication flow
    • Exposes OpenID discovery and JWKS endpoints for token validation
  • Security

    • Data protection and token validation can be certificate-backed (cert-based key management)
    • Token acquisition prioritizes Authorization header, falling back to cookie
  • Documentation

    • Added a certificate management guide covering keys, JWKS, and validation
  • Breaking Changes

    • Removed JwtIssuerSigningKeySecret configuration entry and related validation guidance

@yasmoradi yasmoradi requested review from Copilot and msynk February 22, 2026 20:12
@yasmoradi yasmoradi changed the title use public/private key in bit Boilerplate for data protection and jwt issue/validation (#12120) Use public/private key in bit Boilerplate for data protection and jwt issue/validation (#12120) Feb 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 22, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Replaces symmetric JWT signing with RSA/X509-based signing via a new AppCertificateService, adds OpenID discovery and JWKS endpoints, removes JwtIssuerSigningKeySecret from configuration and docs, updates data protection to use certificates, and adjusts authentication middleware to use JwtBearer with certificate-backed validation.

Changes

Cohort / File(s) Summary
Certificate infrastructure & docs
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj
Adds AppCertificateService that loads/caches X509 from PEMs and exposes RSA security keys; documents certificate usage and JWKS; project files include/copy PEM files conditionally.
JWT signing & middleware
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppJwtSecureDataFormat.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs
Switches from symmetric key to RSA signing (private key from AppCertificateService), adds MapOpenIdConfiguration() to expose OpenID config and JWKS endpoints with public RSA JWK.
Authentication & token handling
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Infrastructure/Services/*, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Extensions/HttpContextExtensions.cs
Replaces prior auth setup with JwtBearer configuration (Authority, TokenValidationParameters); uses HttpContext.GetAccessToken() for token retrieval; adds role-based claim enrichment on token validation; removes old standalone token protector and related class.
Configuration & settings removal
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json, .docs/07- ASP.NET Core Identity - Authentication & Authorization.md, .docs/10- Configuration (appsettings.json).md
Removes JwtIssuerSigningKeySecret property and any runtime validation or docs that required the symmetric signing secret; adds conditional Standalone Identity block for web template.
Data protection
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
Data protection now persists keys to AppDbContext and protects those keys using the application certificate from AppCertificateService.
Removed / moved utilities
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Infrastructure/Services/SimpleJwtSecureDataFormat.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Extensions/HttpContextExtensions.cs
Removed SimpleJwtSecureDataFormat and one duplicate GetAccessToken extension; added consolidated GetAccessToken in shared project.
Build / dev orchestration
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs
ServerWeb project now waits for Redis cache when Redis is enabled (reference chaining).

Sequence Diagrams

sequenceDiagram
    participant Client as Client
    participant API as Server.Api
    participant CertSvc as AppCertificateService
    participant OpenID as /.well-known/openid-configuration & jwks
    participant Validator as Token Validator / Resource Server

    Note over API,CertSvc: Startup / init
    API->>CertSvc: GetAppCertificate()
    CertSvc->>CertSvc: Load PEMs & cache X509
    CertSvc-->>API: X509Certificate2

    Note over API: Token issuance
    API->>CertSvc: GetPrivateSecurityKey()
    CertSvc-->>API: RsaSecurityKey (private)
    API->>API: Sign JWT using RS256
    API-->>Client: Return signed JWT

    Note over Client,OpenID: Discovery
    Client->>OpenID: GET /.well-known/openid-configuration
    OpenID->>CertSvc: GetPublicSecurityKey()
    CertSvc-->>OpenID: RsaSecurityKey (public)
    OpenID-->>Client: issuer, jwks_uri

    Note over Validator,OpenID: Validation flow
    Validator->>OpenID: GET jwks_uri
    OpenID-->>Validator: JWK (public RSA)
    Validator->>Validator: Validate JWT signature with public key
    Validator-->>Client: Access allowed / denied
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped from secrets to keys that pair,
PEMs kept safe with meticulous care,
JWKS sings the public tune,
Tokens signed by the silver moon,
Rabbity cheers — secure and fair! 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: replacing symmetric key-based JWT signing/data protection with public/private key (RSA) infrastructure throughout the boilerplate.
Linked Issues check ✅ Passed The PR implements RSA-based asymmetric key infrastructure for JWT issuance/validation and data protection, fully addressing issue #12120's requirement to use private/public keys instead of symmetric secrets.
Out of Scope Changes check ✅ Passed All changes align with the stated objective: AppCertificateService for key management, JWT signing/validation updates, OpenID configuration endpoints, data protection integration, and removal of symmetric key configuration are all within scope of migrating to asymmetric keys.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs (1)

16-31: Thread-safety issue in lazy initialization of appCert.

GetAppCertificate() has a classic check-then-act race: two threads can both see appCert is null, both load the certificate, and one silently overwrites the other's assignment. While not catastrophic, this can cause subtle issues on platforms where the X509Certificate2 from CreateFromPemFile needs special handling (e.g., ephemeral keys on Linux).

Consider using Lazy<T> or a simple lock for thread-safe initialization:

♻️ Suggested refactor
 public static class AppCertificateService
 {
-    private static X509Certificate2? appCert;
+    private static readonly Lazy<X509Certificate2> appCert = new(LoadCertificate);

     public static X509Certificate2 GetAppCertificate()
     {
-        if (appCert is not null)
-            return appCert;
+        return appCert.Value;
+    }

-        var certPath = Path.Combine(AppContext.BaseDirectory, "AppCertificate.Cert.pem");
-        var keyPath = Path.Combine(AppContext.BaseDirectory, "AppCertificate.Private.pem");
-
-        appCert = X509Certificate2.CreateFromPemFile(certPath, keyPath);
-
-        if (AppEnvironment.IsDevelopment() is false && appCert.Thumbprint is "189C12DB3EEF0A151E3F596DCD807CD2ECA0A26C")
-            throw new InvalidOperationException("You are using the default self-signed certificate in non-development environment. Please use a secure certificate in production.");
-
-        return appCert;
-    }
+    private static X509Certificate2 LoadCertificate()
+    {
+        var certPath = Path.Combine(AppContext.BaseDirectory, "AppCertificate.Cert.pem");
+        var keyPath = Path.Combine(AppContext.BaseDirectory, "AppCertificate.Private.pem");
+
+        var cert = X509Certificate2.CreateFromPemFile(certPath, keyPath);
+
+        if (AppEnvironment.IsDevelopment() is false && cert.Thumbprint is "189C12DB3EEF0A151E3F596DCD807CD2ECA0A26C")
+            throw new InvalidOperationException("You are using the default self-signed certificate in non-development environment. Please use a secure certificate in production.");
+
+        return cert;
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs`
around lines 16 - 31, GetAppCertificate contains a race on the shared field
appCert: two threads can both see appCert null and call
X509Certificate2.CreateFromPemFile, leading to duplicate/overwritten instances;
fix by making initialization thread-safe (either replace appCert with a
Lazy<X509Certificate2> initialized with a factory that calls
X509Certificate2.CreateFromPemFile, or implement a lock and double-check pattern
inside GetAppCertificate to ensure only one thread creates and assigns appCert),
keeping the existing thumbprint/env check after the single creation; reference
symbols: GetAppCertificate, appCert, X509Certificate2.CreateFromPemFile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md`:
- Around line 49-51: Fix the typo in the README header line that currently reads
"# 3. Generate a self-signed X.509 certificate (valid for 1 years)" by changing
"1 years" to "1 year"; optionally consider updating the parenthetical to a
clearer duration like "365 days" or "3650 days (10 years)" if you prefer a
longer non-production validity while keeping the note that production certs
should be rotated regularly.
- Around line 26-36: Update the Data Protection section to clarify that
ProtectKeysWithCertificate (and the public/private certificate pair) is used to
encrypt the data protection key ring (the persisted XML keys), not the
application payloads themselves; state that cookies/anti-forgery tokens are
encrypted/decrypted by symmetric keys derived from that key ring, while the
certificate protects the key ring for cross-instance/load-balanced scenarios,
and mention the public key encrypts the key ring while the private key decrypts
it (or signs/unwraps depending on implementation).

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs`:
- Around line 36-47: GetPrivateSecurityKey() and GetPublicSecurityKey()
currently call GetAppCertificate().GetRSAPrivateKey()/GetRSAPublicKey() on every
invocation, creating new unmanaged RSA instances that are leaked; change this by
adding static readonly (or static) fields (e.g., _privateSecurityKey and
_publicSecurityKey) and initialize them lazily (using ??= or Lazy<T>) inside
GetPrivateSecurityKey() and GetPublicSecurityKey() so the RsaSecurityKey
wrapping the RSA is created only once and reused; keep GetAppCertificate() as
the source but ensure the RSA is obtained once and stored in the static
RsaSecurityKey fields to prevent repeated unmanaged handle allocation.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs`:
- Around line 93-110: The MapOpenIdConfiguration method advertises a jwks_uri
but never serves it; add a second app.MapGet for "/.well-known/jwks" inside
MapOpenIdConfiguration that returns a JSON object with a "keys" array containing
the previously created jwk (the jwk variable from ConvertFromRSASecurityKey) so
consumers can fetch the public key; ensure the endpoint is anonymous/public (no
auth) and returns application/json with the shape { keys = new[] { jwk } } and
keep it alongside the existing "/.well-known/openid-configuration" mapping.
- Around line 95-97: The JWKS entry sets jwk.Kid = "Boilerplate" but the signing
key used in AppJwtSecureDataFormat.Protect() is an RsaSecurityKey without KeyId,
so produced JWTs lack a matching kid; fix by assigning the same KeyId
("Boilerplate") to the RsaSecurityKey returned by
AppCertificateService.GetPrivateSecurityKey() (or set KeyId on that key instance
right before creating new SigningCredentials in
AppJwtSecureDataFormat.Protect()), ensuring the RsaSecurityKey.KeyId matches the
published jwk.Kid and the SigningCredentials include the key id.

---

Nitpick comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs`:
- Around line 16-31: GetAppCertificate contains a race on the shared field
appCert: two threads can both see appCert null and call
X509Certificate2.CreateFromPemFile, leading to duplicate/overwritten instances;
fix by making initialization thread-safe (either replace appCert with a
Lazy<X509Certificate2> initialized with a factory that calls
X509Certificate2.CreateFromPemFile, or implement a lock and double-check pattern
inside GetAppCertificate to ensure only one thread creates and assigns appCert),
keeping the existing thumbprint/env check after the single creation; reference
symbols: GetAppCertificate, appCert, X509Certificate2.CreateFromPemFile.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Bit.Boilerplate server template to move JWT signing/validation and Data Protection key protection from a shared symmetric secret to an X.509 certificate (public/private key pair), and starts exposing OIDC discovery metadata for downstream JWT validation.

Changes:

  • Removed JwtIssuerSigningKeySecret configuration and related validation from server settings.
  • Protected ASP.NET Core Data Protection keys with an X.509 certificate and switched JWT signing/validation to RS256 using that certificate.
  • Added certificate artifacts/docs and introduced an OIDC discovery endpoint stub.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json Removes JWT symmetric signing secret from web config.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Infrastructure/Services/SimpleJwtSecureDataFormat.cs Updates comments to reflect new validation approach in Server.Api.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj Copies certificate artifacts into Server.Web output (needed for integrated hosting).
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json Removes JWT symmetric signing secret from API config.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs Removes secret length/default-value validation and the secret property.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs Protects Data Protection keys with the app certificate.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs Adds OIDC discovery endpoint mapping (currently incomplete).
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs Adds certificate-loading helpers and RSA key extraction for JWT + DP.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppJwtSecureDataFormat.cs Switches JWT signing/validation from HS512 to RS256 using certificate keys.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Boilerplate.Server.Api.csproj Ensures certificate PEM files are copied to output.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md Documents certificate generation and intended OIDC/JWKS integration.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.Public.pem Adds public key PEM (template artifact).
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.Private.pem Adds private key PEM (template artifact).
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.Cert.pem Adds certificate PEM (template artifact).

@yasmoradi yasmoradi marked this pull request as draft February 22, 2026 20:44
@yasmoradi yasmoradi marked this pull request as ready for review February 23, 2026 11:50
@yasmoradi yasmoradi requested a review from Copilot February 23, 2026 11:50
@yasmoradi
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md (1)

26-36: Data Protection description is slightly misleading.

This was previously flagged: ProtectKeysWithCertificate encrypts the data protection key ring (persisted XML keys), not the application payloads directly. Cookies/anti-forgery tokens are encrypted by symmetric keys derived from that key ring.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md`
around lines 26 - 36, The documentation text is misleading: update
AppCertificate.md so it clarifies that ProtectKeysWithCertificate
protects/encrypts the data protection key ring (the persisted XML keys), not the
application payloads directly; explicitly state that cookies/anti-forgery tokens
are encrypted by symmetric keys derived from that key ring and mention the key
ring term (“data protection key ring / persisted XML keys”) and the API name
ProtectKeysWithCertificate to make intent and mechanics clear.
🧹 Nitpick comments (4)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md (1)

74-111: Example code shows two TokenValidationParameters assignments inside one AddJwtBearer block — clarify they're alternatives.

The two assignments to options.TokenValidationParameters separated by // OR (line 96) are inside the same lambda, so the second would overwrite the first. Consider structuring these as two separate code blocks (one for strict validation, one for relaxed) to make the alternatives clearer for readers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md`
around lines 74 - 111, The example currently assigns
options.TokenValidationParameters twice inside the same AddJwtBearer lambda (so
the second overwrites the first); split into two separate, clearly labeled code
examples instead: one AddJwtBearer snippet showing the strict
TokenValidationParameters configuration (with ClockSkew, RequireSignedTokens,
ValidateAudience/ValidateIssuer, ValidAudience/ValidIssuer, etc.) and a separate
snippet showing the relaxed configuration (ValidateAudience = false and
ValidateIssuer/ValidIssuer), or keep only the desired configuration in the
single AddJwtBearer block for clarity; update the comments to indicate these are
alternatives and ensure only one TokenValidationParameters assignment exists per
example.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs (1)

100-116: Add .AllowAnonymous() to the OpenID discovery endpoints for explicit security intent.

The discovery endpoints (/.well-known/openid-configuration and /.well-known/jwks) are correctly accessible without authentication today, since no FallbackPolicy is currently configured. However, explicitly marking these endpoints with .AllowAnonymous() is a best practice that:

  • Makes the security intent explicit for future maintainers
  • Protects against accidental breakage if a fallback authorization policy is added later
  • Aligns with OpenID Connect specifications, where discovery endpoints must be public
Proposed change
         app.MapGet("/.well-known/openid-configuration", (HttpRequest request) =>
         {
             var baseUrl = request.GetBaseUrl();
             return new
             {
                 issuer = app.Configuration["Identity:Issuer"],
                 jwks_uri = new Uri(baseUrl, ".well-known/jwks"),
             };
-        });
+        }).AllowAnonymous();

         app.MapGet("/.well-known/jwks", () =>
         {
             return new
             {
                 keys = new[] { jwk }
             };
-        });
+        }).AllowAnonymous();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs`
around lines 100 - 116, The OpenID discovery endpoints defined via
app.MapGet("/.well-known/openid-configuration", ...) and
app.MapGet("/.well-known/jwks", ...) should explicitly allow anonymous access;
update both MapGet route registrations to chain .AllowAnonymous() so the
discovery JSON (including the jwk variable returned in the jwks handler) remains
public regardless of any future fallback authorization policies.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj (2)

24-24: PackageReference mixed into a ProjectReference item group.

The JwtBearer PackageReference is placed in the item group that primarily holds ProjectReference entries (lines 22–29), while the dedicated PackageReference item group already exists at lines 15–20. Moving it there keeps the grouping consistent.

♻️ Proposed reorganization
     <ItemGroup>
         <PackageReference Include="Bit.BlazorES2019" />
         <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
         <PackageReference Condition=" '$(offlineDb)' == 'true' OR '$(offlineDb)' == ''" Include="Microsoft.EntityFrameworkCore.Tools" PrivateAssets="all" />
         <PackageReference Condition=" '$(sentry)' == 'true' OR '$(sentry)' == '' " Include="Sentry.AspNetCore" />
+        <PackageReference Condition=" '$(api)' == 'Standalone' " Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
     </ItemGroup>

     <ItemGroup>
         <ProjectReference Include="..\Boilerplate.Server.Shared\Boilerplate.Server.Shared.csproj" />
-        <PackageReference Condition=" '$(api)' == 'Standalone' " Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
         <ProjectReference Condition=" '$(api)' == 'Integrated' OR '$(api)' == ''" Include="..\Boilerplate.Server.Api\Boilerplate.Server.Api.csproj" />
         <ProjectReference Include="..\..\Client\Boilerplate.Client.Web\Boilerplate.Client.Web.csproj" />
         <ProjectReference Include="..\..\Client\Boilerplate.Client.Core\Boilerplate.Client.Core.csproj" />
     </ItemGroup>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj`
at line 24, Move the conditional PackageReference for
Microsoft.AspNetCore.Authentication.JwtBearer out of the ProjectReference item
group and into the existing PackageReference item group; locate the element that
reads <PackageReference Condition=" '$(api)' == 'Standalone' "
Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> and cut/paste it into
the other <ItemGroup> that contains the other <PackageReference> entries so
package references are grouped together and ProjectReference groups remain
separate.

44-48: DependentUpon self-reference when glob matches AppCertificate.md.

The glob AppCertificate* will also match AppCertificate.md itself, so that file gets <DependentUpon>AppCertificate.md</DependentUpon> — a self-reference. Visual Studio typically handles this silently (renders the file at the top level), but it's avoidable by excluding the markdown file from the glob and adding it as a plain entry.

♻️ Proposed fix to avoid self-referential DependentUpon
-        <None Include="..\Boilerplate.Server.Api\AppCertificate*">
-            <Link>%(Filename)%(Extension)</Link>
-            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-            <DependentUpon>AppCertificate.md</DependentUpon>
-        </None>
+        <None Include="..\Boilerplate.Server.Api\AppCertificate.md">
+            <Link>%(Filename)%(Extension)</Link>
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+        </None>
+        <None Include="..\Boilerplate.Server.Api\AppCertificate*" Exclude="..\Boilerplate.Server.Api\AppCertificate.md">
+            <Link>%(Filename)%(Extension)</Link>
+            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+            <DependentUpon>AppCertificate.md</DependentUpon>
+        </None>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj`
around lines 44 - 48, The project file uses a glob <None
Include="..\Boilerplate.Server.Api\AppCertificate*"> with
<DependentUpon>AppCertificate.md</DependentUpon>, which creates a self-reference
because the glob also matches AppCertificate.md; fix this by excluding the
markdown from the glob and adding it as a separate item: modify the globbed item
(the <None Include="..\Boilerplate.Server.Api\AppCertificate*"> element) to add
an Exclude="..\Boilerplate.Server.Api\AppCertificate.md" attribute, and then add
a new standalone <None Include="..\Boilerplate.Server.Api\AppCertificate.md">
element without the DependentUpon so AppCertificate.md is a top-level file while
other AppCertificate* files retain the DependentUpon behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md`:
- Around line 26-36: The documentation text is misleading: update
AppCertificate.md so it clarifies that ProtectKeysWithCertificate
protects/encrypts the data protection key ring (the persisted XML keys), not the
application payloads directly; explicitly state that cookies/anti-forgery tokens
are encrypted by symmetric keys derived from that key ring and mention the key
ring term (“data protection key ring / persisted XML keys”) and the API name
ProtectKeysWithCertificate to make intent and mechanics clear.

---

Nitpick comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md`:
- Around line 74-111: The example currently assigns
options.TokenValidationParameters twice inside the same AddJwtBearer lambda (so
the second overwrites the first); split into two separate, clearly labeled code
examples instead: one AddJwtBearer snippet showing the strict
TokenValidationParameters configuration (with ClockSkew, RequireSignedTokens,
ValidateAudience/ValidateIssuer, ValidAudience/ValidIssuer, etc.) and a separate
snippet showing the relaxed configuration (ValidateAudience = false and
ValidateIssuer/ValidIssuer), or keep only the desired configuration in the
single AddJwtBearer block for clarity; update the comments to indicate these are
alternatives and ensure only one TokenValidationParameters assignment exists per
example.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs`:
- Around line 100-116: The OpenID discovery endpoints defined via
app.MapGet("/.well-known/openid-configuration", ...) and
app.MapGet("/.well-known/jwks", ...) should explicitly allow anonymous access;
update both MapGet route registrations to chain .AllowAnonymous() so the
discovery JSON (including the jwk variable returned in the jwks handler) remains
public regardless of any future fallback authorization policies.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj`:
- Line 24: Move the conditional PackageReference for
Microsoft.AspNetCore.Authentication.JwtBearer out of the ProjectReference item
group and into the existing PackageReference item group; locate the element that
reads <PackageReference Condition=" '$(api)' == 'Standalone' "
Include="Microsoft.AspNetCore.Authentication.JwtBearer" /> and cut/paste it into
the other <ItemGroup> that contains the other <PackageReference> entries so
package references are grouped together and ProjectReference groups remain
separate.
- Around line 44-48: The project file uses a glob <None
Include="..\Boilerplate.Server.Api\AppCertificate*"> with
<DependentUpon>AppCertificate.md</DependentUpon>, which creates a self-reference
because the glob also matches AppCertificate.md; fix this by excluding the
markdown from the glob and adding it as a separate item: modify the globbed item
(the <None Include="..\Boilerplate.Server.Api\AppCertificate*"> element) to add
an Exclude="..\Boilerplate.Server.Api\AppCertificate.md" attribute, and then add
a new standalone <None Include="..\Boilerplate.Server.Api\AppCertificate.md">
element without the DependentUpon so AppCertificate.md is a top-level file while
other AppCertificate* files retain the DependentUpon behavior.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1217efd and 65ded9c.

📒 Files selected for processing (11)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/AppCertificate.md
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Extensions/HttpContextExtensions.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/AppCertificateService.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Middlewares.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Program.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Extensions/HttpContextExtensions.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Boilerplate.Server.Web.csproj
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Infrastructure/Services/SimpleJwtSecureDataFormat.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Middlewares.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json
💤 Files with no reviewable changes (2)
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Extensions/HttpContextExtensions.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Infrastructure/Services/SimpleJwtSecureDataFormat.cs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.

yasmoradi and others added 6 commits February 23, 2026 13:19
…te.Server.Web/appsettings.json

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Yas Moradi <yas_moradi@outlook.com>
@yasmoradi yasmoradi merged commit 257f374 into bitfoundation:develop Feb 23, 2026
3 checks passed
@yasmoradi yasmoradi deleted the 12120 branch February 23, 2026 19:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bit Boilerplate must use private/public for data protection and jwt issue/validation

2 participants