Selenium ‘ExpectedConditions’ is obsolete

In C# project ExpectedConditions gives warning:
‘ExpectedConditions’ is obsolete: ‘The ExpectedConditions implementation in the .NET bindings is deprecated and will be removed in a future release. This portion of the code has been migrated to the DotNetSeleniumExtras repository on GitHub (https://github.com/DotNetSeleniumTools/DotNetSeleniumExtras)’

            WebDriverWait wait2 = new WebDriverWait(driver, new TimeSpan(0,0,10));
            wait2.Until(ExpectedConditions.ElementIsVisible(By.Id("password")));

Not tried the NuGet package but should work!

IdentityServer4 – AddSigningCredential using certificate stored in Azure Key Vault

This post shows how to amend IdentityServer4 configuration from using AddDeveloperSigningCredential to AddSigningCredential with an X509 certificate.

The certificate will be stored as a secret in an Azure key vault.

In the IdentityServer4 Quick Start tutorials (Quick Starts), developer signing credentials are used, which is fine for development but in production a certificate should be used – this is required if, for example, Service Fabric is used to host an IdentityServer instance.

This is example of using developer signing credentials (in Startup.cs):

public void ConfigureServices(IServiceCollection services)
{
    // configure identity server with in-memory stores, keys, clients and resources
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients());
}

With thanks to the following links:
Use Azure Key Vault from a Web Application
PFX Certificate in Azure Key Vault, by Rahul Nath
Use Azure Key Vault from a Web Application
Get started with Azure Key Vault

Step 1 – Create the certificate

A certificate can be created using OpenSSL (link).

First create the .key and .cer files:

openssl req –newkey rsa:2048 –nodes –keyout XXXXX.key –x509 –days 365 –out XXXXX.cer

Next, from these files create the .pfx:

openssl pkcs12 –export –in XXXX.cer –inkey XXXX.key –out XXXX.pfx

You will be asked to enter a password – note this down for later.

Step 2 – Upload the certificate into an Azure Key Vault as a secret

Open a Windows Powershell prompt as administrator.

Connect to your Azure account:

Connect-AzureRmAccount 

If necessary, change subscription:

Select-AzureRmSubscription -SubscriptionId AAAA-BBBB-CCCC-DDDDD123456 

Enter the following commands to import the certificate as a secret into your Key Vault:

$pfxFilePath = 'C:\XXXXXX.pfx'
$pwd = 'yourpassword'
$flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$collection.Import($pfxFilePath, $pwd, $flag)
$pkcs12ContentType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12
$clearBytes = $collection.Export($pkcs12ContentType)
$fileContentEncoded = [System.Convert]::ToBase64String($clearBytes)
$secret = ConvertTo-SecureString -String $fileContentEncoded -AsPlainText –Force
$secretContentType = 'application/x-pkcs12'
Set-AzureKeyVaultSecret -VaultName 'YourKeyVaultName' -Name 'YourKeyName' -SecretValue $Secret -ContentType $secretContentType

Note that the password is required here to import the pfx into $collection – it is not stored in the Key Vault.

Login in to the Azure portal and navigate to your Key Vault. The secret should show in the “Secrets” tab. Click on your secret and in the properties tab take a copy of the “Secret Identifier” – eg. “https://yourvault.vault.azure.net/secrets/xyz/123”

Step 3 Create App Registration in Azure.

You need to create an “application registration” and assign this to have permissions to access your Azure key vault.

In the Azure portal, click on “All Services” in the left hand menu, then in the filter type “App registrations”.

Create application registration – ensure you choose “Web app/API” as the Application Type. The Application Id here will be the ClientId.

Click on the Application registration, then Settings, then Keys – enter a description for the key and set the expiration then click “Save” – copy the key value and make a note of it (as you will need it later & will not be able to retrieve it). The value you copy here will be the ClientSecret.

Step 4 Set the Access Policy the secret to your App Registration

In the Azure portal, navigate to Key Vault. Click on the Access Polices tab in Settings. Click “Add New”, then select your Application Registration from the “Select Principal” …
Under Key permissions, check the boxes “Decrypt” and “Sign” in the Cryptographic Operations section.
Under Secret Permissions, check the box “Get” in Secret Management Operations section.

Step 5 Identity Server web site.

See this link
To enable the IdentityServer to access the Azure key vault:
First, add the following NuGet packages:

  • Microsoft.IdentityModel.Clients.ActiveDirectory
  • icrosoft.Azure.KeyVault

Now modify the ConfigureServices method of Startup.cs to call AddSigningCredential (where GetIdentityServerCertificate is a new method that returns the certificate from the key vault)

 public void ConfigureServices(IServiceCollection services)
        {
           // (other code excluded) ...

            services.AddMvc();

            services.AddIdentityServer()
                .AddSigningCredential(GetIdentityServerCertificate(services))
                .AddInMemoryPersistedGrants()
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(GetClientsFromConfig(services))
                .AddAspNetIdentity()
                .AddProfileService();
         }

 
Here is the implementation of GetIdentityServerCertificate

private X509Certificate2 GetIdentityServerCertificate(IServiceCollection services)
        {
             var clientId = "TODO-get from config";
            var clientSecret = "TODO-get from config";
            var secretIdentifier = "TODO-get from config";

            var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(async (authority, resource, scope) =>
            {
                var authContext = new AuthenticationContext(authority);
                ClientCredential clientCreds = new ClientCredential(clientId, clientSecret);

                AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCreds);

                if (result == null)
                {
                    throw new InvalidOperationException("Failed to obtain the JWT token");
                }

                return result.AccessToken;
            }));

            var pfxSecret = keyVaultClient.GetSecretAsync(secretIdentifier).Result;
            var pfxBytes = Convert.FromBase64String(pfxSecret.Value);
            var certificate = new X509Certificate2(pfxBytes);
            return certificate;
        }