This post is about how you can customise ASP.NETs integration with Azure Active Directory to customise the behaviour that redirects unauthorized requests to the
AccessDenied endpoint. If you're using the tremendous Azure Active Directory for authentication with ASP.NET then there's a good chance you're using the
Microsoft.Identity.Web library. It's this that allows us to drop the following statement into the
ConfigureServices method of our
Which (combined with configuration in our
appsettings.json files) hooks us up with Azure AD for authentication. This is 95% awesome. The 5% is what we're here for. Here's a screenshot of the scenario that troubles us:
We've made a request to
/WeatherForecast; a secured endpoint (a controller decorated with the
Authorize attribute). We're authenticated; the app knows who we are. But we're not authorized / allowed to access this endpoint. We don't have permission. The HTTP specification caters directly for this scenario with status code
The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.
Microsoft.Identity.Web is ploughing another furrow. Instead of returning
403, it's returning
302 Found and redirecting the browser to
302 and whilst you could code around this, it's not desirable.
403 - we don't want
You can have this behaviour by dropping the following code after your
This code hijacks the redirect to AccessDenied and transforms it into a
403 instead. Tremendous! What does this look like?
This is the behaviour we want!
You may want to have some nuance to the way you handle unauthorized requests. Because of the nature of
OnRedirectToAccessDenied this is entirely possible; you have complete access to the requests coming in which you can use to direct behaviour. To take a single example, let's say we want to direct normal browsing behaviour (AKA humans clicking about in Chrome) which is not authorized to a given screen, otherwise provide
403s. What would that look like?
So above, we check the request
Accept headers and see if they contain
"text/html"; which we're using as a signal that the request came from a users browsing. (This may not be bulletproof; better suggestions gratefully received.) If the request does contain a
"text/html"``Accept header then we redirect the client to an
/unauthorized screen, otherwise we return
403 as we did before. Super flexible and powerful!