Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ direction TB
<<rw>> +string CertificateDistinguishedName
<<rw>> +string KeyVaultCertificateName
<<rw>> +string CertificateThumbprint
<<rw>> +string CertificateSubjectName
<<rw>> +string CertificateDiskPath
<<rw>> +string CertificatePassword
<<rw>> +string Base64EncodedValue
Expand Down Expand Up @@ -134,6 +135,8 @@ direction TB
SignedAssertionFromVault = 9
AutoDecryptKeys = 10
CustomSignedAssertion = 11
Comment thread
iNinja marked this conversation as resolved.
ManagedCertificate = 12
StoreWithSubjectName = 13
}
class CredentialType { <<enum>>
Certificate = 0
Expand Down Expand Up @@ -298,6 +301,7 @@ Note:
&lt;&lt;rw&gt;&gt; +string KeyVaultUrl
&lt;&lt;rw&gt;&gt; +string CertificateStorePath
&lt;&lt;rw&gt;&gt; +string CertificateDistinguishedName
&lt;&lt;rw&gt;&gt; +string CertificateSubjectName
&lt;&lt;rw&gt;&gt; +string KeyVaultCertificateName
&lt;&lt;rw&gt;&gt; +string CertificateThumbprint
&lt;&lt;rw&gt;&gt; +string CertificateDiskPath
Expand Down Expand Up @@ -330,6 +334,8 @@ Note:
SignedAssertionFromVault = 9
AutoDecryptKeys = 10
CustomSignedAssertion = 11
ManagedCertificate = 12
StoreWithSubjectName = 13
}
class CredentialType { <<enum>>
Certificate = 0
Expand Down
26 changes: 25 additions & 1 deletion docs/credentialdescription.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ This is the recommended approach
|----------------|------------|-------------|------------|----------------|
| **Federation identity credential with Managed Identity (FIC+MSI)** <br> (`SignedAssertionFromManagedIdentity`) | Azure Managed Identity | • Production on Azure<br>• Zero cert management<br>• Cloud-native apps<br><br>_System-assigned_: Resource lifecycle<br>_User-assigned_: Share across resources | • No secret management<br>• Automatic rotation<br>• Azure integration<br>• Enhanced security | Best choice for Azure workloads |
| **Key Vault** <br> (`SourceType = KeyVault`) | Certificate stored in Azure Key Vault | • Production environments<br>• Need for centralized management<br>• Require automatic rotation<br>• Share across services | • Secure storage & access control<br>• Automatic renewal<br>• Audit logging<br>• Managed backup/recovery | Best choice for production workloads not hosted on an Azure compute supporting managed identity|
| **Certificate Store** <br> (`StoreWithThumbprint` or `StoreWithDistinguishedName`) | Certificate in Windows Certificate Store | • Production on Windows<br>• Using Windows cert management<br><br>_Thumbprint_: Target specific version<br>_DistinguishedName_: Auto rollover | • Native Windows management<br>• Windows security integration<br>• HSM support | Ideal for Windows production environments |
| **Certificate Store** <br> (`StoreWithThumbprint` or `StoreWithDistinguishedName` or `StoreWithSubjectName`) | Certificate in Windows Certificate Store | • Production on Windows<br>• Using Windows cert management<br><br>_Thumbprint_: Target specific version<br>_DistinguishedName_: Auto rollover<br>_SubjectName_: Flexible name-based lookup | • Native Windows management<br>• Windows security integration<br>• HSM support | Ideal for Windows production environments |
| **File Path** <br> (`SourceType = Path`) | PFX/P12 file on disk | • Development/testing<br>• Simple deployment<br>• File-based config preferred | • Simple setup<br>• Easy deployment<br>• Direct file access | **Not for production:**<br>• Password in config<br>• Manual management<br>• Less secure storage |
| **Base64 Encoded** <br> (`SourceType = Base64Encoded`) | Certificate as base64 string | • Development/testing<br>• Config-embedded certificates | • Simple configuration<br>• No file system dependency | **Not for production:**<br>• Exposed in config<br>• Manual management<br>• Less secure |
| **Client Secret** <br> (`SourceType = ClientSecret`) | Simple shared secret string | • Development/testing<br>• Basic security requirements | • Simple to use<br>• Easy to configure | **Not for production:**<br>• Less secure<br>• No auto-rotation<br>• Easy to expose |
Expand Down Expand Up @@ -119,6 +119,18 @@ This is the case where the client credentials are a certificate.
}
```

#### From Certificate Store (Using Subject Name)
```json
{
"ClientCredentials": [
{
"SourceType": "StoreWithSubjectName",
"CertificateStorePath": "CurrentUser/My",
"CertificateSubjectName": "CN=WebAppCallingWebApiCert"
}]
}
```

#### From File Path
```json
{
Expand Down Expand Up @@ -234,6 +246,17 @@ var credentialDescription = CredentialDescription.FromCertificateStore(
distinguishedName: "CN=WebAppCallingWebApiCert");
```

#### From Certificate Store (Using Subject Name)
```csharp
// Using property initialization
var credentialDescription = new CredentialDescription
{
SourceType = CredentialSource.StoreWithSubjectName,
CertificateStorePath = "LocalMachine/My",
CertificateSubjectName = "CN=WebAppCallingWebApiCert"
};
```

#### From File Path
```csharp
// Using property initialization
Expand Down Expand Up @@ -335,6 +358,7 @@ var credentialDescription = new CredentialDescription
- Common values:
- `CurrentUser/My`: User certificates
- `LocalMachine/My`: Computer certificates
- Used with `StoreWithThumbprint`, `StoreWithDistinguishedName`, and `StoreWithSubjectName`

4. **Federation Identity Credential with Managed Identity**:
- For system-assigned managed identity, use `SignedAssertionFromManagedIdentity` source type without specifying `ManagedIdentityClientId`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public CredentialDescription(CredentialDescription other)
_certificate = other._certificate;
CertificateStorePath = other.CertificateStorePath;
CertificateDistinguishedName = other.CertificateDistinguishedName;
CertificateSubjectName = other.CertificateSubjectName;
CertificateThumbprint = other.CertificateThumbprint;
CertificateDiskPath = other.CertificateDiskPath;
CertificatePassword = other.CertificatePassword;
Expand Down Expand Up @@ -108,6 +109,9 @@ public string Id
case CredentialSource.StoreWithDistinguishedName:
_cachedId = $"CertificateStoreWithDistinguishedName={CertificateStorePath}/{CertificateDistinguishedName};Thumbprint={certificateThumbprint}";
break;
case CredentialSource.StoreWithSubjectName:
_cachedId = $"CertificateStoreWithSubjectName={CertificateStorePath}/{CertificateSubjectName};Thumbprint={certificateThumbprint}";
break;
case CredentialSource.ClientSecret:
_cachedId = $"RedactedClientSecret={GenerateHash(ClientSecret)}";
break;
Expand Down Expand Up @@ -203,11 +207,11 @@ public string? KeyVaultUrl

/// <summary>
/// When <see cref="SourceType"/> is <see cref="CredentialSource.StoreWithDistinguishedName"/> or
/// <see cref="CredentialSource.StoreWithThumbprint"/>, specifies the certificate store from which to extract
/// <see cref="CredentialSource.StoreWithThumbprint"/> or <see cref="CredentialSource.StoreWithSubjectName"/>, specifies the certificate store from which to extract
/// the certificate. The format is the concatenation of a value of <see cref="StoreLocation"/> and a value of <see cref="StoreName"/>
/// separated by a slash. For instance, use <c>CurrentUser/My</c> for a user certificate, and <c>LocalMachine/My</c> for a computer certificate.
/// </summary>
/// <remarks>Use this property in conjunction with <see cref="CertificateDistinguishedName"/> or <see cref="CertificateThumbprint"/>.</remarks>
/// <remarks>Use this property in conjunction with <see cref="CertificateDistinguishedName"/>, <see cref="CertificateThumbprint"/>, or <see cref="CertificateSubjectName"/>.</remarks>
/// <seealso cref="SourceType"/>
/// <seealso cref="CertificateStorePath"/>
/// <seealso cref="CertificateDistinguishedName"/>
Expand Down Expand Up @@ -251,6 +255,33 @@ public string? CertificateDistinguishedName
}
private string? _certificateDistinguishedName;

/// <summary>
/// When <see cref="SourceType"/> is <see cref="CredentialSource.StoreWithSubjectName"/>, specifies the subject name of
/// the certificate in the store specified by <see cref="CertificateStorePath"/>.
/// </summary>
/// <example>
Comment thread
iNinja marked this conversation as resolved.
/// <format type="text/markdown">
/// <![CDATA[
/// The JSON fragment below describes a user certificate stored in the personal certificates folder (<b>CurrentUser/My</b>) and specified by its subject name, used as a client credential in a confidential client application:
/// :::code language="json" source="~/../abstractions-samples/test/Microsoft.Identity.Abstractions.Tests/CredentialDescriptionTest.cs" id="subjectname_json":::
///
/// The code below describes programmatically in C#, a computer certificate in the personal certificates folder (<b>LocalMachine/My</b>) accessed by its subject name.
/// :::code language="csharp" source="~/../abstractions-samples/test/Microsoft.Identity.Abstractions.Tests/CredentialDescriptionTest.cs" id="subjectname_csharp":::
/// ]]></format>
/// </example>
/// <seealso cref="SourceType"/>
/// <seealso cref="CertificateStorePath"/>
public string? CertificateSubjectName
{
get => _certificateSubjectName;
set
{
_certificateSubjectName = value;
_cachedId = null;
}
}
private string? _certificateSubjectName;

/// <summary>
/// When <see cref="SourceType"/> is <see cref="CredentialSource.KeyVault"/>, use this property to specify the
/// the name of the certificate in Key Vault in conjunction with <see cref="KeyVaultUrl"/>.
Expand Down Expand Up @@ -595,7 +626,8 @@ public virtual object? CachedValue
/// <term><see cref="CredentialType.Certificate"/></term>
/// <description>when <see cref="SourceType"/> is <see cref="CredentialSource.Certificate"/>, you will use this property to provide the certificate yourself.
/// When <see cref="SourceType"/> is <see cref="CredentialSource.Base64Encoded"/> or <see cref="CredentialSource.KeyVault"/>
/// or <see cref="CredentialSource.Path"/> or <see cref="CredentialSource.StoreWithDistinguishedName"/> or <see cref="CredentialSource.StoreWithThumbprint"/></description>
/// or <see cref="CredentialSource.Path"/> or <see cref="CredentialSource.StoreWithDistinguishedName"/> or <see cref="CredentialSource.StoreWithThumbprint"/>
/// or <see cref="CredentialSource.StoreWithSubjectName"/></description>
/// </item>
/// <item>
/// <term><see cref="CredentialType.Secret"/></term>
Expand All @@ -622,6 +654,7 @@ public CredentialType CredentialType
or CredentialSource.Path
or CredentialSource.StoreWithThumbprint
or CredentialSource.StoreWithDistinguishedName
or CredentialSource.StoreWithSubjectName
or CredentialSource.Certificate
or CredentialSource.Base64Encoded
or CredentialSource.ManagedCertificate => CredentialType.Certificate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public override CredentialDescription Read(ref Utf8JsonReader reader, Type typeT
case string _ when propertyName.Equals(nameof(CredentialDescription.CertificateDistinguishedName), StringComparison.OrdinalIgnoreCase):
credentialDescription.CertificateDistinguishedName = reader.GetString();
break;
case string _ when propertyName.Equals(nameof(CredentialDescription.CertificateSubjectName), StringComparison.OrdinalIgnoreCase):
credentialDescription.CertificateSubjectName = reader.GetString();
break;
case string _ when propertyName.Equals(nameof(CredentialDescription.CertificateThumbprint), StringComparison.OrdinalIgnoreCase):
credentialDescription.CertificateThumbprint = reader.GetString();
break;
Expand Down Expand Up @@ -144,6 +147,13 @@ public override void Write(Utf8JsonWriter writer, CredentialDescription value, J
writer.WriteString(nameof(CredentialDescription.CertificateDistinguishedName), value.CertificateDistinguishedName);
break;

case CredentialSource.StoreWithSubjectName:
if (!string.IsNullOrEmpty(value.CertificateStorePath))
writer.WriteString(nameof(CredentialDescription.CertificateStorePath), value.CertificateStorePath);
if (!string.IsNullOrEmpty(value.CertificateSubjectName))
writer.WriteString(nameof(CredentialDescription.CertificateSubjectName), value.CertificateSubjectName);
break;

case CredentialSource.KeyVault:
if (!string.IsNullOrEmpty(value.KeyVaultUrl))
writer.WriteString(nameof(CredentialDescription.KeyVaultUrl), value.KeyVaultUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,5 +191,22 @@ public enum CredentialSource
/// This is currently a Microsoft-Internal concept which has no meaning outside of Microsoft.
/// </summary>
ManagedCertificate = 12,

/// <summary>
/// Use this value when you provide a certificate from the certificate store, described by its subject name.
/// When setting the <see cref="CredentialDescription.SourceType"/> property to this value, you'll also provide the <see cref="CredentialDescription.CertificateSubjectName"/>
/// and <see cref="CredentialDescription.CertificateStorePath"/> properties.
/// </summary>
/// <example>
/// <format type="text/markdown">
/// <![CDATA[
/// The Json fragment below describes a user certificate stored in the personal certificates folder (<b>CurrentUser/My</b>) and specified by its subject name, used as a client credential in a confidential client application:
/// :::code language="json" source="~/../abstractions-samples/test/Microsoft.Identity.Abstractions.Tests/CredentialDescriptionTest.cs" id="subjectname_json":::
///
/// The code below describes programmatically in C#, a computer certificate in the personal certificates folder (<b>LocalMachine/My</b>) accessed by its subject name.
/// :::code language="csharp" source="~/../abstractions-samples/test/Microsoft.Identity.Abstractions.Tests/CredentialDescriptionTest.cs" id="subjectname_csharp":::
/// ]]></format>
/// </example>
StoreWithSubjectName = 13,
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.set -> void
Microsoft.Identity.Abstractions.CredentialSource.StoreWithSubjectName = 13 -> Microsoft.Identity.Abstractions.CredentialSource
*REMOVED*Microsoft.Identity.Abstractions.CredentialDescription.Certificate.get -> System.Security.Cryptography.X509Certificates.X509Certificate2?
*REMOVED*Microsoft.Identity.Abstractions.CredentialDescription.Certificate.set -> void
*REMOVED*virtual Microsoft.Identity.Abstractions.CredentialDescription.CachedValue.get -> object?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.set -> void
Microsoft.Identity.Abstractions.CredentialSource.StoreWithSubjectName = 13 -> Microsoft.Identity.Abstractions.CredentialSource
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider.CreateBoundAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.Identity.Abstractions.OperationResult<Microsoft.Identity.Abstractions.AuthorizationHeaderInformation!, Microsoft.Identity.Abstractions.AuthorizationHeaderError!>>!
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.set -> void
Microsoft.Identity.Abstractions.CredentialSource.StoreWithSubjectName = 13 -> Microsoft.Identity.Abstractions.CredentialSource
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider.CreateBoundAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.Identity.Abstractions.OperationResult<Microsoft.Identity.Abstractions.AuthorizationHeaderInformation!, Microsoft.Identity.Abstractions.AuthorizationHeaderError!>>!
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.set -> void
Microsoft.Identity.Abstractions.CredentialSource.StoreWithSubjectName = 13 -> Microsoft.Identity.Abstractions.CredentialSource
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider.CreateBoundAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.Identity.Abstractions.OperationResult<Microsoft.Identity.Abstractions.AuthorizationHeaderInformation!, Microsoft.Identity.Abstractions.AuthorizationHeaderError!>>!
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#nullable enable
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.get -> string?
Microsoft.Identity.Abstractions.CredentialDescription.CertificateSubjectName.set -> void
Microsoft.Identity.Abstractions.CredentialSource.StoreWithSubjectName = 13 -> Microsoft.Identity.Abstractions.CredentialSource
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider
Microsoft.Identity.Abstractions.IBoundAuthorizationHeaderProvider.CreateBoundAuthorizationHeaderAsync(Microsoft.Identity.Abstractions.DownstreamApiOptions! downstreamApiOptions, System.Security.Claims.ClaimsPrincipal? claimsPrincipal = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.Identity.Abstractions.OperationResult<Microsoft.Identity.Abstractions.AuthorizationHeaderInformation!, Microsoft.Identity.Abstractions.AuthorizationHeaderError!>>!
Loading
Loading