Passwordless authentication for Azure Databases with .Net

Felip Miguel Puig
4 min readJul 19, 2024

Most of Azure Services support Azure AD authentication, including Azure Database for MySQL and Postgres Flexible Server. This is a great feature that allow to use those services without the inconvinients of managing credentials in our applications. However. it is not always clear how to take advantage of that capability from our applications and the documentation sometimes can be confusing.

Photo by Stefan Steinbauer on Unsplash

According to Azure docs, it is possible to use an Azure AD token as password for MySQL and Postgres Flexible server. Something like this:

// Azure AD resource ID for Azure Database for PostgreSQL Flexible Server is https://ossrdbms-aad.database.windows.net/
string accessToken = null;

try
{
// Call managed identities for Azure resources endpoint.
var sqlServerTokenProvider = new DefaultAzureCredential();
accessToken = (await sqlServerTokenProvider.GetTokenAsync(
new Azure.Core.TokenRequestContext(scopes: new string[] { "https://ossrdbms-aad.database.windows.net/.default" }) { })).Token;
}
catch (Exception e)
{
Console.Out.WriteLine("{0} \n\n{1}", e.Message, e.InnerException != null ? e.InnerException.Message : "Acquire token failed");
System.Environment.Exit(1);
}
// Open a connection to the PostgreSQL server using the access token.
string connString = String.Format(
"Server={0}; User Id={1}; Database={2}; Port={3}; Password={4}; SSLMode=Prefer",
Host,
User,
Database,
5432,
accessToken);

using (var conn = new NpgsqlConnection(connString))
{
Console.Out.WriteLine("Opening connection using access token...");
conn.Open();
...
// do whatever you want to do with your database
}

This sample will work, but… It has important drawbacks:

It is not connection pool-friendly. .Net manages database connection pools automatically; database connections with identical connection strings will share and reuse connections from a pool instead of establishing new connections each time a call to the database occurs. This results in a significant performance improvement. In this case, the connection string contains the access token, a time-limited credential. So, the connection pool will be invalidated every time the access token expires. This is not a problem if the application is short-lived, but it can be a problem if the application is long-lived. In addition, if another part of the application retrieves a connection in the same way, the connection pool will be invalidated again.

It’s not Asp.Net/Entity Framework friendly. Usually, Entity Framework connection is configured during application startup. The connection string is not configured later; hence, the access token cannot be refreshed. The problem is that connection pools, by default, are dynamic. It means that few connections are created at startup, and more connections are created as needed. So, if the application is long-lived, and a new connection needs to be created after the access token has expired, it will fail.

To avoid these issues, I created some Nuget packages that make using passwordless connections in your application easier. This is how it looks for Azure Database for PostgreSQL:

NpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder("Server=superserver.postgres.database.azure.com;Database=sampledb;Port=5432;User Id=myuser@mydomain.onmicrosoft.com;Ssl Mode=Require;");
NpgsqlDataSource dataSource = dataSourceBuilder
.UseAzureADAuthentication(new DefaultAzureCredential())
.Build();
using NpgsqlConnection connection = await dataSource.OpenConnectionAsync();

Note that the connection string doesn’t contain a password. As the connection string doesn’t change, it is properly pooled. Npgsql driver provides a method to retrieve the password periodically. Hence, it will work well with aspnet and Entity Framework.

Here is an example of usage with Npgsql EntityFrameworkCore:

// configuring services
var services = new ServiceCollection();
services.AddDbContextFactory<SampleContext>(options =>
{
options.UseNpgsql("POSTGRESQL CONNECTION STRING",
npgsqlOptions => npgsqlOptions.UseAzureADAuthentication(new DefaultAzureCredential())); // Usage of this library
});

There are also equivalent implementations for the MySqlConnector driver and Pomelo EntityFrameworkCore.

MySqlConnector example:

TokenCredentialMysqlPasswordProvider passwordProvider = new TokenCredentialMysqlPasswordProvider(new DefaultAzureCredential());
using MySqlConnection connection = new MySqlConnection(configuration.GetConnectionString());
connection.ProvidePasswordCallback = passwordProvider.ProvidePassword;
await connection.OpenAsync();

And Pomelo EntityFrameworkCore:

// Pomelo requires to specify the server version
ServerVersion serverVersion = ServerVersion.Parse("5.7", Pomelo.EntityFrameworkCore.MySql.Infrastructure.ServerType.MySql);
// configuring services
var services = new ServiceCollection();
services.AddDbContextFactory<SampleContext>(options =>
{
options
.UseMySql("MYSQL CONNECTION STRING", serverVersion)
.UseAzureADAuthentication(new DefaultAzureCredential());
});

As you probably realized, I used DefaultAzureCredential in all samples. Its main advantage is that it can be used with an end user, for instance, by you in your local development environment and other service principals, such as Managed Identities in production.

You can find the implementation of these libraries in Github, or just import in your projects the following nuget packages:

You can find a reference sample here. It is a customization of a Todo sample generated with azd. There is a Terraform script to generate the infrastructure:

  • An Azure Database for MySQL flexible server configured for Azure AD authentication.
  • An Azure Database for Postgres flexible server configured for Azure AD authentication.
  • A User-Assigned Managed Identity.
  • An Azure Container App connecting to MySQL flexible server.
  • An Azure Container App connecting to Postgres flexible server.

The sample also contains the scripts to create the database users linked to the managed identities and grant the appropriate permissions.

You can find more details about the sample in my following article.

--

--

Felip Miguel Puig

More than 20 years of experience on technology in my back. I started my career in dotnet. Also working with Java since 2018.