Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 3, 2025

Add NotAuthorizedPage to AuthorizeRouteView

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Add NotAuthorizedPage parameter to AuthorizeRouteView for consistent authorization failure handling

Description

Currently, Blazor apps rely on AuthorizeView.NotAuthorized for authorization failures, but this only works when authorization happens at the Blazor level—not when triggered by AuthorizationMiddleware. This PR adds NotAuthorizedPage support to AuthorizeRouteView to provide a consistent experience across SSR and interactive render modes.

Changes

  • AuthorizeRouteView: Added NotAuthorizedPage parameter that allows specifying a component type to render when authorization fails
  • IComponent validation: Added validation to ensure the specified page type implements IComponent with a clear error message
  • Unit tests: Added tests for NotAuthorizedPage rendering, priority over NotAuthorized content, and validation
  • E2E tests: Added test infrastructure and E2E test to verify the feature works in browser

Usage

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" 
                            DefaultLayout="@typeof(MainLayout)"
                            NotAuthorizedPage="@typeof(NotAuthorizedPage)">
            <Authorizing>Authorizing...</Authorizing>
        </AuthorizeRouteView>
    </Found>
</Router>
@* NotAuthorizedPage.razor *@
<h1>Access Denied</h1>
<p>You don't have permission to access this resource.</p>

When NotAuthorizedPage is set, it takes priority over the NotAuthorized render fragment content.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Blazor] Forbidden page for AuthorizeView</issue_title>
<issue_description>The same way we have a NotFound page that plays well with SSR, we should have a Forbidden page that plays well for 403 requests.

Currently on Blazor apps you rely on AuthorizeView and the NotAuthorized fragment, but that only works when authorization happens at the Blazor level and not when it is triggered from the AuthorizationMiddleware.

We want to have a page so that we can provide a consistent experience across SSR and interactive render modes.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@dotnet-policy-service
Copy link
Contributor

Greetings! You've submitted a PR that modifies code that is shared with https://github.com/dotnet/runtime . Please make sure you synchronize this code with the changes in that repo!

Copilot AI and others added 3 commits December 3, 2025 23:34
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Copilot AI changed the title [WIP] Add forbidden page for AuthorizeView in Blazor apps Add ForbiddenPage support for Blazor Router Dec 3, 2025
Copilot AI requested a review from javiercn December 3, 2025 23:46
Copilot finished work on behalf of javiercn December 3, 2025 23:46
Comment on lines 88 to 90
[Parameter]
[DynamicallyAccessedMembers(LinkerFlags.Component)]
public Type? ForbiddenPage { get; set; }
Copy link
Member

Choose a reason for hiding this comment

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

This is wrong. The router should not be involved in this feature. It should all be part of AuthorizeRouteView. For starters we only want the ForbiddenPageType. We don't want a way to trigger Forbidden from Navigation manager.

@copilot redesign the fix based on these constraints. You also need to add E2E tests for it. See src/Components/Agents.md

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Redesigned the implementation based on your feedback. The feature is now solely in AuthorizeRouteView with a NotAuthorizedPage property. All Router, NavigationManager, and endpoint changes have been reverted.

Key changes (commit 0fa988e):

  • Added NotAuthorizedPage parameter to AuthorizeRouteView that renders a specified component when authorization fails
  • Added IComponent validation with clear error message
  • Added unit tests and E2E tests for the new feature

Copilot AI and others added 3 commits December 4, 2025 09:53
Based on reviewer feedback:
- Removed all changes from Router, NavigationManager, and SSR endpoints
- Added NotAuthorizedPage property to AuthorizeRouteView only
- When set, renders the specified page component instead of NotAuthorized content

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
…Page

Co-authored-by: javiercn <6995051+javiercn@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 4, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • accounts.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
  • android.clients.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
  • clients2.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
  • content-autofill.googleapis.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
  • optimizationguide-pa.googleapis.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
  • www.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10123 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.HnOMet --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18390372874401936907,15191872928797170270,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)
    • Triggering command: /opt/google/chrome/chrome /opt/google/chrome/chrome --allow-pre-commit-input --disable-REDACTED-networking --disable-client-side-phishing-detection --disable-default-apps --disable-features=IgnoreDuplicateNavs,Prewarm --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging=stderr --headless=new --ignore-certificate-errors --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --ignore-certificate-errors --use-angle=swiftshader-webgl --ignore-certificate-errors --crashpad-handler-pid=10324 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/.org.chromium.Chromium.scoped_dir.qK9lAp --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14153365801711699640,1662638967419089621,262144 --disable-features=IgnoreDuplicateNavs,PaintHolding,Prewarm --variations-seed-version --trace-process-track-uuid=3190708989122997041 --enable-logging=stderr (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Add ForbiddenPage support for Blazor Router Add NotAuthorizedPage to AuthorizeRouteView Dec 4, 2025
Copilot AI requested a review from javiercn December 4, 2025 10:15
Copilot finished work on behalf of javiercn December 4, 2025 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Blazor] Forbidden page for AuthorizeView

2 participants