/// <summary> /// One must be logged in using Azure CLI and set AppAuthenticationTestCertUrl to a secret URL for a certificate in Key Vault before running this test. /// The test creates a new Azure AD application and service principal, uses the cert as the credential, and then uses the library to authenticate to it, using the cert. /// After the cert, the Azure AD application is deleted. Cert is removed from current user store on the machine. /// </summary> /// <param name="certIdentifierType"></param> /// <returns></returns> private async Task GetTokenUsingServicePrincipalWithCertTestImpl(CertIdentifierType certIdentifierType) { string testCertUrl = Environment.GetEnvironmentVariable(Constants.TestCertUrlEnv); // Get a certificate from key vault. // For security, this certificate is not hard coded in the test case, since it gets added as app credential in Azure AD, and may not get removed. AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(Constants.AzureCliConnectionString); KeyVaultHelper keyVaultHelper = new KeyVaultHelper(new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback))); string certAsString = await keyVaultHelper.ExportCertificateAsBlob(testCertUrl).ConfigureAwait(false); X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(certAsString), string.Empty); // Create an application GraphHelper graphHelper = new GraphHelper(_tenantId); Application app = await graphHelper.CreateApplicationAsync(cert).ConfigureAwait(false); // Get token using service principal, this will cache the cert AppAuthenticationResult authResult = null; int count = 5; string thumbprint = cert.Thumbprint?.ToLower(); // Construct connection string using client id and cert info string connectionString = null; switch (certIdentifierType) { case CertIdentifierType.SubjectName: case CertIdentifierType.Thumbprint: string thumbprintOrSubjectName = (certIdentifierType == CertIdentifierType.Thumbprint) ? $"CertificateThumbprint={thumbprint}" : $"CertificateSubjectName={cert.Subject}"; connectionString = $"RunAs=App;AppId={app.AppId};TenantId={_tenantId};{thumbprintOrSubjectName};CertificateStoreLocation={Constants.CurrentUserStore};"; break; case CertIdentifierType.KeyVaultCertificateSecretIdentifier: connectionString = $"RunAs=App;AppId={app.AppId};TenantId={_tenantId};CertificateKeyVaultCertificateSecretIdentifier={testCertUrl};"; break; } AzureServiceTokenProvider astp = new AzureServiceTokenProvider(connectionString); if (certIdentifierType == CertIdentifierType.SubjectName || certIdentifierType == CertIdentifierType.Thumbprint) { // Import the certificate CertUtil.ImportCertificate(cert); } while (authResult == null && count > 0) { try { await astp.GetAccessTokenAsync(Constants.KeyVaultResourceId); authResult = await astp.GetAuthenticationResultAsync(Constants.SqlAzureResourceId, _tenantId); } catch { // It takes time for Azure AD to realize a new application has been added. await Task.Delay(15000); count--; } } if (certIdentifierType == CertIdentifierType.SubjectName || certIdentifierType == CertIdentifierType.Thumbprint) { // Delete the cert CertUtil.DeleteCertificate(cert.Thumbprint); var deletedCert = CertUtil.GetCertificate(cert.Thumbprint); Assert.Null(deletedCert); } // Get token again using a cert which is deleted, but in the cache await astp.GetAccessTokenAsync(Constants.SqlAzureResourceId, _tenantId); // Delete the application and service principal await graphHelper.DeleteApplicationAsync(app); Validator.ValidateToken(authResult.AccessToken, astp.PrincipalUsed, Constants.AppType, _tenantId, app.AppId, cert.Thumbprint, expiresOn: authResult.ExpiresOn); }