From c80354aa2fbfc7a95bef4980470bb26d2930e153 Mon Sep 17 00:00:00 2001
From: mdaneri <17148649+mdaneri@users.noreply.github.com>
Date: Wed, 21 Jan 2026 06:53:19 -0800
Subject: [PATCH 1/2] feat(models): support mutualTLS security scheme
---
.../Models/OpenApiSecurityScheme.cs | 11 +++++++++++
.../Models/SecuritySchemeType.cs | 7 ++++++-
.../V32Tests/OpenApiSecuritySchemeTests.cs | 19 +++++++++++++++++++
.../mutualTlsSecurityScheme.yaml | 2 ++
.../PublicApi/PublicApi.approved.txt | 2 ++
5 files changed, 40 insertions(+), 1 deletion(-)
create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiSecurityScheme/mutualTlsSecurityScheme.yaml
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
index 4bee59619..be389861c 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
@@ -126,6 +126,9 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
// openIdConnectUrl
writer.WriteProperty(OpenApiConstants.OpenIdConnectUrl, OpenIdConnectUrl?.ToString());
break;
+ case SecuritySchemeType.MutualTLS:
+ // No additional properties for mutualTLS
+ break;
}
// deprecated - serialize as native field for v3.2+ or as extension for earlier versions
@@ -170,6 +173,14 @@ public virtual void SerializeAsV2(IOpenApiWriter writer)
return;
}
+ if (Type == SecuritySchemeType.MutualTLS)
+ {
+ // Bail because V2 does not support mutualTLS
+ writer.WriteStartObject();
+ writer.WriteEndObject();
+ return;
+ }
+
writer.WriteStartObject();
// type
diff --git a/src/Microsoft.OpenApi/Models/SecuritySchemeType.cs b/src/Microsoft.OpenApi/Models/SecuritySchemeType.cs
index 6c304597a..5640caa9a 100644
--- a/src/Microsoft.OpenApi/Models/SecuritySchemeType.cs
+++ b/src/Microsoft.OpenApi/Models/SecuritySchemeType.cs
@@ -26,6 +26,11 @@ public enum SecuritySchemeType
///
/// Use OAuth2 with OpenId Connect URL to discover OAuth2 configuration value.
///
- [Display("openIdConnect")] OpenIdConnect
+ [Display("openIdConnect")] OpenIdConnect,
+
+ ///
+ /// Use mutual TLS authentication.
+ ///
+ [Display("mutualTLS")] MutualTLS
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSecuritySchemeTests.cs
index e053d7405..ea0937237 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSecuritySchemeTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSecuritySchemeTests.cs
@@ -102,6 +102,25 @@ public async Task ParseOpenIdConnectSecuritySchemeShouldSucceed()
}, securityScheme);
}
+ [Fact]
+ public async Task ParseMutualTlsSecuritySchemeShouldSucceed()
+ {
+ // Act
+ var securityScheme = await OpenApiModelFactory.LoadAsync(
+ Path.Combine(SampleFolderPath, "mutualTlsSecurityScheme.yaml"),
+ OpenApiSpecVersion.OpenApi3_2,
+ new(),
+ SettingsFixture.ReaderSettings);
+
+ // Assert
+ Assert.Equivalent(
+ new OpenApiSecurityScheme
+ {
+ Type = SecuritySchemeType.MutualTLS,
+ Description = "Sample Description"
+ }, securityScheme);
+ }
+
[Fact]
public async Task ParseOAuth2SecuritySchemeWithDeviceAuthorizationUrlShouldSucceed()
{
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiSecurityScheme/mutualTlsSecurityScheme.yaml b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiSecurityScheme/mutualTlsSecurityScheme.yaml
new file mode 100644
index 000000000..72b4e9ae8
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V32Tests/Samples/OpenApiSecurityScheme/mutualTlsSecurityScheme.yaml
@@ -0,0 +1,2 @@
+type: mutualTLS
+description: Sample Description
diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
index 6a8ad3955..59f1bce31 100644
--- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
+++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
@@ -1933,6 +1933,8 @@ namespace Microsoft.OpenApi
OAuth2 = 2,
[Microsoft.OpenApi.Display("openIdConnect")]
OpenIdConnect = 3,
+ [Microsoft.OpenApi.Display("mutualTLS")]
+ MutualTLS = 4,
}
public abstract class SourceExpression : Microsoft.OpenApi.RuntimeExpression
{
From c45a0bc2feaf809a646f0618841efbeb2ec16a8f Mon Sep 17 00:00:00 2001
From: mdaneri <17148649+mdaneri@users.noreply.github.com>
Date: Wed, 21 Jan 2026 07:22:07 -0800
Subject: [PATCH 2/2] fix(writers): throw for mutualTLS in OAS 3.0
---
.../Models/OpenApiSecurityScheme.cs | 5 +++++
.../Models/OpenApiSecuritySchemeTests.cs | 19 +++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
index be389861c..57603e0aa 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs
@@ -128,6 +128,11 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
break;
case SecuritySchemeType.MutualTLS:
// No additional properties for mutualTLS
+ if (version < OpenApiSpecVersion.OpenApi3_1)
+ {
+ // mutualTLS is introduced in OpenAPI 3.1
+ throw new OpenApiException($"mutualTLS security scheme is only supported in OpenAPI 3.1 and later versions. Current version: {version}");
+ }
break;
}
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
index 1432d07cc..cd8499b9f 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs
@@ -101,6 +101,12 @@ public class OpenApiSecuritySchemeTests
OpenIdConnectUrl = new("https://example.com/openIdConnect")
};
+ private static OpenApiSecurityScheme MutualTlsSecurityScheme => new()
+ {
+ Description = "description1",
+ Type = SecuritySchemeType.MutualTLS
+ };
+
private static OpenApiSecuritySchemeReference OpenApiSecuritySchemeReference => new("sampleSecurityScheme");
private static OpenApiSecurityScheme ReferencedSecurityScheme => new()
{
@@ -208,6 +214,19 @@ public async Task SerializeHttpBearerSecuritySchemeAsV3JsonWorks()
Assert.Equal(expected, actual);
}
+ [Fact]
+ public void SerializeMutualTlsSecuritySchemeAsV3Throws()
+ {
+ // Arrange
+ var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
+ var writer = new OpenApiJsonWriter(outputStringWriter);
+
+ // Act & Assert
+ var exception = Assert.Throws(() => MutualTlsSecurityScheme.SerializeAsV3(writer));
+ Assert.Contains("mutualTLS security scheme is only supported in OpenAPI 3.1 and later versions", exception.Message);
+ Assert.Contains($"Current version: {OpenApiSpecVersion.OpenApi3_0}", exception.Message);
+ }
+
[Fact]
public async Task SerializeOAuthSingleFlowSecuritySchemeAsV3JsonWorks()
{