How to get a TokenCredential from a ServiceClientCredential object? - azure

In my application, we presently are using ServiceClientCredentials from Microsoft.Rest. We are migrating parts of our application over to start using Azure.ResourceManager's ArmClient.
Basically all of our previous application integrations into Azure were using Microsoft.Azure.ResourceManager, which exposed agents like BlobClient or SecretClient, and these all accepted ServiceClientCredentials as a valid token type.
Now, with ArmClient I need to authenticate using DefaultAzureCredential which derives from Azure.Core's TokenCredential.
Surprisingly I haven't been able to find any examples yet of how to create this TokenCredential.
DefaultAzureCredential just works on my local PC since I'm signed into Visual Studio, but not on my build pipeline where I use Certificate based auth exposed as a ServiceClientCredential.

This was easier than I thought. The fix ended up being adding a new ServiceCollection extension method and passing in IWebHostEnvironment.
I use that to determine whether running in local debug, in which case we can use DefaultAzureCredential, or whether running in prod mode, in which case we should use Certificate Based auth.
It looks somewhat like this and works like a charm.
public static IServiceCollection AddDefaultAzureToken (this IServiceCollection services, IWebHostEnvironment environment)
{
if (environment.IsDevelopment())
{
var l = new DefaultAzureCredential();
services.AddSingleton<TokenCredential, l>;
}
else
{
var certCredential= new ClientCertificateCredential(null, null, "Abc");
services.AddSingleton<TokenCredential, certCredential>;
}
return services;
}
This works since DefaultAzureCredential and ClientCertficateCredential all have a common ancestor of TokenCredential, and the L in SOLID, the Liskov Substitution principle tells us that any implementation of a class can be substituted for any other instance of that class without breaking the application.
Note: the above sample was pseudocode and may need slight changing to work in your environment and should be cleaned to match your teams coding standards.

Related

Azure App Configuration to strongly-typed configuration objects

I've been playing around a bit with Azure App Configuration.
Here's an example configruation:
var environmentVariable = Environment.GetEnvironmentVariable("AppConfigurationConnectionString");
var config =
new ConfigurationBuilder()
.AddAzureAppConfiguration(options =>
{
options.Connect(environmentVariable)
.ConfigureKeyVault(kv =>
{
kv.SetCredential(new DefaultAzureCredential());
});
})
.Build();
services.AddSingleton<IConfiguration>(config);
Following this, I can inject an IConfiguration instance into my services and use _config["settingName"] to access config settings. This all works well and is really quite nice.
One thing that I don't know how to do is to map groups of related settings to a strongly typed object (that is, without having to do it all manually, which I could do, but... hoping there's a better way).
In conventional ASP.NET core configuration, I can group related settings settings as follows (i.e. in appsettings.json)
{
"test": {
"key1": "value1",
"key2": "value2"
}
}
using the IOptions pattern as follows:
services.Configure<Test>(config.GetSection("test"));
which allows me to inject a strongly-typed IOptions<Test> instance into my classes. IMO this is a bit nicer than a big flat indexer, where I use _config["key1"] to get config settings.
Is there an approach for Azure App Configruation to allow me to automatically configure strongly-typed config objects that can be injected into my classes?
TIA
.NET Core flattens objects in appsettings.json when it imports them into IConfiguration. For example, your test object becomes the following two keys in IConfiguration
_config["test:key1"]
_config["test:key2"]
This means that you can accomplish exactly what you want with Azure App Configuration by storing the settings in this flattened manner. The Azure App Configuration UI in the Azure portal has an import utility that will allow you to import an appsettings.json file and it does this importing for you.
Here is an example of the import utility in use:
After you have the flattened object in Azure App Configuration the exact code you have will work.

Keyword not supported: 'authentication' error for azure integrated connection

Getting Keyword not supported: 'authentication' error while trying to connect an azure DB through 'Active Directory Integrated' option in .NET core 2.1 project.
Note: I am using EF core to connect the Data source.
TL;DR
As called out by #Aamir Mulla in the comments, this has officially been added since Version 2.0.0
UPDATE - 16/08/2019
Active Directory Password Authentication has now been added for .NET Core in Microsoft.Data.SqlClient 1.0.19221.1-Preview
Unfortunately, the authentication keyword is not yet fully supported in .NET Core. Here is an issue which discusses this.
But .NET Core 2.2 has added some support for this use case as mentioned in this comment. The basic idea is to get the access token by any means (ADAL, REST, etc.) and set SqlConnection.AccessToken to it.
As for using this with EF Core, there's a good discussion about this in this github issue and in particular the comment by mgolois provides a simple implementation to the solution that cbriaball mentions in the thread.
Here is the same for reference
Note that this sample is using the Microsoft.Azure.Services.AppAuthentication library
// DB Context Class
public class SampleDbContext : DbContext
{
public SampleDbContext(DbContextOptions<TeamsDbContext> options) : base(options)
{
var conn = (System.Data.SqlClient.SqlConnection)this.Database.GetDbConnection();
conn.AccessToken = (new AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
}
}
// Startup.cs
services.AddDbContext<SampleDbContext>(options =>
{
options.UseSqlServer(<Connection String>);
});
The connection string would be something like this
Server=tcp:<server_name>.database.windows.net,1433;Database=<db_name>;
If you're still having the issue, make sure you have
Microsoft.Data.SqlClient package installed, not System.Data.SqlClient. They both contain SqlConnection class, switching the package for the first one fixed the issue for me.
As of today 7/18/2022 , I am still getting the issue from Azure when trying to use it through ManagedIdentity.
The microsoft doc at https://learn.microsoft.com/en-us/azure/app-service/tutorial-connect-msi-sql-database?tabs=windowsclient%2Cefcore%2Cdotnetcore
to use managed identity we need to use the connection string in this format!
"Server=tcp:.database.windows.net;Authentication=Active Directory Default; Database=;"
But looks like Azure is not liking it!
However, adding the access token helped!
var connectionString =
"Server=tcp:yourazuresqlservername.database.windows.net; Database=yourazuresqldbname;";
var con = new SqlConnection(connectionString);
//And then
con.AccessToken = (new AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
con.Open();
//Do sql tasks
con.Close();

Google Calendar API and shared hosting issue

I'm trying to use a public Google calendar in a webpage that will need editing functionalities.
To that effect, I created the calendar and made it public. I then created a Google service account and the related client id.
I also enabled the Calendar API and added the v3 dlls to the project.
I downloaded the p12 certificate and that's when the problems start.
The call to Google goes with a X509 cert but the way the .NET framework is built is that it uses a user temp folder.
Since it's a shared host for the web server (GoDaddy), I cannot have the app pool identity modified.
As a result, I'm getting this error:
System.Security.Cryptography.CryptographicException: The system cannot
find the file specified.
when calling:
X509Certificate2 certificate = new X509Certificate2(GoogleOAuth2CertificatePath,
"notasecret", X509KeyStorageFlags.Exportable);
that cerificate var is then to be used in the google call:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(GoogleOAuth2EmailAddress)
{
User = GoogleAccount,
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
... but I never get that far.
Question: is there a way to make the call differently, i.e. not to use a X509 certificate but JSON instead?
Or can I get the x509 function to use a general temp location rather than a user location to which I have no access to since I can't change the identity in the app pool?
Since I'm completely stuck, any help would be appreciated.
One simple option which avoids needing to worry about file locations is to embed the certificate within your assembly. In Visual Studio, right-click on the file and show its properties. Under Build Action, pick "Embedded resource".
You should then be able to load the data with something like this:
// In a helper class somewhere...
private static byte[] LoadResourceContent(Type type, string resourceName)
{
string fullName = type.Namespace + "." + resourceName;
using (var stream = type.Assembly.GetManifestResourceStream(fullName)
{
var output = new MemoryStream();
stream.CopyTo(output);
return output.ToArray();
}
}
Then:
byte[] data = ResourceHelper.LoadResourceContent(typeof(MyType), "Certificate.p12");
var certificate = new X509Certificate2(data, "notasecret", X509KeyStorageFlags.Exportable);
Here MyType is some type which is in the same folder as your resource.
Note that there are lots of different "web" project types in .NET... depending on the exact project type you're using, you may need to tweak this.

MVC5, OWIN, and Ninject - GetOwinContext Issues

I have an MVC5 project that is doing OwinStartup, and I'm using Ninject.MVC5, Ninject.Web.Common.OwinHost, etc.
I have NinjectWebCommon bootstrapping DI and things were working just fine. Until I started playing with the identity code.
I need to issue a password reset token, which requires a DataProtectionProvider. No problem, the ApplicationUserManager wires that up in the Create method, which is bound to the OwinContext during startup in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
...
}
In my NinjectWebCommon I have the following registrations:
kernel.Bind<IDataContext>()
.ToMethod(ctx => HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>())
.InRequestScope();
kernel.Bind<IApplicationUserManager>()
.ToMethod(ctx => HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>())
.InRequestScope();
The problem I'm having is that the token isn't being issued. Why isn't that what my question is about? Well, if I get an instance of the ApplicationUserManager using the Create myself and use that, things work flawlessly.
Next on my plate a reported user creation bug. Hyphens aren't working in usernames. In that same ApplicationUserManager.Create method, is the UserValidator code that's overriding the default AllowOnlyAlphanumericUserNames value.
Again, if I use a self created dependency, it works as expected. This seems to indicate Ninject's injected dependency isn't using the ApplicationUserManager.Create'd version.
I'm guessing this has to do with the call to: HttpContext.Current.GetOwinContext??
Is there something I need to do in order to inject something that relies on the owin context or something else I need to do while registering my resolver?
I've seen other questions here showing UseNinjectMiddleware and UseNinjectWebApi. I tried that approach, but didn't have any luck, nothing was being injected in...
Any assistance is greatly appreciated!

Can ACS Service Namespace creation be automated?

First, let me state my real problem: I've got code that makes calls to the ACS Management service, and I'd like my integration tests to be able to be run concurrently without each test run clobbering the others. That is, since multiple people / build servers might end up running these tests concurrently, if they're all using the same ACS service namespace, concurrency issues arise.
My thinking is the simplest means of achieving this would be to generate new, unique ACS service namespaces for each test runner -- but as far as I can tell, there's no automated way of creating new service namespaces (or management client keys). Am I wrong? Is there another way of going about this?
An automated method of creating new service namespaces would be extraordinarily helpful.
You are correct. That's not possible today. Maybe you can describe your scenario in more detail and there might be some alternative solutions to avoid having to recreate the namespace?
Technically it should be possible, since the Management Portal is a Silverlight application accessing a WCF RIA Service.
If you dig deep enough you'll find some useful information:
This is the Silverlight XAP for the management of Windows Azure AppFabric: https://appfabricportal.windows.azure.com/ClientBin/Microsoft.AppFabric.WebConsole.4.1.3.xap
This is the service being used when listing/creating/... namespaces etc..: https://appfabricportal.windows.azure.com/Services/Microsoft-AppFabric-Web-Services-AppFabricDomainService.svc?wsdl
And this is a piece of the DomainContext:
public sealed class AppFabricDomainContext : DomainContext
{
public AppFabricDomainContext(Uri serviceUri)
: this((DomainClient) new WebDomainClient<AppFabricDomainContext.IAppFabricDomainServiceContract>(serviceUri, true))
{
}
...
public InvokeOperation CreateServiceNamespace(IEnumerable<string> serviceNames, string parentProjectKey, string serviceNamespace, IEnumerable<string> packageKeys, string regionKey, Action<InvokeOperation> callback, object userState)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
dictionary.Add("serviceNames", (object) serviceNames);
dictionary.Add("parentProjectKey", (object) parentProjectKey);
dictionary.Add("serviceNamespace", (object) serviceNamespace);
dictionary.Add("packageKeys", (object) packageKeys);
dictionary.Add("regionKey", (object) regionKey);
this.ValidateMethod("CreateServiceNamespace", (IDictionary<string, object>) dictionary);
return this.InvokeOperation("CreateServiceNamespace", typeof (void), (IDictionary<string, object>) dictionary, true, callback, userState);
}
}
Finding this info was the easy part, getting it to work... that's something else. Take the authentication part for example, you'll need to authenticate with Windows Live and use those credentials when calling the WCF RIA Service.
Good luck!

Resources