EF Core: Best practice way to connect to Azure SQL with AD Authentication

Viewed 979

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?

1 Answers

@ErikEJ answered this in the comments.

The solution is to wait for v3.0 of the Microsoft.Data.SqlClient which introduces Active Directory Default authentication type. With that, we'll be able to do:

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 {
     var connString = "Server=tcp:xxxxxx.database.windows.net,1433;Database=yyyyy;Authentication=Active Directory Default";
     optionsBuilder.UseSqlServer(conn);
 }

and it sounds like that will provide a neat solution to this.

Related