Skip to content

refactor(Layout): update authentication state on navigation - #8505

Merged
ArgoZhang merged 3 commits into
mainfrom
refactor-layout
Oct 9, 2026
Merged

ArgoZhang merged 3 commits into
mainfrom
refactor-layout

Conversation

@ArgoZhang

@ArgoZhang ArgoZhang commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Link issues

fixes #8504

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Keep layout authentication state synchronized with navigation and render the correct authorized or unauthorized content.

Bug Fixes:

  • Update the layout authorization state when navigation changes so unauthorized pages render the appropriate content and authorized navigation restores the main layout.

Enhancements:

  • Handle authorization callbacks being absent or removed without applying navigation authorization logic.

Tests:

  • Add unit coverage for unauthorized rendering, authorized-state restoration, default redirect behavior, and null authorization callbacks.

@bb-auto bb-auto Bot added the enhancement New feature or request label Oct 9, 2026
@bb-auto bb-auto Bot added this to the v11.0.0 milestone Oct 9, 2026

@bb-auto bb-auto 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.

Auto approved by bb-auto

@ArgoZhang
ArgoZhang merged commit ec57b09 into main Oct 9, 2026
4 checks passed
@ArgoZhang
ArgoZhang deleted the refactor-layout branch October 9, 2026 08:39
@sourcery-ai

sourcery-ai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors Layout navigation authorization to keep rendered content synchronized with the latest authorization result, including recovery to authorized content, non-redirect unauthorized rendering, and safe handling of missing or removed callbacks; adds focused bUnit coverage for these scenarios.

Sequence diagram for Layout authorization on navigation

sequenceDiagram
    participant Navigation
    participant Layout
    participant OnAuthorizing
    participant UI

    Navigation->>Layout: LocationChanged(location)
    alt OnAuthorizing is null
        Layout-->>Layout: return
    else callback exists
        Layout->>OnAuthorizing: OnAuthorizing(location)
        OnAuthorizing-->>Layout: auth
        alt auth is true
            opt _authenticated is false
                Layout->>Layout: StateHasChanged()
                Layout->>UI: Render authorized content
            end
        else IsAutoNavigateWhenNotAuthorize is true
            Layout->>Navigation: NavigateTo(NotAuthorizeUrl, true)
        else _authenticated is true
            Layout->>Layout: StateHasChanged()
            Layout->>UI: Render NotAuthorized template
        end
    end
Loading

File-Level Changes

Change Details Files
Updates layout authorization state in response to navigation outcomes.
  • Short-circuits navigation handling when no authorization callback is configured.
  • Restores authenticated content when authorization succeeds after a denied route.
  • Updates the authenticated flag and renders the unauthorized template when navigation is denied without automatic redirection.
  • Preserves automatic navigation to the configured unauthorized URL when enabled.
src/BootstrapBlazor/Components/Layout/Layout.razor.cs
Adds coverage for navigation-time authorization behavior and callback changes.
  • Tests denied navigation rendering without redirect and restoration after later authorization.
  • Tests default redirect behavior, null authorization callbacks, and clearing the callback at runtime.
test/UnitTest/Components/LayoutTest.cs
Updates project configuration metadata.
  • Applies the project-file change included in the pull request.
src/BootstrapBlazor/BootstrapBlazor.csproj

Assessment against linked issues

Issue Objective Addressed Explanation
#8504 Update the layout's authentication state whenever navigation occurs by evaluating the current URL through the authorization callback. ✅
#8504 Reflect navigation authorization changes in the rendered layout, showing the unauthorized content when access is denied without automatic redirection and restoring the main content when access is granted. ✅
#8504 Preserve the existing automatic navigation behavior for unauthorized locations and safely handle cases where the authorization callback is not configured or is later cleared. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/BootstrapBlazor/Components/Layout/Layout.razor.cs" line_range="739-744" />
<code_context>
+        InvokeAsync(async () =>
         {
-            InvokeAsync(async () =>
+            var auth = await OnAuthorizing(e.Location);
+            if (auth)
             {
-                var auth = await OnAuthorizing(e.Location);
-                if (!auth && IsAutoNavigateWhenNotAuthorize)
</code_context>
<issue_to_address>
**Protected routes render as authorized**

When onAuthorizing returns true for a route whose framework authorization check denies the user, `Navigation_LocationChanged` sets `_authenticated` from `OnAuthorizing` alone, bypassing the route handler's `IsAuthorizedAsync` result, so the layout renders `Main` for a route the user cannot access.

Combine the callback result with the destination route's framework authorization result before setting `_authenticated`.

Also at `src/BootstrapBlazor/Components/Layout/Layout.razor.cs:745`.
</issue_to_address>

### Comment 2
<location path="src/BootstrapBlazor/Components/Layout/Layout.razor.cs" line_range="737" />
<code_context>
+        }

-        if (OnAuthorizing != null)
+        InvokeAsync(async () =>
         {
-            InvokeAsync(async () =>
</code_context>
<issue_to_address>
**Stale checks overwrite current authorization**

When multiple navigations overlap and an earlier `OnAuthorizing` call completes after a later one, `Navigation_LocationChanged` applies each result to the shared `_authenticated` state and may redirect based on the stale location, so the layout shows the wrong content for the current page.

Before applying a result or redirecting, verify that its location is still current.

Also at `src/BootstrapBlazor/Components/Layout/Layout.razor.cs:739-758`.
</issue_to_address>

### Comment 3
<location path="src/BootstrapBlazor/Components/Layout/Layout.razor.cs" line_range="739" />
<code_context>
+        InvokeAsync(async () =>
         {
-            InvokeAsync(async () =>
+            var auth = await OnAuthorizing(e.Location);
+            if (auth)
             {
</code_context>
<issue_to_address>
**Failed checks retain access**

When the authorization callback hangs or throws after the layout was previously authorized, `_authenticated` is not cleared before `OnAuthorizing` is awaited, and the dispatched task has no failure handling. If the callback stalls or throws during a dependency failure, the previous authorized state remains active and the layout continues rendering protected content.

Fail closed while authorization is pending or fails, and handle callback exceptions and timeouts explicitly.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and the navigation callback now controls whether the layout renders its main content or the unauthorized template, so an incorrect or stale authorization result could expose protected UI or redirect users incorrectly. Reverting prevents the new behavior, but any access or content exposure that occurred while it was active cannot be undone.

Blocking findings: src/BootstrapBlazor/Components/Layout/Layout.razor.cs:744, src/BootstrapBlazor/Components/Layout/Layout.razor.cs:737, src/BootstrapBlazor/Components/Layout/Layout.razor.cs:739


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment on lines +739 to 744
var auth = await OnAuthorizing(e.Location);
if (auth)
{
var auth = await OnAuthorizing(e.Location);
if (!auth && IsAutoNavigateWhenNotAuthorize)
// 当前地址已授权时恢复 UI 状态
if (!_authenticated)
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Critical · Protected routes render as authorized

When onAuthorizing returns true for a route whose framework authorization check denies the user, Navigation_LocationChanged sets _authenticated from OnAuthorizing alone, bypassing the route handler's IsAuthorizedAsync result, so the layout renders Main for a route the user cannot access.

Combine the callback result with the destination route's framework authorization result before setting _authenticated.

Also at src/BootstrapBlazor/Components/Layout/Layout.razor.cs:745.

Prompt for AI agents
In `src/BootstrapBlazor/Components/Layout/Layout.razor.cs` at lines 739-744:

**Protected routes render as authorized**

When onAuthorizing returns true for a route whose framework authorization check denies the user, `Navigation_LocationChanged` sets `_authenticated` from `OnAuthorizing` alone, bypassing the route handler's `IsAuthorizedAsync` result, so the layout renders `Main` for a route the user cannot access.

Combine the callback result with the destination route's framework authorization result before setting `_authenticated`.

Also at `src/BootstrapBlazor/Components/Layout/Layout.razor.cs:745`.

}

if (OnAuthorizing != null)
InvokeAsync(async () =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Critical · Stale checks overwrite current authorization

When multiple navigations overlap and an earlier OnAuthorizing call completes after a later one, Navigation_LocationChanged applies each result to the shared _authenticated state and may redirect based on the stale location, so the layout shows the wrong content for the current page.

Before applying a result or redirecting, verify that its location is still current.

Also at src/BootstrapBlazor/Components/Layout/Layout.razor.cs:739-758.

Prompt for AI agents
In `src/BootstrapBlazor/Components/Layout/Layout.razor.cs` at line 737:

**Stale checks overwrite current authorization**

When multiple navigations overlap and an earlier `OnAuthorizing` call completes after a later one, `Navigation_LocationChanged` applies each result to the shared `_authenticated` state and may redirect based on the stale location, so the layout shows the wrong content for the current page.

Before applying a result or redirecting, verify that its location is still current.

Also at `src/BootstrapBlazor/Components/Layout/Layout.razor.cs:739-758`.

InvokeAsync(async () =>
{
InvokeAsync(async () =>
var auth = await OnAuthorizing(e.Location);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Critical · Failed checks retain access

When the authorization callback hangs or throws after the layout was previously authorized, _authenticated is not cleared before OnAuthorizing is awaited, and the dispatched task has no failure handling. If the callback stalls or throws during a dependency failure, the previous authorized state remains active and the layout continues rendering protected content.

Fail closed while authorization is pending or fails, and handle callback exceptions and timeouts explicitly.

Prompt for AI agents
In `src/BootstrapBlazor/Components/Layout/Layout.razor.cs` at line 739:

**Failed checks retain access**

When the authorization callback hangs or throws after the layout was previously authorized, `_authenticated` is not cleared before `OnAuthorizing` is awaited, and the dispatched task has no failure handling. If the callback stalls or throws during a dependency failure, the previous authorized state remains active and the layout continues rendering protected content.

Fail closed while authorization is pending or fails, and handle callback exceptions and timeouts explicitly.

@codecov

codecov Bot commented Oct 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1eb200a) to head (ef1d43f).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #8505   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          777       777           
  Lines        35160     35175   +15     
=========================================
+ Hits         35160     35175   +15     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(Layout): update authentication state on navigation

1 participant