Return HTTP 400 (not 500) for mixed SMART v1/v2 scopes#5650
Closed
mikaelweave wants to merge 2 commits into
Closed
Return HTTP 400 (not 500) for mixed SMART v1/v2 scopes#5650mikaelweave wants to merge 2 commits into
mikaelweave wants to merge 2 commits into
Conversation
Mixed SMART v1 and v2 clinical scopes on a request returned HTTP 500 instead of 400. SmartClinicalScopesMiddleware threw BadHttpRequestException, but because the middleware runs before MVC, the FhirException->400 mapping (OperationOutcomeExceptionFilterAttribute) never runs and the exception surfaced as a 500. Fix: write a 400 OperationOutcome response directly from the middleware and short-circuit the pipeline, mirroring ThrottlingMiddleware. Adds unit and E2E tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5650 +/- ##
==========================================
+ Coverage 77.51% 77.62% +0.11%
==========================================
Files 1004 1004
Lines 36772 36873 +101
Branches 5563 5587 +24
==========================================
+ Hits 28503 28624 +121
+ Misses 6918 6893 -25
- Partials 1351 1356 +5 🚀 New features to boost your workflow:
|
… 400 Replace the hand-written JSON response in SmartClinicalScopesMiddleware with the canonical FHIR error flow: build an OperationOutcome and execute an OperationOutcomeResult through the MVC result pipeline (the same approach BaseExceptionMiddleware uses), so the 400 response goes through the FHIR output formatters and content negotiation. Add a protected internal virtual ExecuteResultAsync seam and update the unit test to verify the produced OperationOutcomeResult, mirroring BaseExceptionMiddlewareTests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem (ICM)
A request whose token mixes SMART v1 scopes (e.g.
patient/Observation.read) and SMART v2 granular scopes (e.g.patient/Observation.rs) returned HTTP 500 instead of 400.Root cause
SmartClinicalScopesMiddlewaredetected the mixed-scope case and threwBadHttpRequestException. However, the middleware runs in the ASP.NET pipeline before MVC. TheFhirException→ 400 mapping (OperationOutcomeExceptionFilterAttribute) is an MVC action filter, so it never runs for exceptions thrown from middleware. The exception was instead caught byUseExceptionHandler("/CustomError")/BaseExceptionMiddleware, both of which default to 500.The originally-proposed fix (swapping
BadHttpRequestException→BadRequestException) does not fix this — any exception type thrown from the middleware still surfaces as a 500.Fix
Write a
400 Bad RequestOperationOutcomeresponse directly from the middleware and short-circuit the pipeline (return without calling_next), mirroring the existing pattern inThrottlingMiddleware. The body is a FHIROperationOutcomewithseverity: error,code: invalid, and the existingMixedSMARTV1AndV2ScopesAreNotAllowedmessage.Tests
SmartClinicalScopesMiddlewareTests): updated the mixed-scope theory to assert the middleware now returns400with anOperationOutcomebody ("code":"invalid"+ the message), instead of throwing.SmartClinicalScopesValidationTests): acquires a real token forSmartUserClientwith a mixed scope (patient/Patient.read patient/Patient.rs) from the dev token endpoint, issues a FHIR request, and asserts400 BadRequestwith anOperationOutcome. Skips when security is not enabled on the test server.Verification
Microsoft.Health.Fhir.Apibuilds clean (net8.0 + net9.0).Follow-up (out of ICM scope)
Two sibling
BadHttpRequestExceptionthrows in the same middleware for thefhirUserclaim cases have the identical latent 500 behavior. Left unchanged to keep this PR focused on the ICM; worth a follow-up.