{"id":21598541,"url":"https://github.com/felipmiguel/azuredb.passwordless","last_synced_at":"2025-04-11T01:04:01.777Z","repository":{"id":117071067,"uuid":"544778123","full_name":"felipmiguel/AzureDb.Passwordless","owner":"felipmiguel","description":".net library with extensions for passwordless authentication to Azure Database for MySql and Azure Database for PostgreSQL","archived":false,"fork":false,"pushed_at":"2024-07-19T22:07:07.000Z","size":5108,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-11T01:03:52.310Z","etag":null,"topics":["azure","azure-entra","azure-entra-id","azuread","csharp","dotnet","entra-id","managed-identity","mysql","passwordless","passwordless-authentication","postgresql","workload-identity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/felipmiguel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-03T07:55:40.000Z","updated_at":"2024-07-24T16:24:34.000Z","dependencies_parsed_at":"2024-07-20T01:00:30.674Z","dependency_job_id":null,"html_url":"https://github.com/felipmiguel/AzureDb.Passwordless","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipmiguel%2FAzureDb.Passwordless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipmiguel%2FAzureDb.Passwordless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipmiguel%2FAzureDb.Passwordless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipmiguel%2FAzureDb.Passwordless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/felipmiguel","download_url":"https://codeload.github.com/felipmiguel/AzureDb.Passwordless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248322597,"owners_count":21084336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","azure-entra","azure-entra-id","azuread","csharp","dotnet","entra-id","managed-identity","mysql","passwordless","passwordless-authentication","postgresql","workload-identity"],"created_at":"2024-11-24T18:12:29.629Z","updated_at":"2025-04-11T01:04:01.752Z","avatar_url":"https://github.com/felipmiguel.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Batec.Azure.Data.Extensions\n\nThis repository contains helper libraries that can connect to Azure Database for PostgreSQL and Azure Database for MySQL using Azure AD authentication. Many Azure services support Azure AD authentication. They require an Azure AD access token with specific scopes. Azure Database for Postgres and Azure Database for MySql expect an Azure AD access token with `https://ossrdbms-aad.database.windows.net` audience. It is possible to get an access token of this kind using Azure.Identity library or even using Azure CLI.\n\nThis repository provides a core library that obtains access tokens for Azure Database for Postgresql and MySql and caches them until they expire.\n\n## Actual challenges\n\nAzure Database for MySQL and Postgresql support using Azure AD authentication. The most direct way to authenticate using Azure AD is by retrieving an access token and using it as a password to connect to the database. Something similar to this:\n\n```csharp\nusing System;\nusing System.Net;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing Npgsql;\nusing Azure.Identity;\n\nnamespace Driver\n{\n    class Script\n    {\n        // Obtain connection string information from the portal for use in the following variables\n        private static string Host = \"HOST\";\n        private static string User = \"USER\";\n        private static string Database = \"DATABASE\";\n\n        static async Task Main(string[] args)\n        {\n            //\n            // Get an access token for PostgreSQL.\n            //\n            Console.Out.WriteLine(\"Getting access token from Azure AD...\");\n\n            // Azure AD resource ID for Azure Database for PostgreSQL Flexible Server is https://ossrdbms-aad.database.windows.net/\n            string accessToken = null;\n\n            try\n            {\n                // Call managed identities for Azure resources endpoint.\n                var sqlServerTokenProvider = new DefaultAzureCredential();\n                accessToken = (await sqlServerTokenProvider.GetTokenAsync(\n                    new Azure.Core.TokenRequestContext(scopes: new string[] { \"https://ossrdbms-aad.database.windows.net/.default\" }) { })).Token;\n\n            }\n            catch (Exception e)\n            {\n                Console.Out.WriteLine(\"{0} \\n\\n{1}\", e.Message, e.InnerException != null ? e.InnerException.Message : \"Acquire token failed\");\n                System.Environment.Exit(1);\n            }\n\n            //\n            // Open a connection to the PostgreSQL server using the access token.\n            //\n            string connString =\n                String.Format(\n                    \"Server={0}; User Id={1}; Database={2}; Port={3}; Password={4}; SSLMode=Prefer\",\n                    Host,\n                    User,\n                    Database,\n                    5432,\n                    accessToken);\n\n            using (var conn = new NpgsqlConnection(connString))\n            {\n                Console.Out.WriteLine(\"Opening connection using access token...\");\n                conn.Open();\n\n                using (var command = new NpgsqlCommand(\"SELECT version()\", conn))\n                {\n\n                    var reader = command.ExecuteReader();\n                    while (reader.Read())\n                    {\n                        Console.WriteLine(\"\\nConnected!\\n\\nPostgres version: {0}\", reader.GetString(0));\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n\u003e [!NOTE]\n\u003e Code snippet extracted from https://learn.microsoft.com/azure/postgresql/flexible-server/how-to-connect-with-managed-identity#connect-using-managed-identity-in-c\n\n\u003e [!WARNING]\n\u003e As explained later in this page, this is not the recommended way to connect to an Azure Database using Azure AD authentication.\n\nThis approach has some drawbacks:\n\n**It's not connection pool-friendly.** .Net manages connection pools automatically. It considers that a connection is the same if it has the same connection string. 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 it is long-lived. In addition, if another part of the application retrieves an access token again, the connection pool will be invalidated, even if the previous access token has not yet expired.\n\n**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.\n\n## Proposed solution\n\nThe proposed solution, which utilizes the Azure.Identity library, provides a flexible approach to retrieve an access token with the correct audience and scope. This process is designed to be transparent for the user, allowing developers to focus on their specific application scenarios. Whether it's ASP.Net Core, Entity Framework, or other scenarios, the proposed solution is adaptable and can be used effectively.\n\nThe application should retrieve an access token before creating a connection, cache it, and retrieve a new one only if it expires. Each database driver provides a different way to connect using an access token:\n\n* PostgreSQL: The Npgsql library provides a mechanism to obtain a password periodically. That is using NpgsqlDataSourceBuilder and [_UsePeriodicPasswordProvider_ method](https://www.npgsql.org/doc/api/Npgsql.NpgsqlDataSourceBuilder.html#Npgsql_NpgsqlDataSourceBuilder_UsePeriodicPasswordProvider_System_Nullable_Func_Npgsql_NpgsqlConnectionStringBuilder_CancellationToken_ValueTask_System_String____TimeSpan_TimeSpan_). The library [Batec.Azure.Data.Extensions.Npgsql] contains a class that provides a callback that obtains the access token from Azure to be used as a password.\n* MySQL:\n  * MySql.Data library provides a mechanism to configure an authentication plugin. The library Batec.Azure.Data.Extensions.MySql provides a class that implements the authentication plugin and obtains the access token from Azure AD to be used as a password. The problem with this library is that the configuration uses the old System.Configuration library, which is not supported in .Net Core. So, the library is not usable in .Net Core applications. This library was tested with Azure Database for MySQL Flexible Server and Azure Database for MySQL Single Server, but **token as password only works in Azure Database for MySQL Single Server**.\n  * [MySqlConnector](https://mysqlconnector.net/) is an open-source driver for dotnet. It provides a similar mechanism to PostgreSQL for this scenario. It provides a [_ProvidePasswordCallback_ delegate](https://mysqlconnector.net/api/mysqlconnector/mysqlconnection/providepasswordcallback/). The library Batec.Azure.Data.Extensions.MySqlConnector provides a class that implements the delegate signature and obtains the password from Azure AD. This driver works with Azure Database for MySQL Flexible Server and Azure Database for MySQL Single Server. Although it does not have an Entity Framework implementation, it is possible to use [Pomelo](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql) with it.\n\n\u003e [!NOTE] This [fork of MySql.Data](https://github.com/mysql/mysql-connector-net/compare/8.0...felipmiguel:mysql-connector-net:8.0.tunned) library implements a couple of experimental workarounds to make it work in .Net Core applications.\n\nThe solution relies on Azure.Identity library. This library supports different authentication mechanisms, such as Managed Identities, or IDE identities (Visual Studio, Visual Studio Code, IntelliJ).\n\n### Connection pooling\n\nTo make this library connection pool-friendly, it should use a valid token, not an expired one, and the connection string should be the same for all connections to avoid pool fragmentation.\n\nHere is an example for PostgreSQL:\n\n```csharp\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(GetConnectionString());\nNpgsqlDataSource dataSource = dataSourceBuilder\n                 .UsePeriodicPasswordProvider(async (settings, cancellationToken) =\u003e\n                 {\n                     var azureCredential = new DefaultAzureCredential();\n                     AccessToken token = await azureCredential.GetTokenAsync(new TokenRequestContext(new string[] { \"https://ossrdbms-aad.database.windows.net/.default\" }));\n                     return token.Token;\n                 }, TimeSpan.FromMinutes(55), TimeSpan.FromMilliseconds(100))\n                 .Build();\nusing NpgsqlConnection connection = await dataSource.OpenConnectionAsync();\n```\n\nThe class _TokenCredentialMysqlPasswordProvider_ facilitates the above implementation. It provides an access token and also provides a caching mechanism to avoid retrieving an access token that has not yet expired.\n\n```csharp\nTokenCredentialNpgsqlPasswordProvider passwordProvider = new TokenCredentialNpgsqlPasswordProvider();\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(GetConnectionString());\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UsePeriodicPasswordProvider(passwordProvider.PasswordProvider, TimeSpan.FromMinutes(2), TimeSpan.FromMilliseconds(100))\n                .Build();\nusing NpgsqlConnection connection = await dataSource.OpenConnectionAsync();\n```\n\nAnd to make it simpler there is an extension method for NpgsqlDataSourceBuilder named `UseAzureADAuthentication`. This is the recommended option:\n\n```csharp\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.GetConnectionString());\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UseAzureADAuthentication(new DefaultAzureCredential())\n                .Build();\nusing NpgsqlConnection connection = await dataSource.OpenConnectionAsync();\n```\n\n\u003e [!NOTE] According to Npgsql driver documentation, the password callback is called by a timer, not when it will be used. Therefore, in the first code sample, the timer is set to a value lower than the default AAD access token expiration time - 1 hour. The second example uses TokenCredentialMysqlPasswordProvider, which reuses the token if it is not expired; for that reason, it can be checked more frequently, and it won't perform an access token retrieval unless it expires. \n\n### Entity Framework Core\n\nTo facilitate the use of this library with Entity Framework Core, an extension with the following signature is provided for each driver.\n\n```csharp\nnamespace Microsoft.EntityFrameworkCore;\n\npublic static class DbContextOptionsBuilderExtension\n{\n    public static DbContextOptionsBuilder UseAzureADAuthentication(this DbContextOptionsBuilder optionsBuilder, TokenCredential credential)\n    {\n        // Specific driver implementation\n    }\n}\n```\n\nThe `TokenCredential` will be used to retrieve the access token.\n\n### Developer friendly\n\nDevelopers need to test their applications from their development machines. This means the solution should be tested from their machines using credentials different from Managed Identities, such as their IDE Azure credentials (Visual Studio, Visual Studio Code, IntelliJ) or their Azure cli credentials. As the implementation is based on `Azure.Identity.DefaultAzureCredential`, it allows users to use all mentioned mechanisms.\n\nThe library Batec.Azure.Data.Extensions.Core implements a caching mechanism, so the access token is retrieved only once per application execution and refreshed if it expires. Even if some of the DefaultAzureCredential mechanisms already implement caching, it causes us to try all fallback mechanisms, which is unnecessary.\n\n## PostgreSQL\n\n[Npgsql](https://www.npgsql.org/doc/security.html?tabs=tabid-1) library offers a mechanism that periodically retrieves a password that can be used to create a physical connection to the database. That is the `UsePeriodicPasswordProvider` method. The library Batec.Azure.Data.Extensions.Npgsql provides a class that implements a property that can be used to retrieve the password from Azure AD.\n\nThe library Batec.Azure.Data.Extensions.Npgsql provides an extension method for NpgsqlDataSource named `UseAzureADAuthentication` that simplifies the code and is the recommended option:\n\n```csharp\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(configuration.GetConnectionString());\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UseAzureADAuthentication(new DefaultAzureCredential())\n                .Build();\nusing NpgsqlConnection connection = await dataSource.OpenConnectionAsync();\n```\n\nThat is equivalent to this:\n\n```csharp\nTokenCredentialNpgsqlPasswordProvider passwordProvider = new TokenCredentialNpgsqlPasswordProvider(new DefaultAzureCredential());\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(GetConnectionString());\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UsePeriodicPasswordProvider(passwordProvider.PasswordProvider, TimeSpan.FromMinutes(2), TimeSpan.FromMilliseconds(100))\n                .Build();\nusing NpgsqlConnection connection = await dataSource.OpenConnectionAsync();\n```\n\n### Entity Framework Core\n\nNpgsql Entity Framework Core provider provides two mechanisms:\n\n* Using a NpgsqlDataSource\n* Using a DbContextOptionsBuilderOptions.ProvidePasswordCallback. This delegate provides a mechanism to retrieve a the password before the connection is created. \n\n#### NpgsqlDataSource\n\nThis is the recommended option.\n\nDbContext factories:\n\n```csharp\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(\"PSQL CONNECTION STRING\");\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UseAzureADAuthentication(new DefaultAzureCredential())\n                .Build();\nServiceCollection services = new ServiceCollection();\nservices.AddDbContextFactory\u003cChecklistContext\u003e(options =\u003e\n{\n    options.UseNpgsql(dataSource);\n});\n```\n\nDbContext:\n\n```csharp\nNpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(\"PSQL CONNECTION STRING\");\nNpgsqlDataSource dataSource = dataSourceBuilder\n                .UseAzureADAuthentication(new DefaultAzureCredential())\n                .Build();\nServiceCollection services = new ServiceCollection();\nservices.AddDbContext\u003cChecklistContext\u003e(options =\u003e\n{\n    options.UseNpgsql(dataSource);\n});\n```\n\n#### DbContextOptionsBuilderOptions.ProvidePasswordCallback\n\nDbContext factories:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddDbContextFactory\u003cMyContext\u003e(options =\u003e\n    {        \n        TokenCredentialNpgsqlPasswordProvider passwordProvider = new TokenCredentialNpgsqlPasswordProvider(new DefaultAzureCredential());\n        string connectionString = \"PSQL CONNECTION STRING\";\n        options.UseNpgsql(connectionString, npgopts =\u003e\n        {\n            npgopts.ProvidePasswordCallback(passwordProvider.ProvidePasswordCallback);\n        });\n    });\n}\n```\n\nDbContext:\n\n```csharp\nbuilder.Services.AddDbContext\u003cMyContext\u003e(options =\u003e\n{\n    TokenCredentialMysqlPasswordProvider passwordProvider = new TokenCredentialMysqlPasswordProvider();\n    string connectionString = \"PSQL CONNECTION STRING\";\n    options.UseNpgsql(connectionString, npgopts =\u003e\n    {\n        npgopts.ProvidePasswordCallback(passwordProvider.ProvidePasswordCallback);\n    });\n});\n```\n\nThere are more usage examples in project [Batec.Azure.Data.Extensions.NpgsqlTests](./Batec.Azure.Data.Extensions.NpgsqlTests/)\n\n## MySql\n\n### MySql.Data\n\nMySql allows to configure a custom [authentication plugin](https://dev.mysql.com/doc/connector-net/en/connector-net-programming-authentication-user-plugin.html). It should derive from `MySql.Data.MySqlClient.Authentication.MySqlAuthenticationPlugin`. Active Directory authentication is very similar to Clear Text authentication. For that reason, the [AzureIdentityMysqlAuthenticationPlugin](Batec.Azure.Data.Extensions.MySql/AzureIdentityMysqlAuthenticationPlugin.cs) implementation in this repository is based on the [ClearPasswordPlugin](https://github.com/mysql/mysql-connector-net/blob/8.0/MySQL.Data/src/Authentication/ClearPasswordPlugin.cs) from MySql.Data library.\n\nTo override the default client/server negotiation of the plugin to use, it is necessary to specify _DefaultAuthenticationPlugin_ in the connection string.\n\n```bash\nserver=myserver.mysql.database.azure.com;user id=myuser@myserver;database=mydb;sslmode=Required;defaultauthenticationplugin=mysql_clear_password;\n```\n\nTo configure the authentication plugin it is necessary to enable the plugin in the configuration file:\n\n```xml\n\u003c?xml version=\"1.0\"?\u003e\n\u003cconfiguration\u003e\n  \u003cconfigSections\u003e\n    \u003csection name=\"MySQL\" type=\"MySql.Data.MySqlClient.MySqlConfiguration,\nMySql.Data\"/\u003e\n  \u003c/configSections\u003e\n  \u003cMySQL\u003e\n    \u003cAuthenticationPlugins\u003e\n      \u003cadd name=\"mysql_clear_password\"\ntype=\"Batec.Azure.Data.Extensions.MySql.AzureIdentityMysqlAuthenticationPlugin, Batec.Azure.Data.Extensions.MySql\"\u003e\u003c/add\u003e\n    \u003c/AuthenticationPlugins\u003e  \n  \u003c/MySQL\u003e\n...\n\u003c/configuration\u003e\n```\n\n\u003e [!CAUTION] This configuration implementation has a major issue. It is based on the legacy `System.Configuration` implementation, which most .NET Core applications do not use. The configuration file is not read by default, which can mess up the application's implementation.\n\n#### Experimental plugin implementation\n\nBatec.Azure.Data.Extensions.MySql.AzureIdentityMysqlAuthenticationPlugin implements a workaround for the configuration issue described above. To set the configuration it uses reflection to update the internal configuration of the client. This is not a recommended approach, but it is the only way to configure the plugin without using the configuration file.\n\nThe purpose is configuring the private field [_Plugins_](https://github.com/mysql/mysql-connector-net/blob/a53e0c62cf416116ae650aa84065e45385019612/MySQL.Data/src/Authentication/AuthenticationManager.cs#L37), setting the plugin implemented in this library.\n\nThe following code snippet shows how to configure the plugin using reflection:\n\n```csharp\npublic static void RegisterAuthenticationPlugin()\n{\n    Type authenticationPluginManagerType = Type.GetType(\"MySql.Data.MySqlClient.Authentication.AuthenticationPluginManager, MySql.Data\");\n    FieldInfo pluginsField = authenticationPluginManagerType.GetField(\"Plugins\", BindingFlags.Static | BindingFlags.NonPublic);\n    IDictionary plugins = pluginsField.GetValue(null) as IDictionary;\n    object clearTextPasswordPlugin = plugins[\"mysql_clear_password\"];\n    clearTextPasswordPlugin.GetType().GetField(\"Type\").SetValue(clearTextPasswordPlugin, typeof(AzureIdentityMysqlAuthenticationPlugin).AssemblyQualifiedName);\n    plugins[\"mysql_clear_password\"] = clearTextPasswordPlugin;\n}\n```\n\n\u003e[!CAUTION] This is an experimental implementation and a completely discouraged approach. It can be broken in future versions of the driver.\n\n#### Experimental driver implementation\n\nI created an [experimental implementation](https://github.com/felipmiguel/mysql-connector-net) to configure the authentication plugin using the connection string. In this case, the connection string should contain the _AuthenticationPlugins_ parameter, with a list of the plugins to use. Then the connection string looks like this:\n\n```bash\nserver=myserver.mysql.database.azure.com;user id=myuser@myserver;database=mydb;sslmode=Required;defaultauthenticationplugin=mysql_clear_password;authenticationplugins=mysql_clear_password:Batec.Azure.Data.Extensions.MySql.AzureIdentityMysqlAuthenticationPlugin# Batec.Azure.Data.Extensions.MySql# Version=1.0.0.0# Culture=neutral# PublicKeyToken=null;\n```\n\n\u003e [!NOTE] the plugin list replaced character , with character #. This is because the attribute is an array it can be confused with item separator.\n\nIf you want to use this connector, you can add the [following package(s)](https://github.com/felipmiguel?tab=packages\u0026repo_name=mysql-connector-net) to your project:\n\nFor MySql connections:\n\n```dotnetcli\ndotnet add \u003cPROJECT\u003e package MySql.Data --version 8.0.30.1\n```\n\nAnd for MySql Entity Framework Core provider:\n\n```dotnetcli\ndotnet add \u003cPROJECT\u003e package MySql.EntityFrameworkCore --version 6.0.4\n```\n\nThere are more usage examples in project [Batec.Azure.Data.Extensions.MySqlTests](./Batec.Azure.Data.Extensions.MySqlTests/)\n\n### MySqlConnector and Pomelo.EntityFrameworkCore.MySql\n\n[MySqlConnector](https://mysqlconnector.net/) is a popular and alternative driver for MySql and .Net. It is the driver used by [Pomelo](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql), a popular implementation of Entity Framework Core provider for MySql.\n\nMySqlConnector provides the same mechanism as the PostgreSQL driver to delegate the password acquisition. You should provide a delegate for [_ProvidePasswordCallback_](https://mysqlconnector.net/api/mysqlconnector/mysqlconnection/providepasswordcallback/).\n\nThere is an implementation of this delegate in [Batec.Azure.Data.Extensions.MySqlConnector.TokenCredentialMysqlPasswordProvider](./Batec.Azure.Data.Extensions.MySqlConnector/TokenCredentialMysqlPasswordProvider.cs) class.\n\nHere an example of usage:\n\n```csharp\nTokenCredentialMysqlPasswordProvider passwordProvider = new TokenCredentialMysqlPasswordProvider(new DefaultAzureCredential());\nusing MySqlConnection connection = new MySqlConnection(GetConnectionString());\nconnection.ProvidePasswordCallback = passwordProvider.ProvidePassword;\nawait connection.OpenAsync();\nMySqlCommand cmd = new MySqlCommand(\"SELECT now()\", connection);\nDateTime? serverDate = (DateTime?) await cmd.ExecuteScalarAsync();\n```\n\nTo facilitate the usage in Entity Framework Core, there is an extension method in [Batec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore](Batec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore/DbContextOptionsBuilderExtension.cs) class. It has the same signature as the extension method provided for PostgreSQL driver.\n\n```csharp\nusingBatec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore;\n\nnamespace Microsoft.EntityFrameworkCore;\n\npublic static class DbContextOptionsBuilderExtension\n{\n    public static DbContextOptionsBuilder UseAzureADAuthentication(this DbContextOptionsBuilder optionsBuilder, TokenCredential credential)\n    {\n        // see: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1643\n        return optionsBuilder.AddInterceptors(new TokenCredentialMysqlPasswordProviderInterceptor(credential));\n    }\n}\n```\n\nIn this case, the implementation is a bit more complex, as Pomelo does not provide a way to configure the password provider. To do it, an interceptor is registered in the context. The interceptor is responsible for setting the password provider before opening the connector.\n\n```csharp\nusing Batec.Azure.Data.Extensions.MySqlConnector;\nusing Microsoft.EntityFrameworkCore.Diagnostics;\nusing MySqlConnector;\nusing System.Data.Common;\n\nnamespaceBatec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore\n{\n    internal class TokenCredentialMysqlPasswordProviderInterceptor : DbConnectionInterceptor\n    {\n        private readonly TokenCredentialMysqlPasswordProvider _passwordProvider;\n\n        public TokenCredentialMysqlPasswordProviderInterceptor(TokenCredential credential)\n        {\n            _passwordProvider = new TokenCredentialMysqlPasswordProvider(credential);\n        }\n\n        public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)\n        {\n            var mysqlConnection = (MySqlConnection)connection;\n            mysqlConnection.ProvidePasswordCallback = _passwordProvider.ProvidePassword;\n            return result;\n        }\n\n        public override ValueTask\u003cInterceptionResult\u003e ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)\n        {\n            var mysqlConnection = (MySqlConnection)connection;\n            mysqlConnection.ProvidePasswordCallback = _passwordProvider.ProvidePassword;\n            return ValueTask.FromResult(result);\n        }\n    }\n}\n```\n\nThere are more usage examples in project [Batec.Azure.Data.Extensions.MySqlConnectorTests](./Batec.Azure.Data.Extensions.MySqlConnectorTests/)\n\n## Nuget packages\n\nThis repository produces the following Nuget packages:\n\n* [Batec.Azure.Data.Extensions.Npgsql](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.Npgsql)\n* [Batec.Azure.Data.Extensions.MySqlConnector](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.MySqlConnector)\n* [Batec.Azure.Data.Extensions.Core](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.Core). This package is referenced by the other two packages.\n\nAnd here the Entity Framework packages:\n* [Batec.Azure.Data.Extensions.Npgsql.EntityFrameworkCore](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.Npgsql.EntityFrameworkCore)\n* [Batec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.Pomelo.EntityFrameworkCore)\n* [Batec.Azure.Data.Extensions.MySql.EntityFrameworkCore](https://www.nuget.org/packages/Batec.Azure.Data.Extensions.MySql.EntityFrameworkCore)\n\n### Experimental packages\nThe experimental MySql.Data package is available in the following feed:\n* [Batec.Azure.Data.Extensions.MySql](https://github.com/felipmiguel/AzureDb.Passwordless/pkgs/nuget/Batec.Azure.Data.Extensions.MySql). This package references [MySql.Data experimental implementation](https://github.com/felipmiguel?tab=packages\u0026repo_name=mysql-connector-net). [!WARNING] This is an experimental and not recommended approach. It can break in future versions of the driver.\n\nIf you want to use above packages you should add the nuget feed to your project:\n\n```bash\ndotnet nuget add source --username [YOUR GITHUB USERID] --password [YOUR PAT] --store-password-in-clear-text --name github \"https://nuget.pkg.github.com/felipmiguel/index.json\"\n```\n\nYou PAT should include the following scope `_read:packages_`.\n\n## Test projects\n\n* [Batec.Azure.Data.Extensions.NpgsqlTests](./Batec.Azure.Data.Extensions.NpgsqlTests/)\n* [Batec.Azure.Data.Extensions.MySqlConnectorTests](./Batec.Azure.Data.Extensions.MySqlConnectorTests/)\n* [Batec.Azure.Data.Extensions.MySqlTests](./Batec.Azure.Data.Extensions.MySqlTests/)\n\n### Entity Framework Core\n\nThere is a sample Entity Framework Core project that can be used with all tests. It is located in [Sample.Repository](./Sample.Repository/). \nTo use it in your project:\n* Reference the project\n* Add a class implementing `IDesignTimeDbContextFactory\u003cChecklistContext\u003e`\n\nHere an example for Postgresql:\n\n```csharp\npublic class ChecklistContextFactory : IDesignTimeDbContextFactory\u003cChecklistContext\u003e\n{\n    public ChecklistContext CreateDbContext(string[] args)\n    {\n        Console.WriteLine(\"args {0}\", string.Join(\",\", args));\n        ConfigurationBuilder configBuilder = new ConfigurationBuilder();\n        configBuilder\n            .AddJsonFile(\"appsettings.json\")\n            .AddJsonFile(\"appsettings.Deployment.json\");\n        IConfigurationRoot config = configBuilder.Build();\n        ServiceCollection services = new ServiceCollection();\n        services.AddDbContext\u003cChecklistContext\u003e(options =\u003e\n        {\n            options.UseNpgsql(GetPGConnString(config), optionsBuilder =\u003e\n            optionsBuilder\n                .MigrationsAssembly(Assembly.GetExecutingAssembly().FullName)\n                .UseAzureADAuthentication(new DefaultAzureCredential()));\n        });\n        var serviceProvider = services.BuildServiceProvider();\n        return serviceProvider.GetRequiredService\u003cChecklistContext\u003e();\n    }\n    private string GetPGConnString(IConfiguration configuration)\n    {\n        return configuration.GetConnectionString(\"AZURE_POSTGRESQL_CONNECTIONSTRING\");\n    }\n}\n```\n\nThen execute the following dotnet commands to initialize the database.\n\n```bash\ndotnet build\n# Get connections string\nCONNSTRING=\"YOUR CONNECTION STRING\"\nASPNETCORE_ENVIRONMENT=Deployment\n# replace AZURE_POSTGRESQL_CONNECTIONSTRING by the connection string referenced in your code\necho \"{\\\"ConnectionStrings\\\":{\\\"AZURE_POSTGRESQL_CONNECTIONSTRING\\\":\\\"${CONNSTRING}\\\"}}\" \u003eappsettings.Deployment.json\ndotnet tool install --global dotnet-ef\ndotnet add package Microsoft.EntityFrameworkCore.Design\ndotnet ef migrations add InitialCreate\ndotnet ef database update\n```\n\u003e [!NOTE] It is necessary to install entity framework tool `dotnet tool install --global dotnet-ef`\n\n## Sample projects\n\n[batec-ossrdbms-demo](https://github.com/felipmiguel/batec-ossrdbms-demo) This is complete example of how to use the libraries in a webapi project. It can be configured with Azure Database for Postgresql Flexible Server and Azure Database for MySql Flexible Server. It includes the deployment automation for managed identities and Azure Database Azure AD authentication using Terraform.\n\n[Dotnet.Passwordless.Samples](https://github.com/felipmiguel/DotNet.Passwordless.Samples) contains a sample with a webapi that can be deployed to Azure App Service, using Azure Managed Identity to access a database. There is a sample for Azure Database for Postgresql flexible server, MySqlAzure Database for MySql flexible server and Azure Sql Server.\n\n## Reference links\n\n* Postgresql: \u003chttps://www.npgsql.org/doc/security.html?tabs=tabid-1\u003e\n* MySql: \u003chttps://dev.mysql.com/doc/connector-net/en/connector-net-programming-authentication-user-plugin.html\u003e\n* MySqlConnector: \u003chttps://mysqlconnector.net/\u003e\n* Pomelo: \u003chttps://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipmiguel%2Fazuredb.passwordless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffelipmiguel%2Fazuredb.passwordless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipmiguel%2Fazuredb.passwordless/lists"}