From 852abe7c594c5572a94982a2927ccba62ebb39c7 Mon Sep 17 00:00:00 2001 From: Jordan Le Date: Wed, 10 Jun 2026 11:51:19 -0400 Subject: [PATCH 1/2] Harden WebHostSample dev-mode JWT auth to prevent accidental production exposure (FireWatch ce3d20a9) The WebHostSample TokenController is an anonymously reachable JWT issuer paired with a Startup.ConfigureJwtBearerOptons branch that, when ASPNETCORE_ENVIRONMENT is Development, disables every JWT validation (issuer, audience, lifetime, signing-key). Together they allow any caller who can reach a Development-mode deployment of this sample to mint a token that the sample host will accept. FireWatch verdict on ce3d20a9 (IMPORTANT, NOT_EXPLOITABLE_BY_DESIGN): WebHostSample is the explicit sample project with InMemoryProvider; the symmetric key is below the 256-bit HMAC-SHA256 floor (IDX10720 rejects it); no real production system runs this code. The real risk is downstream consumers who clone the repo, swap in a real backing store, and ship without removing the dev-mode auth. This PR adds defensive hardening to protect those downstream consumers: 1. TokenController.cs - file-level "DO NOT USE IN PRODUCTION" banner + an [Obsolete] attribute on the class so any consumer extending/referencing it gets a compiler warning. 2. Startup.cs - the dev-mode TokenValidationParameters block is wrapped in #if DEBUG. The preprocessor strips the entire 'if (...) { ... } else' prefix in Release builds, leaving only the production branch which enforces real Authority / Audience checks. Verified by building both configurations with 0 errors. 3. README.md - explicit warning in the Authorization section pointing at both TokenController and the dev-mode JWT branch and stating that both must be removed before any non-sample deployment. Each layer is independently sufficient to prevent the accidental-production shipping scenario; combined they offer defense in depth. This is the "Accept as Not Exploitable + hardening PR" arm of the plan agreed with the user for finding ce3d20a9. The Partner Portal feedback is being submitted separately. FireWatch finding: https://firewatch-pilot-fd-atb3ceg5egfxc9gg.b02.azurefd.net/Services/fbfae0f8-2ef1-47f4-94e5-029d635c2081/scimreferencecode/ce3d20a9-a7b4-472b-bf96-1b46a1018423 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Controllers/TokenController.cs | 16 ++++++++++++++++ Microsoft.SCIM.WebHostSample/Startup.cs | 10 ++++++++++ README.md | 3 +++ 3 files changed, 29 insertions(+) diff --git a/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs b/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs index c51f8f80..b99254f3 100644 --- a/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs +++ b/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs @@ -1,6 +1,21 @@ //------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ +// +// ⚠️ DO NOT USE IN PRODUCTION ⚠️ +// +// This controller exists solely to issue self-signed JWTs for the SCIM sample/ +// integration-test flow. It is anonymously reachable and uses a symmetric key +// from configuration with no rotation, no revocation, and no authentication +// of the requester. Combined with the dev-mode JWT validation in Startup.cs +// (guarded by #if DEBUG), it allows any anonymous caller to mint a token that +// the sample host will accept. +// +// Any consumer who copies this sample for a production SCIM endpoint MUST +// either delete this controller entirely or replace it with a properly +// authenticated, audience-scoped token issuer. +// +//------------------------------------------------------------ namespace Microsoft.SCIM.WebHostSample.Controllers { @@ -13,6 +28,7 @@ namespace Microsoft.SCIM.WebHostSample.Controllers // Controller for generating a bearer token for authorization during testing. // This is not meant to replace proper Oauth for authentication purposes. + [Obsolete("Sample only - remove this controller or replace with a properly authenticated OAuth token issuer before deploying to any non-sample environment.")] [Route("scim/token")] [ApiController] public class TokenController : ControllerBase diff --git a/Microsoft.SCIM.WebHostSample/Startup.cs b/Microsoft.SCIM.WebHostSample/Startup.cs index 11531c3a..e9c342ba 100644 --- a/Microsoft.SCIM.WebHostSample/Startup.cs +++ b/Microsoft.SCIM.WebHostSample/Startup.cs @@ -50,6 +50,15 @@ void ConfigureAuthenticationOptions(AuthenticationOptions options) void ConfigureJwtBearerOptons( JwtBearerOptions options) { + // The development branch below disables every JWT validation + // (issuer, audience, lifetime, signing-key) and is intended only + // for local end-to-end testing of the sample. Guarding it with + // #if DEBUG ensures that a Release build of this sample cannot + // accidentally ship the bypass to a production environment - the + // preprocessor strips the dev branch (and the surrounding 'if'), + // leaving only the production branch which enforces real + // Authority / Audience checks. +#if DEBUG if (this.environment.IsDevelopment()) { options.TokenValidationParameters = @@ -65,6 +74,7 @@ void ConfigureJwtBearerOptons( JwtBearerOptions options) }; } else +#endif { options.Authority = this.configuration["Token:TokenIssuer"]; options.Audience = this.configuration["Token:TokenAudience"]; diff --git a/README.md b/README.md index c4d3a6a1..09c33f01 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,9 @@ The SCIM standard leaves authentication and authorization relatively open. You c > **[NOTE]** > These authorization methods provided by this repo are solely for testing. When integrating with Azure AD, review the authorization guidance provided [here](https://docs.microsoft.com/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#authorization-for-provisioning-connectors-in-the-application-gallery). +> **⚠️ [DO NOT USE IN PRODUCTION]** +> The `TokenController` in `Microsoft.SCIM.WebHostSample` is an anonymously reachable JWT issuer intended only for local sample/integration-test flows. The dev-mode `TokenValidationParameters` in `Startup.cs.ConfigureJwtBearerOptons` disables every JWT validation check (issuer, audience, lifetime, signing key) and is guarded by `#if DEBUG` so Release builds physically cannot ship the bypass. **Before deploying any SCIM endpoint derived from this sample to a non-sample environment, delete `TokenController` and the dev-mode branch, and replace them with a properly authenticated, audience-scoped OAuth token issuer.** + ## Contributing to the reference code From 1847313aedcbc9025c544b2dc1234f7b66137019 Mon Sep 17 00:00:00 2001 From: Jordan Le Date: Wed, 17 Jun 2026 16:45:04 -0400 Subject: [PATCH 2/2] Address review feedback: Obsolete error:true + Release comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - [Obsolete] now uses error: true — compile error, not suppressible warning - Added clarifying comment for the Release-build code block Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs | 2 +- Microsoft.SCIM.WebHostSample/Startup.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs b/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs index b99254f3..8b859d5f 100644 --- a/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs +++ b/Microsoft.SCIM.WebHostSample/Controllers/TokenController.cs @@ -28,7 +28,7 @@ namespace Microsoft.SCIM.WebHostSample.Controllers // Controller for generating a bearer token for authorization during testing. // This is not meant to replace proper Oauth for authentication purposes. - [Obsolete("Sample only - remove this controller or replace with a properly authenticated OAuth token issuer before deploying to any non-sample environment.")] + [Obsolete("Sample only - remove this controller or replace with a properly authenticated OAuth token issuer before deploying to any non-sample environment.", error: true)] [Route("scim/token")] [ApiController] public class TokenController : ControllerBase diff --git a/Microsoft.SCIM.WebHostSample/Startup.cs b/Microsoft.SCIM.WebHostSample/Startup.cs index e9c342ba..3787451e 100644 --- a/Microsoft.SCIM.WebHostSample/Startup.cs +++ b/Microsoft.SCIM.WebHostSample/Startup.cs @@ -75,6 +75,7 @@ void ConfigureJwtBearerOptons( JwtBearerOptions options) } else #endif + // Release: always enforce production JWT validation (dev-mode block is stripped by preprocessor). { options.Authority = this.configuration["Token:TokenIssuer"]; options.Audience = this.configuration["Token:TokenAudience"];