Authentication is usually one of the first security decisions made when building an ASP.NET Core application.
For many applications, the architecture looks like this:
User
|
v
ASP.NET Core
|
v
Identity Provider
|
v
Application
|
v
PostgreSQL
The application authenticates the user, creates an application identity, and then connects to PostgreSQL using a database credential.
PostgreSQL 18 adds another option at the database layer: OAuth 2.0 authentication.
PostgreSQL 18 introduces an oauth authentication method in pg_hba.conf, along with OAuth-related libpq client support and token validation infrastructure. PostgreSQL's documentation describes this as authorization and optional authentication through a third-party OAuth 2.0 identity provider.
This creates an interesting architecture for .NET applications:
Identity Provider
OAuth / OIDC
|
+-----------+-----------+
| |
v v
ASP.NET Core PostgreSQL 18
| |
| |
+-------- Identity -------+
However, PostgreSQL OAuth authentication and ASP.NET Core Identity solve different problems.
ASP.NET Core Identity manages application users, passwords, roles, claims, login flows, and related application identity concerns. PostgreSQL OAuth authentication controls how a PostgreSQL client authenticates when connecting to the database.
Understanding that distinction is essential before replacing traditional database credentials.
What PostgreSQL 18 Adds
PostgreSQL 18 adds the oauth authentication method to pg_hba.conf. The PostgreSQL server can use an external OAuth 2.0 identity provider, while the client obtains and presents an access token.
A simplified flow looks like:
Application
|
| OAuth token
v
PostgreSQL Client
|
v
PostgreSQL 18
|
v
OAuth Token Validator
|
v
Identity Provider
PostgreSQL documents configuration parameters including:
issuerscopevalidatormap
The issuer identifies the authorization server, while scope defines the OAuth scopes required for authentication and authorization. PostgreSQL can also map the identity provider identity to a PostgreSQL role.
OAuth Authentication Is Not the Same as OpenID Connect Login
This distinction is important.
OAuth 2.0 primarily defines delegated authorization.
OpenID Connect builds an authentication layer on top of OAuth 2.0.
An ASP.NET Core application may use OpenID Connect like this:
Browser
|
v
ASP.NET Core
|
v
OpenID Connect Provider
|
v
ID Token / Claims
PostgreSQL OAuth authentication is different:
.NET Database Client
|
v
OAuth Access Token
|
v
PostgreSQL 18
PostgreSQL uses the OAuth token to authorize and optionally authenticate the database connection.
So adding PostgreSQL OAuth does not mean ASP.NET Core Identity disappears.
Where ASP.NET Core Identity Fits
ASP.NET Core Identity provides application-level identity functionality.
A typical application may have:
ASP.NET Core
|
+--> ApplicationUser
+--> Roles
+--> Claims
+--> Login
+--> Password Management
+--> External Login
Microsoft's documentation describes ASP.NET Core Identity as providing UI-oriented login functionality for ASP.NET Core web applications. For applications using external identity platforms, ASP.NET Core can also integrate with OAuth and OpenID Connect providers.
PostgreSQL authentication sits below this layer.
Application Layer
-----------------
ASP.NET Core Identity
Authorization
Business Logic
Database Layer
--------------
PostgreSQL Authentication
Database Roles
Database Privileges
The two layers can use the same identity provider without being the same authentication mechanism.
A Practical Architecture
Consider a multi-tenant SaaS application.
Identity Provider
|
+---------+---------+
| |
v v
ASP.NET Core PostgreSQL 18
| |
v v
Application User Database Role
| |
+---------+---------+
|
v
Data Access
The identity provider can be responsible for authenticating the human user.
PostgreSQL OAuth can provide a token-based authentication mechanism for the database connection.
The application still needs authorization logic for deciding what that authenticated user is allowed to do.
Authentication answers:
Who are you?
Authorization answers:
What are you allowed to access?
Those are separate questions.
PostgreSQL OAuth Configuration
PostgreSQL's pg_hba.conf controls client authentication. PostgreSQL 18 supports:
oauth
as an authentication method.
A conceptual configuration looks like:
hostssl all all 10.0.0.0/24 oauth
A more complete OAuth configuration uses authentication options:
hostssl appdb appuser 10.0.0.0/24 oauth
issuer="https://identity.example.com"
scope="postgres"
The exact issuer and scope values depend on the identity provider and OAuth validator configuration.
Do not copy these values directly into production.
The PostgreSQL documentation notes that the issuer must match the identity provider's discovery metadata exactly, including formatting and case.
Why hostssl Matters
OAuth tokens are credentials.
You should therefore protect the database connection with TLS.
A typical rule is:
hostssl appdb appuser 10.0.0.0/24 oauth ...
rather than exposing bearer-token authentication over an unencrypted connection.
The complete TLS configuration depends on your PostgreSQL deployment.
The important architectural path is:
OAuth Token
|
v
TLS Connection
|
v
PostgreSQL
PostgreSQL Role Mapping
OAuth identity and PostgreSQL role names do not necessarily have to be identical.
PostgreSQL supports a map option for mapping identity-provider identities to database usernames. If no mapping is configured, the identity determined by the OAuth validator must match the requested PostgreSQL role name.
Conceptually:
OAuth Subject
|
v
Identity Mapping
|
v
PostgreSQL Role
For example:
[email protected]
|
v
ApplicationUser
|
v
reporting_user
The exact mapping strategy depends on how the identity provider and PostgreSQL roles are organized.
Why Database Roles Still Matter
OAuth authentication does not eliminate PostgreSQL privileges.
Suppose a user successfully authenticates.
That does not mean the user should have:
SELECT * FROM sensitive_customers;
PostgreSQL permissions still matter.
For example:
GRANT CONNECT ON DATABASE appdb
TO reporting_user;
GRANT USAGE ON SCHEMA public
TO reporting_user;
GRANT SELECT ON TABLE sales
TO reporting_user;
Do not grant broader permissions simply because OAuth is being used.
The authentication mechanism and database authorization model should remain separate.
ASP.NET Core Configuration
An ASP.NET Core application can continue using its normal authentication setup.
For example, an OpenID Connect application can be configured with:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority =
builder.Configuration["Identity:Authority"];
options.ClientId =
builder.Configuration["Identity:ClientId"];
options.ClientSecret =
builder.Configuration["Identity:ClientSecret"];
options.ResponseType = "code";
options.UsePkce = true;
});
Microsoft currently recommends the OpenID Connect authorization-code approach with PKCE for appropriate ASP.NET Core web applications.
The database connection is a separate concern.
The Important Question: Which Token Goes to PostgreSQL?
Do not automatically send the user's ASP.NET authentication token to PostgreSQL.
The token audience, scopes, issuer, lifetime, and intended resource must match the PostgreSQL authentication configuration.
A better architecture is:
Browser
|
v
ASP.NET Core
|
+--> User Authentication
|
+--> Application Authorization
|
+--> Database Token Acquisition
|
v
PostgreSQL 18
The database token should be issued for the intended PostgreSQL resource and scopes.
The exact token acquisition mechanism depends on your identity provider and PostgreSQL client configuration.
Using Npgsql in .NET
ASP.NET Core applications commonly use Npgsql to communicate with PostgreSQL.
A conventional connection string might look like:
Host=db.example.com;
Port=5432;
Database=appdb;
Username=appuser;
Password=...
SSL Mode=Require;
With OAuth authentication, the password-based credential model changes.
Conceptually:
Host
Port
Database
Username
OAuth Access Token
TLS
The exact Npgsql OAuth configuration depends on the Npgsql version and the OAuth flow supported by your identity provider.
This is an important area where documentation for the specific Npgsql version should be followed rather than assuming every version has identical OAuth APIs.
Keep Database Credentials Out of Source Code
Even if you are not using PostgreSQL OAuth, avoid:
var connectionString =
"Host=db;Database=app;Username=admin;Password=secret";
Use configuration providers and secret management instead.
For example:
var connectionString =
builder.Configuration.GetConnectionString(
"Postgres");
For production, store sensitive configuration in a managed secret system appropriate for your hosting environment.
Microsoft's ASP.NET Core documentation also recommends keeping application secrets out of application configuration checked into source control.
Token Lifetime Changes the Connection Model
Passwords can remain valid for a long time.
OAuth access tokens generally have limited lifetimes.
That means a database connection architecture needs to consider:
Token
|
v
Connection
|
v
Token Expiration
Questions to answer include:
How is the token obtained?
How long is it valid?
Does the connection pool reuse connections?
What happens when a token expires?
How does the client refresh credentials?
What happens to an existing connection?
How are authentication failures surfaced?
These questions should be tested rather than assumed.
Connection Pooling
ASP.NET Core applications commonly use connection pooling.
A simplified flow is:
Request
|
v
DbContext
|
v
Npgsql Pool
|
v
PostgreSQL
OAuth introduces another credential lifecycle into this design.
You need to test:
Token valid
|
v
Connection succeeds
Token near expiration
|
v
New connection
Token expired
|
v
Refresh / reconnect
The exact behavior depends on the Npgsql version and authentication implementation.
Do not disable connection pooling merely because OAuth is being introduced.
Measure the actual behavior first.
User Identity vs Application Identity
One of the most important architecture decisions is whether PostgreSQL should see:
Individual User
or:
Application / Service Identity
A traditional ASP.NET Core application often uses:
Users
|
v
ASP.NET Core
|
v
Application DB Role
For example:
All authenticated users
|
v
webapp_role
The application then enforces user-level authorization.
A more identity-aware design might be:
User A
|
v
OAuth Identity A
|
v
PostgreSQL Role A
This can provide stronger database-level identity separation, but it also introduces substantially more operational complexity.
Choose the model deliberately.
Application Authorization Still Matters
Suppose:
PostgreSQL Role:
sales_reader
and the user has:
Application Role:
SalesManager
These are not automatically equivalent.
Your ASP.NET Core application might allow:
SalesManager
|
+--> View Sales
+--> Export Reports
while PostgreSQL only provides:
SELECT
The application remains responsible for enforcing business-level authorization.
Do not assume PostgreSQL authentication replaces ASP.NET Core authorization policies.
Row-Level Security
For multi-tenant applications, PostgreSQL Row-Level Security can provide an additional database-level boundary.
Conceptually:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
Then a policy can restrict rows based on a database session context.
The exact design depends on how the authenticated identity is propagated.
A conceptual architecture is:
OAuth Identity
|
v
PostgreSQL Role / Session Context
|
v
RLS Policy
|
v
Tenant Rows
This can provide defense in depth.
However, don't implement RLS based on an untrusted client-supplied tenant identifier.
The tenant context must originate from trusted authentication or server-side authorization logic.

Join the conversation! Your thoughts help the community grow.