Skip to content

Commit f6dd6be

Browse files
committed
[PostgreSQL] Create azure-postgresql-auth package
Signed-off-by: Paul Van Eck <paulvaneck@microsoft.com>
1 parent 1e540de commit f6dd6be

42 files changed

Lines changed: 2634 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Release History
2+
3+
## 1.2.0b1 (Unreleased)
4+
5+
### Features Added
6+
7+
### Breaking Changes
8+
9+
### Bugs Fixed
10+
11+
- Removed dependency on `DefaultAzureCredential` in source library
12+
13+
### Other Changes
14+
15+
## 1.0.1 (2025-11-26)
16+
17+
### other changes
18+
19+
- Update author to Microsoft
20+
21+
## 1.0.0 (2025-11-14)
22+
23+
### Features Added
24+
25+
- Initial public release
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Copyright (c) Microsoft Corporation.
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
recursive-include tests *.py *.yaml
2+
include *.md
3+
include LICENSE
4+
recursive-include samples *.py *.md
5+
include azure_postgresql_auth/py.typed
6+
recursive-include doc *.rst
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Azure PostgreSQL Auth client library for Python
2+
3+
The Azure PostgreSQL Auth client library provides Microsoft Entra ID authentication for Python database drivers connecting to Azure Database for PostgreSQL. It supports psycopg2, psycopg3, and SQLAlchemy with automatic token management and connection pooling.
4+
5+
[Source code](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/postgresql/azure-postgresql-auth)
6+
| [Package (PyPI)](https://pypi.org/project/azure-postgresql-auth/)
7+
| [Samples](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/postgresql/azure-postgresql-auth/samples)
8+
9+
## Getting started
10+
11+
### Prerequisites
12+
13+
- Python 3.9 or later
14+
- An Azure subscription
15+
- An Azure Database for PostgreSQL Server instance with Entra ID authentication enabled
16+
- A credential object that implements the [TokenCredential](https://learn.microsoft.com/python/api/azure-core/azure.core.credentials.tokencredential) interface
17+
18+
### Install the package
19+
20+
Install the core package:
21+
22+
```bash
23+
pip install azure-postgresql-auth
24+
```
25+
26+
Install with driver-specific extras:
27+
28+
```bash
29+
# For psycopg3 (recommended for new projects)
30+
pip install "azure-postgresql-auth[psycopg3]"
31+
32+
# For psycopg2 (legacy support)
33+
pip install "azure-postgresql-auth[psycopg2]"
34+
35+
# For SQLAlchemy
36+
pip install "azure-postgresql-auth[sqlalchemy]"
37+
```
38+
39+
Install Azure Identity for credential support:
40+
41+
```bash
42+
pip install azure-identity
43+
```
44+
45+
## Key concepts
46+
47+
### Authentication flow
48+
49+
1. **Token Acquisition**: Uses Azure Identity credentials to acquire access tokens from Microsoft Entra ID.
50+
2. **Automatic Refresh**: Tokens are acquired for each new database connection.
51+
3. **Secure Transport**: Tokens are passed as passwords in PostgreSQL connection strings over SSL.
52+
4. **Server Validation**: Azure Database for PostgreSQL validates the token and establishes the authenticated connection.
53+
5. **User Mapping**: The token's user principal name (UPN) is mapped to a PostgreSQL user for authorization.
54+
55+
### Token scopes
56+
57+
The library requests the following OAuth2 scopes:
58+
59+
- **Database scope**: `https://ossrdbms-aad.database.windows.net/.default` (primary)
60+
- **Management scope**: `https://management.azure.com/.default` (fallback for managed identities)
61+
62+
### Driver support
63+
64+
- **psycopg3**: Modern PostgreSQL driver (recommended for new projects) — sync and async support
65+
- **psycopg2**: Legacy PostgreSQL driver — synchronous only
66+
- **SQLAlchemy**: ORM/Core interface using event listeners — sync and async engine support
67+
68+
### Security features
69+
70+
- **Token-based authentication**: No passwords stored or transmitted
71+
- **Automatic expiration**: Tokens expire and are refreshed automatically
72+
- **SSL enforcement**: All connections require SSL encryption
73+
- **Principle of least privilege**: Only database-specific scopes are requested
74+
75+
## Examples
76+
77+
### Configuration
78+
79+
The samples use environment variables to configure database connections. Copy `.env.example` into a `.env` file
80+
in the same directory as the sample and update the variables:
81+
82+
```
83+
POSTGRES_SERVER=<your-server.postgres.database.azure.com>
84+
POSTGRES_DATABASE=<your_database_name>
85+
```
86+
87+
### psycopg2 — Connection pooling
88+
89+
```python
90+
from azure_postgresql_auth.psycopg2 import EntraConnection
91+
from azure.identity import DefaultAzureCredential
92+
from psycopg2 import pool
93+
from functools import partial
94+
95+
credential = DefaultAzureCredential()
96+
connection_factory = partial(EntraConnection, credential=credential)
97+
98+
with pool.ThreadedConnectionPool(
99+
minconn=1,
100+
maxconn=5,
101+
host="your-server.postgres.database.azure.com",
102+
database="your_database",
103+
connection_factory=connection_factory,
104+
) as connection_pool:
105+
conn = connection_pool.getconn()
106+
with conn.cursor() as cur:
107+
cur.execute("SELECT 1")
108+
```
109+
110+
### psycopg2 — Direct connection
111+
112+
```python
113+
from azure_postgresql_auth.psycopg2 import EntraConnection
114+
from azure.identity import DefaultAzureCredential
115+
116+
with EntraConnection(
117+
"postgresql://your-server.postgres.database.azure.com:5432/your_database",
118+
credential=DefaultAzureCredential(),
119+
) as conn:
120+
with conn.cursor() as cur:
121+
cur.execute("SELECT 1")
122+
```
123+
124+
### psycopg3 — Synchronous connection
125+
126+
```python
127+
from azure_postgresql_auth.psycopg3 import EntraConnection
128+
from azure.identity import DefaultAzureCredential
129+
from psycopg_pool import ConnectionPool
130+
131+
with ConnectionPool(
132+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
133+
connection_class=EntraConnection,
134+
kwargs={"credential": DefaultAzureCredential()},
135+
min_size=1,
136+
max_size=5,
137+
) as pg_pool:
138+
with pg_pool.connection() as conn:
139+
with conn.cursor() as cur:
140+
cur.execute("SELECT 1")
141+
```
142+
143+
### psycopg3 — Asynchronous connection
144+
145+
```python
146+
from azure_postgresql_auth.psycopg3 import AsyncEntraConnection
147+
from azure.identity.aio import DefaultAzureCredential
148+
from psycopg_pool import AsyncConnectionPool
149+
150+
async with AsyncConnectionPool(
151+
conninfo="postgresql://your-server.postgres.database.azure.com:5432/your_database",
152+
connection_class=AsyncEntraConnection,
153+
kwargs={"credential": DefaultAzureCredential()},
154+
min_size=1,
155+
max_size=5,
156+
) as pg_pool:
157+
async with pg_pool.connection() as conn:
158+
async with conn.cursor() as cur:
159+
await cur.execute("SELECT 1")
160+
```
161+
162+
### SQLAlchemy — Synchronous engine
163+
164+
> For more information, see SQLAlchemy's documentation on
165+
> [controlling how parameters are passed to the DBAPI connect function](https://docs.sqlalchemy.org/en/20/core/engines.html#controlling-how-parameters-are-passed-to-the-dbapi-connect-function).
166+
167+
```python
168+
from sqlalchemy import create_engine
169+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication
170+
from azure.identity import DefaultAzureCredential
171+
172+
engine = create_engine(
173+
"postgresql+psycopg://your-server.postgres.database.azure.com/your_database",
174+
connect_args={"credential": DefaultAzureCredential()},
175+
)
176+
enable_entra_authentication(engine)
177+
178+
with engine.connect() as conn:
179+
result = conn.execute(text("SELECT 1"))
180+
```
181+
182+
### SQLAlchemy — Asynchronous engine
183+
184+
```python
185+
from sqlalchemy.ext.asyncio import create_async_engine
186+
from azure_postgresql_auth.sqlalchemy import enable_entra_authentication_async
187+
from azure.identity import DefaultAzureCredential
188+
189+
engine = create_async_engine(
190+
"postgresql+psycopg://your-server.postgres.database.azure.com/your_database",
191+
connect_args={"credential": DefaultAzureCredential()},
192+
)
193+
enable_entra_authentication_async(engine)
194+
195+
async with engine.connect() as conn:
196+
result = await conn.execute(text("SELECT 1"))
197+
```
198+
199+
## Troubleshooting
200+
201+
### Authentication errors
202+
203+
If you get "password authentication failed", ensure your Azure identity has been granted access to the database:
204+
205+
```sql
206+
-- Run as a database administrator
207+
CREATE ROLE "your-user@your-domain.com" WITH LOGIN;
208+
GRANT ALL PRIVILEGES ON DATABASE your_database TO "your-user@your-domain.com";
209+
```
210+
211+
### Connection timeouts
212+
213+
Increase the connection timeout for slow networks:
214+
215+
```python
216+
conn = EntraConnection.connect(
217+
"postgresql://server:5432/db",
218+
credential=DefaultAzureCredential(),
219+
connect_timeout=30,
220+
)
221+
```
222+
223+
### Windows async compatibility
224+
225+
On Windows, you may need to set the event loop policy for async usage:
226+
227+
```python
228+
import asyncio
229+
import sys
230+
231+
if sys.platform == "win32":
232+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
233+
```
234+
235+
### Debug logging
236+
237+
Enable debug logging to troubleshoot authentication issues:
238+
239+
```python
240+
import logging
241+
logging.basicConfig(level=logging.DEBUG)
242+
```
243+
244+
## Next steps
245+
246+
### Additional documentation
247+
248+
For more information about Azure Database for PostgreSQL Entra ID authentication, see the
249+
[Azure documentation](https://learn.microsoft.com/azure/postgresql/security/security-entra-configure).
250+
251+
### Samples
252+
253+
Explore [sample code](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/postgresql/azure-postgresql-auth/samples) for psycopg2, psycopg3, and SQLAlchemy.
254+
255+
## Contributing
256+
257+
This project welcomes contributions and suggestions. Most contributions require you to agree to a
258+
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
259+
the rights to use your contribution. For details, visit [https://cla.microsoft.com](https://cla.microsoft.com).
260+
261+
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
262+
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
263+
provided by the bot. You will only need to do this once across all repos using our CLA.
264+
265+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
266+
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
267+
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
268+
269+
![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-python%2Fsdk%2Fpostgresql%2Fazure-postgresql-auth%2FREADME.png)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
"""
8+
Azure PostgreSQL Auth client library for Python.
9+
10+
This library provides Microsoft Entra ID authentication for Python database drivers
11+
connecting to Azure Database for PostgreSQL. It supports psycopg2, psycopg3,
12+
and SQLAlchemy with automatic token management.
13+
14+
Available submodules (with optional dependencies):
15+
- psycopg2: Support for psycopg2 driver (pip install azure-postgresql-auth[psycopg2])
16+
- psycopg3: Support for psycopg (v3) driver (pip install azure-postgresql-auth[psycopg3])
17+
- sqlalchemy: Support for SQLAlchemy ORM (pip install azure-postgresql-auth[sqlalchemy])
18+
"""
19+
20+
from azure_postgresql_auth._version import VERSION
21+
22+
__version__ = VERSION
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
VERSION = "1.2.0b1"

0 commit comments

Comments
 (0)