In the last three lessons the Kladde got many permission decisions: the API demands a sign-in, internal pages do too, only
the manager deletes, the appointment booking is public. Each of these decisions sits in a different place, in an attribute, a
line in Program.cs, an _Imports.razor. If one of them drops out during a refactoring, nobody notices: the app starts, all pages
work, and only a stranger notices that he can delete contacts.
Broken access control is number 1 in the OWASP Top 10 (lesson 2.6.6). In this lesson you therefore secure the Kladde's permissions with two kinds of tests: a matrix that checks the expected result for every important request and every person, and a permissions test over all endpoints that turns red as soon as an endpoint without a permission decision is added.
The test scheme under the magnifying glass
Since lesson 2.6.1, every test request signs in through the scheme Test, with a name in X-Test-User and, since lesson 2.6.3, with
roles in X-Test-Roles. A sign-in scheme is a class that inherits from AuthenticationHandler<TOptions> and answers one question:
Who is sending this request? The answer is one of three results:
| Result | Meaning | In the test scheme |
|---|---|---|
AuthenticateResult.Success(ticket) |
The person is recognized; the ticket contains their ClaimsPrincipal. |
header X-Test-User present |
AuthenticateResult.NoResult() |
This scheme knows nothing about the request; it stays anonymous. | header missing |
AuthenticateResult.Fail(…) |
There was proof, but it's invalid, such as a wrong signature. | doesn't occur |
What's important is what the test scheme replaces. The factory sets only DefaultAuthenticateScheme, the scheme for recognizing:
services.Configure<AuthenticationOptions>(options =>
options.DefaultAuthenticateScheme = TestAuthHandler.SchemeName);
For the challenge (no sign-in) and the forbid (no permission), the Kladde's default scheme stays responsible, the
cookie scheme. That's why in a test, pages answer with exactly the same redirect to /sign-in or /no-access as in production, and
the API with 401 and 403. Anyone who sets AddAuthentication("Test") as the default for everything instead gets a 401 everywhere in the test, pages
too, and tests different behavior from what people will see later.
Two rules go with this scheme:
- It lives only in the test project. There's nothing of it in
Program.cs, not even behindif (app.Environment.IsDevelopment()). A scheme that believes anyone who sends a header is an open door in production, and a wrongly set environment is enough to push it open. - It doesn't test a sign-in, only permissions. Whether signing in with Entra ID works is shown by the tests from lesson 2.6.2. Here the question is: What may a person with these roles do?