Skip to content

Add cloudflare purge cache by tag (#12788) - #12789

Merged
yasmoradi merged 5 commits into
bitfoundation:developfrom
yasmoradi:12788
Jul 31, 2026
Merged

Add cloudflare purge cache by tag (#12788)#12789
yasmoradi merged 5 commits into
bitfoundation:developfrom
yasmoradi:12788

Conversation

@yasmoradi

@yasmoradi yasmoradi commented Jul 31, 2026

Copy link
Copy Markdown
Member

closes #12788

Summary by CodeRabbit

  • New Features

    • Added cache-tag-based invalidation across application and CDN caches, including support for multiple CDN zones.
    • Added validation preventing tenants from using reserved deployment domains.
    • Improved tenant-aware role visibility for global administrators.
    • Enhanced Keycloak claim synchronization and token audience handling.
    • Added localized messages for reserved tenant domains.
  • Bug Fixes

    • Improved refresh-token session persistence and reCAPTCHA failure handling.
    • Fixed localized password validation fallback behavior.
  • Documentation

    • Reorganized authentication, authorization, caching, multi-tenancy, and external identity provider guidance.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

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.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b8424bc-78c4-4321-9864-2eb91d7a7076

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

The boilerplate updates identity documentation and services, adds token-type JWT audiences, improves Keycloak claim handling, introduces cache-tag CDN purging across zones, reserves deployment domains for tenants, validates production configuration, and updates integration tests.

Changes

Identity and authentication

Layer / File(s) Summary
Authentication, tokens, and identity services
src/Templates/Boilerplate/Bit.Boilerplate/.docs/07- ASP.NET Core Identity - Authentication & Authorization.md, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/...
Identity flows now document and implement token-specific audiences, explicit refresh persistence, claim handling, localization fallback, and form-encoded reCAPTCHA requests.
Authorization, tenancy, and provider integration
src/Templates/Boilerplate/Bit.Boilerplate/.docs/07- ASP.NET Core Identity - Authentication & Authorization.md, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/..., src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.AppHost/Infrastructure/Realms/dev-realm.json
Authorization, tenant isolation, external providers, Keycloak revalidation, and email verification mapping are documented and wired. Global administrators receive tenant-scoped role results when a tenant is selected.

Response caching

Layer / File(s) Summary
Shared cache-tag generation and response behavior
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Services/AppResponseCachePolicy.cs, src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md
Output caching and edge caching use normalized cache tags. Tags encode unsafe characters, cover culture and query variants, and control Cache-Tag emission.
Multi-zone purge integration and validation
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/ResponseCacheService.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Caching/ProductResponseCacheTests.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Server/.../appsettings.json
Cache purging uses tags across configured Cloudflare zones in batches. Configuration and health checks use ZoneIds. Tests cover URL variants and edge-cache headers.

Reserved tenant domains

Layer / File(s) Summary
Reserved-domain validation and localization
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/..., src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings*.resx
Tenant validation rejects deployment hosts, trusted origins, and their reserved subdomains. Localized ReservedTenantDomain messages were added.

Template configuration and tests

Layer / File(s) Summary
Configuration validation and assertion updates
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Identity/TwoFactorAuthTests.cs, src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Mcp/GetCurrentDateTimeMcpIntegrationTests.cs
Production settings reject the default SMTP connection string. Updated tests use direct MSTest assertions without changing tested behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AppResponseCachePolicy
  participant OutputCache
  participant ResponseCacheService
  participant Cloudflare
  Client->>AppResponseCachePolicy: Request culture/query variant
  AppResponseCachePolicy->>OutputCache: Store response with canonical cache tag
  AppResponseCachePolicy-->>Client: Return Cache-Tag when edge-cacheable
  ResponseCacheService->>OutputCache: Evict entries by cache tag
  ResponseCacheService->>Cloudflare: Purge cache tags across configured zones
Loading

Poem

A rabbit tags the cache with care,
Across each path and zone.
JWTs find audiences fair,
Reserved names guard the home.
Tests hop cleanly, settings shine—
The boilerplate grows more fine.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The pull request includes unrelated identity, tenant-domain, localization, Keycloak, reCAPTCHA, documentation, and test assertion changes beyond Cloudflare cache-tag purging. Remove unrelated identity, tenant, localization, Keycloak, reCAPTCHA, documentation, and assertion changes, or link them to separate issues.
Docstring Coverage ⚠️ Warning Docstring coverage is 48.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding Cloudflare cache purging by tag.
Linked Issues check ✅ Passed The changes implement Cloudflare cache-tag purging with multi-zone support, cache-tag generation, configuration updates, and integration tests for issue [#12788].
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs (1)

209-222: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the repeated deployment-host array.

The [Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)] array is built twice in this method: once for the name check (line 210) and once for the domain check (line 220). Compute it once and reuse it for both checks.

♻️ Proposed refactor
     private async Task Validate(Tenant tenant, CancellationToken cancellationToken)
     {
         var entry = DbContext.Entry(tenant);
+        var deploymentHosts = new[] { Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host }
+            .Concat(serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost))
+            .ToArray();

         // The custom domain is matched against the request host (case-insensitive), so it's stored as a lowercase host; a blank one becomes null.
         // SECURITY: this endpoint is self-service (tenant admins), and a custom domain wins over the sub domain during resolution.
         // Enforcing uniqueness alone is NOT enough for production - verify domain ownership and add it to TrustedOrigins first.
         tenant.Domain = string.IsNullOrWhiteSpace(tenant.Domain) ? null : tenant.Domain.Trim().ToLowerInvariant();

         if ((entry.State is EntityState.Added || entry.Property(t => t.Name).IsModified)
-            && ReservedTenantNames.IsReserved(tenant.Name, [Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)]))
+            && ReservedTenantNames.IsReserved(tenant.Name, deploymentHosts))
             throw new ResourceValidationException((nameof(TenantDto.Name), [Localizer[nameof(AppStrings.ReservedTenantName), tenant.Name!]]));

         // Remote validation example: Any errors thrown here will be displayed in the client's edit form component.
         if ((entry.State is EntityState.Added || entry.Property(t => t.Name).IsModified)
             && await DbContext.Tenants.AnyAsync(t => t.Id != tenant.Id && t.Name == tenant.Name, cancellationToken))
             throw new ResourceValidationException((nameof(TenantDto.Name), [Localizer[nameof(AppStrings.DuplicateTenantName), tenant.Name!]]));

         if (tenant.Domain is not null
             && (entry.State is EntityState.Added || entry.Property(t => t.Domain).IsModified)
-            && ReservedTenantNames.IsReservedDomain(tenant.Domain, [Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, .. serverSharedSettings.TrustedOrigins.Select(ServerSharedSettings.GetTrustedOriginHost)]))
+            && ReservedTenantNames.IsReservedDomain(tenant.Domain, deploymentHosts))
             throw new ResourceValidationException((nameof(TenantDto.Domain), [Localizer[nameof(AppStrings.ReservedTenantDomain), tenant.Domain]]));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs`
around lines 209 - 222, In the containing method, extract the repeated
deployment-host array into a local variable before the reserved-name and
reserved-domain checks, then pass that variable to both
ReservedTenantNames.IsReserved and ReservedTenantNames.IsReservedDomain.
Preserve the existing host ordering and TrustedOrigins projection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.ExternalSignIn.cs`:
- Line 93: Update ExternalSignInCallback so provider-asserted email or phone
identifiers are only used for FindUser(...) matching and SetEmailConfirmedAsync
confirmation after validating the provider’s verified-assurance claim (for
example, email_verified == true). If verification is absent or false, do not
link or confirm the identifier; preserve the existing explicit account-link flow
if available.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/GoogleRecaptchaService.cs`:
- Around line 21-24: Update the response handling in GoogleRecaptchaService
around the PostAsync call to dispose the HttpResponseMessage on every path,
including when IsSuccessStatusCode is false. Use scoped disposal for response so
its resources are released before returning or processing the successful result.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs`:
- Around line 218-222: Update TenantManagementController.Validate to add a
ReservedTenantNames.IsReservedDomain check alongside its duplicate-domain
validation, using Request.GetBaseUrl().Host, Request.GetWebAppUrl().Host, and
trusted-origin hosts from serverSharedSettings.TrustedOrigins. Apply the same
reserved-domain validation error as TenantController for added or modified
non-null tenant domains.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/ResponseCacheService.cs`:
- Around line 81-85: Update the purge request flow in ResponseCacheService to
deserialize the Cloudflare response envelope after SendAsync and validate its
success property, rather than relying only on EnsureSuccessStatusCode(). When
success is false, throw an exception that includes all returned error codes and
messages; preserve HTTP status validation and the existing successful-response
behavior.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs`:
- Around line 190-198: Update ServerApiSettings and its configuration binding to
preserve the previous Cloudflare:ZoneId key: map it into ZoneIds as a temporary
compatibility alias, or explicitly fail startup with a migration error when it
is supplied. Ensure existing ApiToken plus ZoneId configurations do not silently
make Configured false, and keep ResponseCacheService purge behavior intact.

In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Services/AppResponseCachePolicy.cs`:
- Around line 33-36: Update AppResponseCachePolicy.CreateCacheTag to URI-encode
the relative path, including non-ASCII UTF-8 characters, before applying the
existing cache-tag normalization. Ensure the same encoded representation is used
by both Cache-Tag response headers and ResponseCacheService.PurgeCache so
generated tags remain matchable.

---

Nitpick comments:
In
`@src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs`:
- Around line 209-222: In the containing method, extract the repeated
deployment-host array into a local variable before the reserved-name and
reserved-domain checks, then pass that variable to both
ReservedTenantNames.IsReserved and ReservedTenantNames.IsReservedDomain.
Preserve the existing host ordering and TrustedOrigins projection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8eb6fb1c-44c7-4976-b81b-300b7b172b6b

📥 Commits

Reviewing files that changed from the base of the PR and between 52e105e and 76b334a.

📒 Files selected for processing (31)
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/07- ASP.NET Core Identity - Authentication & Authorization.md
  • src/Templates/Boilerplate/Bit.Boilerplate/.docs/14- Response Caching System.md
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.ExternalSignIn.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/IdentityController.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/RoleManagementController.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/AppIdentityErrorDescriber.cs
  • 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/Features/Identity/Services/AppUserClaimsPrincipalFactory.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Identity/Services/GoogleRecaptchaService.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/ReservedTenantNames.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Features/Tenants/TenantController.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Infrastructure/Services/ResponseCacheService.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
  • 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.AppHost/Infrastructure/Realms/dev-realm.json
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Shared/Infrastructure/Services/AppResponseCachePolicy.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.ar.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.de.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.es.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fr.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.hi.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.sv.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.zh.resx
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Caching/ProductResponseCacheTests.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Identity/TwoFactorAuthTests.cs
  • src/Templates/Boilerplate/Bit.Boilerplate/src/Tests/Features/Mcp/GetCurrentDateTimeMcpIntegrationTests.cs

@yasmoradi
yasmoradi merged commit 44cb1a1 into bitfoundation:develop Jul 31, 2026
3 checks passed
@yasmoradi
yasmoradi deleted the 12788 branch July 31, 2026 08:50
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 CDN purge by tag is missing

1 participant