Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
323 stars 55 forks source link

[Bug] Navigation fallback ignores configured routes allowedRoles #670

Open binkiesjoris opened 2 years ago

binkiesjoris commented 2 years ago

Describe the bug Using the following staticwebapp.config.json, if the navigation fallback rule is activated the allowedRoles setting for the '/index.html' route is ignored and the file is served to anonymous users. Directly navigating to /index.html or /index.js works as expected and properly redirects to the login page.

{
    "routes": [
        {
            "route": "/login",
            "rewrite": "/login.html"
        },
        {
            "route": "/index*.{js,html}",
            "allowedRoles": [
                "authenticated"
            ]
        }
    ],
    "navigationFallback": {
        "rewrite": "/index.html"
    },
    "responseOverrides": {
        "401": {
            "redirect": "/login",
            "statusCode": 302
        }
    }
}

It should be noted this is only the case for a deployed SWA, in the local emulator this works as expected and the fallback is properly redirected.

Expected behavior Navigation fallback respects the 'allowedRoles' of configured routes, so that anonymous users are always redirected to /login following the reponseOverrides rule.

Additional context I'm running a SPA that I want to secure behind a static login page in order to prevent unauthorized access to the html/js code. For convenience sake the login page is reusing the styling of the application, but it also needs to serve things like a favicon/manifest.json so I can't use a catch-all route with the allowedRoles set to 'authenticated', but I'd also expect such a route to still be ignored by the fallback.

miwebst commented 2 years ago

Routes are not applied on NavigationFallback by design. If you'd like to achieve that behavior you can use the 404 ResponseOverride but that will have other implications (i.e. non-existent routes will redirect).

Can you elaborate on the security concern with unauthenticated users having access to the base index.html / js ?

binkiesjoris commented 2 years ago

I think we might have made an error in our reasoning, what we wanted to achieve is preventing unauthorized persons from accessing the SPA code would because it will contain information about proprietary data formats, rendering approaches etc,. It would also allow the SPA to not have to reason about the login state. This wont really work though, even with the previously described expected behavior, because SWA only takes care of authentication, not authorization.

Currently I simply added routes for each specific application route and set the allowedRoles on those, e.g.: { "route": "/editor/*", "rewrite": "/index.html", "allowedRoles": [ "authenticated" ] }, { "route": "/", "rewrite": "/index.html", "allowedRoles": [ "authenticated" ] }

and changed the navigation fallback to rewrite a 404 page and the responseOverride to rewrite a static login page:

"navigationFallback": { "rewrite": "/404.html" }, "responseOverrides": { "401": { "rewrite": "/login.html", "statusCode": 200 } }

This at least prevents unauthenticated persons from accessing the SPA, any valid application URL is properly directed to the SPA and any unsupported route shows a 404 page.

rellis-of-rhindleton commented 2 years ago

Found this looking for information on navigationFallback's behavior. Honestly this is surprising. I've used a couple of other website engines where URL rewriting re-runs the route evaluation as if the request had come in for the rewritten URL. That is the behavior I would expect. It allows all route configuration to be in effect. Having navigationFallback ignore the configured routes makes it of little use.

My use case is an SPA where unmatched routes should serve /index.html. There is no reason why that file should have authentication requirements if requested directly, but no authentication requirements if served via fallback.

To get around this we're using a catch-all route (/*) at the end of the route list, and re-specifying the necessary configuration (authentication, headers, etc.).