16. Authentication and authorization
Sections
Reading settings
Natural voice recorded for this course, the same in every browser.
No voice for this language in this browser.
More voices
Code and English terms
Off: the page’s voice reads everything, with no pause between two voices.
No voice for this language in this browser.
More voices
More voices: Microsoft Edge has natural online voices, and Windows (Settings › Time & language › Speech), macOS and Android install others.
Authentication establishes an identity. Policy-based authorization decides whether that identity may perform an operation. Keeping those steps separate explains the two failure codes:
401 Unauthorized: no usable identity was established;403 Forbidden: the identity is valid but does not satisfy the endpoint policy.
Prerequisites
Section titled “Prerequisites”Complete minimal APIs and controllers first. You should already understand HTTP status codes, middleware order and dependency injection.
Validate a token; do not merely decode it
Section titled “Validate a token; do not merely decode it”The local proof configures the JWT bearer handler with an issuer, audience, signing key and lifetime validation. Decoding the three Base64Url segments is not authentication: the signature and all relevant claims still need validation.
The signing keys are generated in memory for one process. Tokens never enter stdout, fixtures, source control or the site. A fixed course time drives the lifetime validator, so an expired token stays expired on every runner.
== JWT bearer authentication rejects unusable credentialsmissing token: 401malformed token: 401expired token: 401wrong signature: 401A policy expresses application permission
Section titled “A policy expresses application permission”The endpoint requires a CanReadScales policy. That policy requires the scope=scales.read claim. A correctly signed token without the claim is authenticated but forbidden; the token with the claim reaches the handler:
== A policy distinguishes authenticated from authorizedwithout required claim: 403with required claim: 200 C majorThis experiment proves local ASP.NET Core behavior, not an OAuth2 deployment. Production still needs a trusted authorization server, key rotation and discovery, HTTPS, a deliberate claim mapping contract, and an audience per resource server. Do not create access tokens inside a production API.
Run the proof
Section titled “Run the proof”dotnet run --project code/csharp-advanced/Advanced -c Release -- l16The checked transcript is expected/l16.txt.
Exercises
Section titled “Exercises”- Add a second policy that accepts either
scales.reador anadminrole, and test both branches. - Add an issuer mismatch and an audience mismatch to the 401 matrix without printing a token.
- Replace the symmetric course key with validation against a local asymmetric public key.
Solutions
- Write a custom
AuthorizationHandleror useRequireAssertion; keep authentication unchanged and verify valid-but-insufficient identities still return 403. - Parameterize the token factory’s issuer and audience, send only the resulting status to the transcript, and retain the fixed lifetime validator.
- Create an ephemeral RSA key pair, sign with the private key, and configure validation with only the public key. Dispose both keys and publish neither the key nor the token.