There seem to be three main ways of connecting to the database with EF Core and Azure AD integrated authentication.
Method 1: Classic approach
In the DbContext, put this:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var conn = new SqlConnection(ConnString);
conn.AccessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").Result;
optionsBuilder.UseSqlServer(conn);
}
This works both locally on the developer's machine and also in Azure, but has this dodgy async-over-sync use of .Result
as there is no async version of OnConfiguring. Using .Result
to call async-over-sync is not recommended.
Method 2: Use a connection interceptor
public class AadAuthenticationDbConnectionInterceptor : DbConnectionInterceptor
{
public override async Task<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken)
{
var sqlConnection = (SqlConnection)connection;
sqlConnection.AccessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
}
public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
{
var sqlConnection = (SqlConnection)connection;
sqlConnection.AccessToken = new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/").GetAwaiter().GetResult();
return base.ConnectionOpening(connection, eventData, result);
}
}
This also works both locally and in Azure, but there is still the dodgy async-over-sync use of .Result
as EF calls the non-async ConnectionOpening method if you perform any non-async EF operations.
Method 3: Specify Authentication type in connection string
If you are using Microsoft.Data.SqlClient v2.1.0 or newer, you can use two new authentication types: Active Directory Managed Identity
and Active Directory Interactive
.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString = "Server=tcp:xxxxxx.database.windows.net,1433;Database=yyyyy;Authentication=Active Directory Managed Identity";
optionsBuilder.UseSqlServer(conn);
}
This takes care of everything for you and doesn't have the async-sync issues. However, it requires you to know whether the code is running in Azure or not to select the authentication type; and for local development the Active Directory Interactive
mode is very annoying as it pops up a login prompt every time you start the application.
And the question is...
Is there a way to get this working that doesn't have the problem of sync-async and also doesn't have the problem of a login popup every time you run the app?