Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions sdk/postgresql/Microsoft.Azure.PostgreSQL.Auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on keep-a-changelog principles and this project adheres to semantic versioning.

## [Unreleased]

### Added
- Implement Entra ID authentication libraries for Npgsql to connect to a PostgreSQL database

### Changed
-

### Fixed
- Remove dependency on DefaultAzureCredential in source library
Comment on lines +13 to +16
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG has a "Fixed" entry stating "Remove dependency on DefaultAzureCredential in source library" but this is a new library being added. The "Fixed" section should be empty or this should be in a different section, as there was no previous version to fix.

Suggested change
-
### Fixed
- Remove dependency on DefaultAzureCredential in source library
- Remove dependency on DefaultAzureCredential in source library
### Fixed
-

Copilot uses AI. Check for mistakes.

### Deprecated
-

### Removed
-

### Security
-

---
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{4D180A10-CB16-4781-BEB7-EAE2038A0993}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{5D20AA90-6969-D8BD-9DCD-8634F4692FDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GettingStarted", "samples\GettingStarted\GettingStarted.csproj", "{8528AA1A-1D38-48FF-9234-F49492A460C1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft", "Microsoft", "{7ECBF5AD-AE5E-07AC-33F2-F258A53C5EA9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure", "Azure", "{A542B900-C6AB-CFD7-234F-C4052AF02FF1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PostgreSQL", "PostgreSQL", "{4F6131AE-B29D-8566-B8CA-1A2AABE27274}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{93934517-16C9-C51A-8F2B-54760F50BDEB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.PostgreSQL.Entra", "src\Microsoft\Azure\PostgreSQL\Auth\Microsoft.Azure.PostgreSQL.Auth.csproj", "{3E862DB4-B843-4361-94B5-8CF34402B511}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft", "Microsoft", "{8FEB4F0F-C974-64A2-0863-8577ABAC15AD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure", "Azure", "{AC05A953-B9EF-C104-E53F-E15EBB9C3478}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PostgreSQL", "PostgreSQL", "{7164C26A-6C7C-D37D-98D2-1150AFE094DD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Auth", "Auth", "{290860F1-0C73-540D-3A79-AA6C3ABBD9C3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.PostgreSQL.Entra.Tests", "tests\Microsoft\Azure\PostgreSQL\Auth\Microsoft.Azure.PostgreSQL.Auth.csproj", "{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}"
Comment on lines +25 to +37
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution file references the project with name "Microsoft.Azure.PostgreSQL.Entra" on line 25 and "Microsoft.Azure.PostgreSQL.Entra.Tests" on line 37, but the actual project files are named "Microsoft.Azure.PostgreSQL.Auth.csproj". This naming inconsistency will cause the solution file to incorrectly reference these projects. The project names in the solution should match the actual .csproj file names.

Copilot uses AI. Check for mistakes.
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|x64.ActiveCfg = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|x64.Build.0 = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|x86.ActiveCfg = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Debug|x86.Build.0 = Debug|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|Any CPU.Build.0 = Release|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|x64.ActiveCfg = Release|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|x64.Build.0 = Release|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|x86.ActiveCfg = Release|Any CPU
{8528AA1A-1D38-48FF-9234-F49492A460C1}.Release|x86.Build.0 = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|x64.ActiveCfg = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|x64.Build.0 = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|x86.ActiveCfg = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Debug|x86.Build.0 = Debug|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|Any CPU.Build.0 = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|x64.ActiveCfg = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|x64.Build.0 = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|x86.ActiveCfg = Release|Any CPU
{3E862DB4-B843-4361-94B5-8CF34402B511}.Release|x86.Build.0 = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|x64.ActiveCfg = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|x64.Build.0 = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|x86.ActiveCfg = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Debug|x86.Build.0 = Debug|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|Any CPU.Build.0 = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|x64.ActiveCfg = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|x64.Build.0 = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|x86.ActiveCfg = Release|Any CPU
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8528AA1A-1D38-48FF-9234-F49492A460C1} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA}
{7ECBF5AD-AE5E-07AC-33F2-F258A53C5EA9} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{A542B900-C6AB-CFD7-234F-C4052AF02FF1} = {7ECBF5AD-AE5E-07AC-33F2-F258A53C5EA9}
{4F6131AE-B29D-8566-B8CA-1A2AABE27274} = {A542B900-C6AB-CFD7-234F-C4052AF02FF1}
{93934517-16C9-C51A-8F2B-54760F50BDEB} = {4F6131AE-B29D-8566-B8CA-1A2AABE27274}
{3E862DB4-B843-4361-94B5-8CF34402B511} = {93934517-16C9-C51A-8F2B-54760F50BDEB}
{8FEB4F0F-C974-64A2-0863-8577ABAC15AD} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{AC05A953-B9EF-C104-E53F-E15EBB9C3478} = {8FEB4F0F-C974-64A2-0863-8577ABAC15AD}
{7164C26A-6C7C-D37D-98D2-1150AFE094DD} = {AC05A953-B9EF-C104-E53F-E15EBB9C3478}
{290860F1-0C73-540D-3A79-AA6C3ABBD9C3} = {7164C26A-6C7C-D37D-98D2-1150AFE094DD}
{750B2A4F-9EF5-4CC5-8EF9-A93F4A1748F6} = {290860F1-0C73-540D-3A79-AA6C3ABBD9C3}
EndGlobalSection
EndGlobal
78 changes: 78 additions & 0 deletions sdk/postgresql/Microsoft.Azure.PostgreSQL.Auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
## Overview

This library enables Microsoft Entra ID authentication for Azure Database for PostgreSQL using the Npgsql driver. It eliminates database password management by using secure, token-based authentication through Azure's identity platform.

### Key Benefits

- **Passwordless Authentication**: Uses OAuth 2.0 access tokens instead of database passwords
- **Centralized Identity Management**: Leverages existing Entra ID users and groups
- **Zero Secrets**: No database credentials stored in application code
- **Automatic Token Handling**: Manages token acquisition and renewal transparently

## Prerequisites

**Azure Database for PostgreSQL Setup:**
- Azure Database for PostgreSQL server with Entra ID authentication enabled
- Entra ID administrator configured (to set up database users)
- Application's Entra ID identity created as a database user with appropriate permissions

**Application Identity (choose one):**
- **Managed Identity**: For applications running in Azure (App Service, Functions, VMs)
- **Service Principal**: For applications with client credentials
- **User Identity**: For development or interactive scenarios

**Example PostgreSQL Setup:**
```sql
-- Connect as Entra ID administrator and create database user
CREATE ROLE "[email protected]" WITH LOGIN;
GRANT CONNECT ON DATABASE mydb TO "[email protected]";
GRANT USAGE ON SCHEMA public TO "[email protected]";
-- Grant additional permissions as needed
```

## Usage

In your program, import the namespace `Microsoft.Azure.PostgreSQL.Auth`

```csharp
using Microsoft.Azure.PostgreSQL.Auth;
```
Use the extension methods as needed:

### Asynchronous Authentication (Recommended)
```csharp
using Azure.Identity;

// Fill in with connection information to Azure PostgreSQL server
// Note: No username/password in connection string - authentication handled by Entra ID
var connectionString = "Host=myserver.postgres.database.azure.com;Database=mydb;Port=5432;SSL Mode=Require;";
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);

// Use the async extension method for Entra authentication
// This will automatically:
// - Detect the current Azure identity (managed identity, service principal, or user)
// - Acquire a PostgreSQL-scoped access token
// - Configure the connection to use token-based authentication
var credential = new DefaultAzureCredential();
await dataSourceBuilder.UseEntraAuthenticationAsync(credential);
```

### Synchronous Authentication
```csharp
using Azure.Identity;

// Fill in with connection information to Azure PostgreSQL server
var connectionString = "Host=myserver.postgres.database.azure.com;Database=mydb;Port=5432;SSL Mode=Require;";
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);

// Use the sync extension method for Entra authentication
var credential = new DefaultAzureCredential();
dataSourceBuilder.UseEntraAuthentication(credential);
```

## Benefits

- **Enhanced Security**: No database passwords to manage or rotate
- **Simplified Deployment**: Works seamlessly with Azure managed identities
- **Compliance**: Supports enterprise identity governance and MFA requirements
- **Developer Experience**: Transparent authentication - existing Npgsql code works unchanged
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Identity;
using Npgsql;
using Microsoft.Azure.PostgreSQL.Auth;
using Microsoft.Extensions.Configuration;

namespace GettingStarted;

/// <summary>
/// This example enables Entra authentication before connecting to the database via NpgsqlConnection.
/// </summary>
public class CreateDbConnectionNpgsql
{
public static async Task Main(string[] args)
{
Console.WriteLine("=== Getting Started with Azure Entra Authentication for PostgreSQL ===\n");

// Build configuration
var configuration = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();

// Read configuration values and build connection string once
var server = configuration["Host"];
var database = configuration["Database"] ?? "postgres";
var port = configuration.GetValue<int>("Port", 5432);
var connectionString = $"Host={server};Database={database};Port={port};SSL Mode=Require;";

Console.WriteLine("--- Testing UseEntraAuthentication (sync) ---");
await ExecuteQueriesWithEntraAuth(connectionString, useAsync: false);

Console.WriteLine("\n--- Testing UseEntraAuthenticationAsync ---");
await ExecuteQueriesWithEntraAuth(connectionString, useAsync: true);

Console.WriteLine("\n=== Sample completed ===");
}

/// <summary>
/// Show how to create a connection to the database with Entra authentication and execute some prompts.
/// </summary>
/// <param name="connectionString">The PostgreSQL connection string</param>
/// <param name="useAsync">If true, uses UseEntraAuthenticationAsync; otherwise uses UseEntraAuthentication</param>
private static async Task ExecuteQueriesWithEntraAuth(string connectionString, bool useAsync = false)
{

var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);

// Here, we use the appropriate extension method provided by NpgsqlDataSourceBuilderExtensions.cs
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment references "NpgsqlDataSourceBuilderExtensions.cs" but the actual class name is "EntraIdExtension". This could confuse developers trying to understand the code.

Suggested change
// Here, we use the appropriate extension method provided by NpgsqlDataSourceBuilderExtensions.cs
// Here, we use the appropriate extension method provided by the EntraIdExtension class

Copilot uses AI. Check for mistakes.
// to enable Entra Authentication. This will handle username extraction and token refresh as needed.
var credential = new DefaultAzureCredential();
if (useAsync)
{
await dataSourceBuilder.UseEntraAuthenticationAsync(credential);
}
else
{
dataSourceBuilder.UseEntraAuthentication(credential);
}

using var dataSource = dataSourceBuilder.Build();
await using var connection = await dataSource.OpenConnectionAsync();

// Get PostgreSQL version
using var cmd1 = new NpgsqlCommand("SELECT version()", connection);
var version = await cmd1.ExecuteScalarAsync();
Console.WriteLine($"PostgreSQL Version: {version}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Npgsql" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="8.0.0" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft\Azure\PostgreSQL\Auth\Microsoft.Azure.PostgreSQL.Auth.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"Host": "myserver.postgres.database.azure.com",
"Database": "mydatabase",
"Port": 5432
}
Loading
Loading