- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
When an ASP.NET Core API Leaves the Development Machine
Carved Rock Fitness rents outdoor gear to guided trip operators. Its Web API started as a single developer's localhost project but is now being handed to an operations team that will run it behind Azure App Service. Before that happens, the API needs real bearer-token security instead of a shared password, a considered answer for why it isn't using a hosted identity provider yet, two live route versions so existing trip-planning clients keep working, request scripts the ops team can run without writing code, and confirmation that the app behaves correctly once a proxy sits in front of it and secrets no longer live in a local file.
Lab Info
Table of Contents
-
Challenge
Step 1: Meet Carved Rock Fitness's road to production
Carved Rock Fitness rents outdoor gear to guided trip operators. Its Web API grew out of a single developer's localhost project, and the
GearControlleranswers requests today, but it isn't ready to hand to an operations team that will run it in Azure App Service:- Anyone who knows a shared password can call any endpoint.
- Nothing distinguishes an old trip-planning client from a new one.
- Ops has no ready-made way to send a safe, authenticated request.
- Nothing has been checked behind a proxy, or with secrets kept outside a local file.
Over the next four steps you will take the API from runs on my machine to ready to hand to ops. ### Step 2: Issue and evaluate bearer tokens
A JSON Web Token (JWT) is a signed, self-contained statement of who the caller is and what they may do. It is checked cryptographically on every request, with no round trip to a database. You will:
- Issue tokens that carry role and scope claims.
- Require those claims before a rental request is honored.
Carved Rock Fitness mints its own tokens rather than using an OpenID Connect provider such as Azure AD or Auth0. That is a defensible short-term choice, but only if you can say what a real OAuth 2.0/OpenID Connect provider adds (issuer, audience, expiry, and standard claim names) and where the custom tokens fall short. You will build a tool that makes that comparison concrete. ### Step 3: Version the API and its OpenAPI documents
Trip-planning clients built against the current gear listing can't break when a richer response is needed.
- v1 keeps its existing contract.
- v2 adds new fields and tighter authorization.
- One OpenAPI document per version, so a Swagger client exploring v2 isn't shown v1's routes. Each document also declares how to attach a bearer token. ### Step 4: Script the endpoints
Ops needs to send requests without writing a test harness. You will generate HTTP_REPL commands and Visual Studio
.httpfiles that already include the right route, version, andAuthorizationheader. ### Step 5: Run behind the App Service proxyOnce the API runs in Azure App Service:
- The app sees the proxy's address instead of the caller's, unless forwarded headers are handled.
- Secrets come from a managed store instead of
appsettings.Development.json.
If either is wrong, the failures only appear in production. This step makes both visible and diagnosable before that happens.
info> If you get stuck, you can refer to the provided solution code for each task, available in the
solutionfolder. -
Challenge
Step 2: Issue and evaluate bearer tokens
A bearer token is only useful if it is signed and carries the claims a policy can check.
src/TokenService.csalready has the signing key and JWT plumbing wired up; it just hands back an empty placeholder instead of a real token. You will teach it to embed the caller's role and scopes as claims and sign the result, so every downstream check in this lab has a real token to evaluate. A signed token is only as useful as the checks that read it. Rather than wiring claims-based authorization straight into the ASP.NET Core pipeline,src/RentalsAccessPolicy.csisolates the decision itself: given aClaimsPrincipal, should this caller reach the rentals endpoint? Keeping that logic in one place, separate from the pipeline, is what lets you test the authorization rule directly against the claims a token like the one you just issued would carry. Carved Rock Fitness's tokens work today, but 'works' and 'meets what an OpenID Connect provider guarantees' are different questions.src/ComplianceAdvisor.csexists to answer the second one on demand: given any token string, does it carry the issuer, audience, and expiry claims a real OIDC provider would always include? That answer is what lets you tell ops, precisely, what changes the day Carved Rock Fitness adopts one. -
Challenge
Step 3: Version the API and its OpenAPI documents
Existing trip-planning clients call gear listing and expect exactly the fields they were built against -- name and daily rate, nothing else.
src/GearControllerV1.csis that frozen contract. It already has a route and an inventory field wired up; it just needs to actually project and return that fixed shape instead of the stub's empty response. v2 exists precisely because v1 cannot grow without breaking someone.src/GearControllerV2.csreads from the same gear inventory as v1 but is free to expose more of it -- specifically, how many units of each item are actually available to rent. That is new information v1 clients never asked for and would not know what to do with, which is exactly why it lives behind a separate version. Two live route versions need two OpenAPI documents, or a Swagger client exploring v1 would be shown v2's routes and vice versa.src/OpenApiDocumentBuilder.csbuilds that per-version description, including the fact that both versions sit behind the same bearer-token requirement -- a detail the document has to declare before any client generated from it can even attempt to authenticate. -
Challenge
Step 4: Script the endpoints before anyone deploys them
HTTP_REPL lets someone explore an API interactively from a terminal, one typed command at a time --
cd,set header,get. Ops shouldn't have to work out that sequence themselves.src/HttpReplScriptBuilder.csgenerates it for them: point at the base URL, attach the bearer token, then exercise both live gear versions in order. Visual Studio's.httpfiles take a different shape from HTTP_REPL commands:@variable = valuedeclarations up top, then one request per block, each block a bareGETline followed by its headers, separated from the next by a blank line.src/HttpFileBuilder.csproduces exactly that text so ops can open one file and run both versioned requests without typing anything. Scripts are only half the story -- ops also needs to know, before running them, whether the token they were handed will actually pass v1's and v2's authorization checks.src/PreflightReport.cscombines what you already built: it decodes the token's claims the same wayRentalsAccessPolicydoes and reports a plain PASS or FAIL line for each version, so a bad token is caught before anyone runs the scripts against it. -
Challenge
Step 5: Run correctly behind the App Service proxy
Behind Azure App Service's proxy, the app's own
HttpContextsees the proxy's internal scheme and host, not the address the caller actually used. App Service compensates by addingX-Forwarded-ProtoandX-Forwarded-Hostheaders to every forwarded request.src/ForwardedHeadersResolver.csreads those headers back out so anything that needs 'the address the caller used' -- links in a response, logs, diagnostics -- gets the right answer whether or not a proxy sits in front. A localappsettings.jsonsecret disappears the moment Carved Rock Fitness moves to a managed host -- secrets there arrive as environment variables instead, and only fall back to configuration during local development.src/SecretLoader.csencodes that fallback chain once, in one place, so every piece of the app that needs a secret asks this and gets the right source without knowing which environment it's running in. A startup failure that just says 'configuration invalid' sends ops hunting through every possible secret by hand.src/DiagnosticsReporter.cschecks the specific keys Carved Rock Fitness actually requires and names the ones that never resolved, turning a vague failure into a short, actionable list. -
Challenge
Step 6: Run the application
Every task is done, and each one was graded the moment you completed it. What you have not done yet is watch the whole application run at once -- which is the only place the pieces you wrote separately become a single working program.
Start the application
Open the Terminal tab and run:
dotnet run --project src/Lab.csprojThe program runs in the terminal and prints its output there. Every behaviour it shows is code you wrote in the previous steps, so read the output against what each task asked for -- that is the whole lab, working as one program. ### Run the checks yourself
The lab ran each task's test for you as you went, but they are ordinary tests and you can run them whenever you like. The whole suite:
dotnet test tests/Lab.Tests.csproj --nologo --verbosity quietOr one task at a time:
bash runTest.sh Task_2_1Each failure names the task it belongs to, so the output tells you which step to go back to rather than which line to stare at. Two things worth trying before you finish: break something on purpose and watch the matching check fail, then put it back. Knowing what a failure looks like is worth as much as knowing what a pass looks like, and it costs you nothing here.
When you are done, everything you wrote is still in the editor tabs -- the lab is yours to keep reading.
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.