Skip to content

Commit

Permalink
add more tests
Browse files Browse the repository at this point in the history
add cert option in dev app
add decrypt cert
  • Loading branch information
jennyf19 committed Jun 8, 2020
1 parent 39cf144 commit 5295761
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public static CertificateDescription FromKeyVault(string keyVaultUrl, string cer
Container = keyVaultUrl,
ReferenceOrValue = certificateName,
};
// todo support values?
}

/// <summary>
Expand Down Expand Up @@ -73,7 +72,7 @@ public static CertificateDescription FromPath(string path, string password = nul
}

/// <summary>
/// Create a certificate description from a thumprint and store location.
/// Create a certificate description from a thumbprint and store location (certificate manager on Windows for instance)
/// </summary>
/// <param name="certificateThumbprint">Certificate thumbprint.</param>
/// <param name="certificateStoreLocation">Store location where to find the certificate.</param>
Expand All @@ -93,7 +92,8 @@ public static CertificateDescription FromStoreWithThumprint(
}

/// <summary>
/// Create a certificate description from a thumprint and store location.
/// Create a certificate description from a certificate distinguished name (such as CN=name)
/// and store location (certificate manager on Windows for instance).
/// </summary>
/// <param name="certificateDistinguishedName">Certificate distinguished named.</param>
/// <param name="certificateStoreLocation">Store location where to find the certificate.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Azure.Identity;
Expand Down Expand Up @@ -34,10 +35,10 @@ public void LoadIfNeeded(CertificateDescription certificateDescription)
certificateDescription.Certificate = LoadFromPath(certificateDescription.Container, certificateDescription.ReferenceOrValue);
break;
case CertificateSource.StoreWithThumbprint:
certificateDescription.Certificate = LoadLocalCertificateFromThumbprint(certificateDescription.Container, certificateDescription.ReferenceOrValue);
certificateDescription.Certificate = LoadLocalCertificateFromThumbprint(certificateDescription.ReferenceOrValue, certificateDescription.Container);
break;
case CertificateSource.StoreWithDistinguishedName:
certificateDescription.Certificate = LoadFromStoreWithDistinguishedName(certificateDescription.Container, certificateDescription.ReferenceOrValue);
certificateDescription.Certificate = LoadFromStoreWithDistinguishedName(certificateDescription.ReferenceOrValue, certificateDescription.Container);
break;
default:
break;
Expand Down Expand Up @@ -150,5 +151,13 @@ private static X509Certificate2 FindCertificateByCriterium(
var cert = signingCert.OfType<X509Certificate2>().OrderByDescending(c => c.NotBefore).FirstOrDefault();
return cert;
}

internal /*for test only*/ static X509Certificate2 LoadFirstCertificate(IEnumerable<CertificateDescription> certificateDescription)
{
DefaultCertificateLoader defaultCertificateLoader = new DefaultCertificateLoader();
CertificateDescription certDescription = certificateDescription.First();
defaultCertificateLoader.LoadIfNeeded(certDescription);
return certDescription?.Certificate;
}
}
}
16 changes: 14 additions & 2 deletions src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

Expand Down Expand Up @@ -84,11 +85,22 @@ public class MicrosoftIdentityOptions : OpenIdConnectOptions
/// <summary>
/// Description of the certificates used to prove the identity of the Web app or Web API.
/// </summary>
public CertificateDescription[] ClientCertificates { get; set; }
/// <example> An example in the appsetting.json:
/// <code>
/// "ClientCertificates": [
/// {
/// "SourceType": "StoreWithDistinguishedName",
/// "Container": "CurrentUser/My",
/// "ReferenceOrValue": "CN=WebAppCallingWebApiCert"
/// }
/// ]
/// </code>
/// </example>
public IEnumerable<CertificateDescription> ClientCertificates { get; set; }

/// <summary>
/// Description of the certificates used to decrypt an encrypted token in a Web API.
/// </summary>
public CertificateDescription[] DecryptCertificates { get; set; }
public IEnumerable<CertificateDescription> DecryptCertificates { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public ClientCredentialType ValidateEitherClientCertificateOrClientSecret(
return ClientCredentialType.Certificate;
}

public enum ClientCredentialType
internal enum ClientCredentialType
{
None = 0,
Both = 1,
Expand Down
13 changes: 3 additions & 10 deletions src/Microsoft.Identity.Web/TokenAcquisition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -366,8 +367,8 @@ private async Task<IConfidentialClientApplication> BuildConfidentialClientApplic

if (clientCredentialType == ClientCredentialType.Certificate)
{
CertificateDescription certificateDescription = LoadFirstCertificate(_microsoftIdentityOptions);
builder.WithCertificate(certificateDescription.Certificate);
X509Certificate2 certificate = DefaultCertificateLoader.LoadFirstCertificate(_microsoftIdentityOptions.ClientCertificates);
builder.WithCertificate(certificate);
}

app = builder.Build();
Expand Down Expand Up @@ -565,14 +566,6 @@ private IAccount GetAccountByUserFlow(IEnumerable<IAccount> accounts, string use
return null;
}

internal /*for test only*/ static CertificateDescription LoadFirstCertificate(MicrosoftIdentityOptions microsoftIdentityOptions)
{
DefaultCertificateLoader defaultCertificateLoader = new DefaultCertificateLoader();
CertificateDescription certificateDescription = microsoftIdentityOptions.ClientCertificates.First();
defaultCertificateLoader.LoadIfNeeded(certificateDescription);
return certificateDescription;
}

internal /*for test only*/ string CreateRedirectUri()
{
if (Uri.TryCreate(_microsoftIdentityOptions.RedirectUri, UriKind.Absolute, out Uri uri))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public static AuthenticationBuilder AddProtectedWebApi(
{
options.TokenValidationParameters.TokenDecryptionKey = new X509SecurityKey(tokenDecryptionCertificate);
}
else if (tokenDecryptionCertificate == null && microsoftIdentityOptions.DecryptCertificates != null)
{
options.TokenValidationParameters.TokenDecryptionKey =
new X509SecurityKey(DefaultCertificateLoader.LoadFirstCertificate(microsoftIdentityOptions.DecryptCertificates));
}

if (options.Events == null)
{
Expand Down
3 changes: 3 additions & 0 deletions tests/Microsoft.Identity.Web.Test.Common/TestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,8 @@ public static class TestConstants
gBOkSdYjXgOvcJGgE4FJkKAMQzAhkdYq5+stfUotG6vZNL3nVOOA6aELMq/ENhrJLC3rTwLOIgj4Cy+B7BxUS9GxTPphneuZCBzjvqhzP5DmLBs8l8qu10XAsh
y1NFZmB24rMoq8C+HPOpuVLzkwBr+qcCq7ry2326auogvVMGaxhHlwSLR4Q1OhRjKs8JctCk2+5Qs1NHfawa7jWHxdAK6cLm7Rv/c0ig2Jow7wRaI5ciAcEjX7
m1t9gRT1mNeeluL4cZa6WyVXqXc6U2wfR5DY6GOMUubN5Nr1n8Czew8TPfab4OG37BuEMNmBpqoRrRgFnDzVtItOnhuFTa0=";

public const string KeyVaultContainer = "https://buildautomation.vault.azure.net";
public const string KeyVaultReference = "AzureADIdentityDivisionTestAgentCert";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using Microsoft.Identity.Web.Test.Common;
using Xunit;

namespace Microsoft.Identity.Web.Test.Certificates
{
public class DefaultCertificateLoaderTests
{
// https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Credentials/appId/86699d80-dd21-476a-bcd1-7c1a3d471f75/isMSAApp/
// [InlineData(CertificateSource.KeyVault, TestConstants.KeyVaultContainer, TestConstants.KeyVaultReference)]
// [InlineData(CertificateSource.Path, @"c:\temp\WebAppCallingWebApiCert.pfx", "")]
// [InlineData(CertificateSource.StoreWithDistinguishedName, "CurrentUser/My", "CN=WebAppCallingWebApiCert")]
// [InlineData(CertificateSource.StoreWithThumbprint, "CurrentUser/My", "962D129A859174EE8B5596985BD18EFEB6961684")]
[InlineData(CertificateSource.Base64Encoded, null, TestConstants.CertificateX5c)]
[Theory]
public void TestDefaultCertificateLoader(CertificateSource certificateSource, string container, string referenceOrValue)
{
CertificateDescription certificateDescription = new CertificateDescription
{
SourceType = certificateSource,
Container = container,
ReferenceOrValue = referenceOrValue,
};

ICertificateLoader loader = new DefaultCertificateLoader();
loader.LoadIfNeeded(certificateDescription);

Assert.NotNull(certificateDescription.Certificate);
}

[InlineData(CertificateSource.Base64Encoded, null, TestConstants.CertificateX5c)]
[Theory]
public void TestLoadFirstCertificate(
CertificateSource certificateSource,
string container,
string referenceOrValue)
{
IEnumerable<CertificateDescription> certDescriptions = CreateCertificateDescriptions(
certificateSource,
container,
referenceOrValue);

X509Certificate2 certificate = DefaultCertificateLoader.LoadFirstCertificate(certDescriptions);

Assert.NotNull(certificate);
Assert.Equal("CN=ACS2ClientCertificate", certificate.Issuer);
}

private IEnumerable<CertificateDescription> CreateCertificateDescriptions(
CertificateSource certificateSource,
string container,
string referenceOrValue)
{
List<CertificateDescription> certificateDescription = new List<CertificateDescription>();
certificateDescription.Add(new CertificateDescription
{
SourceType = certificateSource,
Container = container,
ReferenceOrValue = referenceOrValue,
});

return certificateDescription;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dependencies": {
"secrets1": {
"type": "secrets"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"dependencies": {
"secrets1": {
"type": "secrets.user"
}
}
}
55 changes: 30 additions & 25 deletions tests/WebAppCallsWebApiCallsGraph/Client/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,39 @@
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "msidentitysamplestesting.onmicrosoft.com",
"TenantId": "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab",
"ClientId": "86699d80-dd21-476a-bcd1-7c1a3d471f75",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath ": "/signout-callback-oidc",
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "msidentitysamplestesting.onmicrosoft.com",
"TenantId": "7f58f645-c190-4ce5-9de4-e2b7acd2a6ab",
"ClientId": "86699d80-dd21-476a-bcd1-7c1a3d471f75",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath ": "/signout-callback-oidc",



// To call an API
"ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
},
"TodoList": {
/*
// To call an API
// "ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
//"ClientCertificates": [
// {
// "SourceType": "",
// "Container": "",
// "ReferenceOrValue": ""
// }
//]
},
"TodoList": {
/*
TodoListScope is the scope of the Web API you want to call. This can be: "api://a4c2469b-cf84-4145-8f5f-cb7bacf814bc/access_as_user",
- a scope for a V2 application (for instance api://b3682cc7-8b30-4bd2-aaba-080c6bf0fd31/access_as_user)
- a scope corresponding to a V1 application (for instance <GUID>/user_impersonation, where <GUID> is the
clientId of a V1 application, created in the https://portal.azure.com portal.
*/
"TodoListScope": "api://a4c2469b-cf84-4145-8f5f-cb7bacf814bc/access_as_user",
"TodoListBaseAddress": "https://localhost:44351"
"TodoListScope": "api://a4c2469b-cf84-4145-8f5f-cb7bacf814bc/access_as_user",
"TodoListBaseAddress": "https://localhost:44351"

},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

0 comments on commit 5295761

Please sign in to comment.