DEV Community

Authorization methods in .NET microservices

Authorization methods in .NET microservices

Today I want to dive into one of the most critical stages of microservices development: authorization. This stage can bring significant complexity. Each service is now a separate HTTP server with its own endpoints, and the client (bot, frontend, mobile app) must have simultaneous access to all of them.

Theory: Authentication vs Authorization in Microservices

In microservices architecture, it's crucial to distinguish between:

  • Authenticationwho is this?
  • Authorizationwhat is this subject allowed to do?

Authentication is typically handled by a dedicated Identity Provider (IdP) or Auth service, while microservices accept already-validated tokens and decide what the client can access.

High-level microservices security architecture

Main Authorization Approaches in Microservices

  • Session-based authorization (cookies + server-side session store)
  • Token-based: -> JWT (JSON Web Token) -> Opaque tokens
  • OAuth 2.0 / OpenID Connect (OIDC) on top of tokens (usually JWT)
  • API Keys
  • mTLS (mutual TLS) — mutual TLS authentication between services
  • Combinations (e.g., OIDC+JWT for users + mTLS between services)

🔐 JWT — compact signed token with claims (sub, roles, scopes, etc.) that can be validated locally without accessing shared state.

🌐 OAuth2/OIDC — protocols defining:

  • How clients obtain tokens
  • How SSO is implemented
  • Token format (JWT or opaque) is a separate layer

📈 Approach Features & 2026 Trends

Key trend: Moving authentication (and part of authorization) to a centralized IdP/Auth-hub, while microservices become "resource servers" that trust validated tokens.

Centralized IdP/Auth-hub + Resource Servers Pattern

Centralized IdP:

  • Stores user accounts, login methods, MFA, social login, etc.
  • Implements OAuth2/OIDC protocols and issues tokens (access, refresh, ID token)
  • Serves as the single source of truth for authentication and basic roles/groups

Resource Servers (microservices):

  • Host protected resources
  • Accept only requests with valid tokens (OAuth2 terminology)

Simultaneous zero-trust hardening: Even inside the cluster, services explicitly authenticate (mTLS, certificates, SPIFFE).

Quick Overview of Approaches

Approach Key Characteristics
Sessions Simpler for classic web apps/monoliths. Requires shared session store or sticky sessions in microservices (scaling complexity).
JWT Stateless approach, perfect for microservices + API gateway. De facto standard for OAuth2/OIDC access tokens in 2026.
OAuth2/OIDC Industry standard for external clients, mobile, SPA, B2B. Easy SSO, MFA, social login via IdP.
API Keys Simple keys for machine-to-machine access/integrations. Often combined with IP limits, rate limiting, gateway policies in 2026.
mTLS Standard service-to-service auth in service mesh (Istio, Linkerd, Consul). Guarantees "this pod is exactly that service" but carries no user rights.

Modern production standard: API Gateway/Ingress + OAuth2/OIDC + JWT + mTLS inside mesh.

1️⃣ Sessions (Cookies + Server-Side Sessions)

How It Works

  1. - User logs in on server (password, MFA)
  2. - Server generates unique session ID and stores full session in store (Redis, SQL, Memcached)
  3. - Session ID written to cookie (typically HttpOnly, Secure, SameSite=Strict/Lax)
  4. - Subsequent requests → server reads session ID from cookie → fetches session data from store
  5. - If session found and not expired → user authenticated

Microservices variants:

  • Sticky sessions: Load balancer binds user to one pod/service (by cookie)
  • Shared session store: All microservices read from central store (Redis cluster)

✅ Advantages

  • Implementation simplicity: Built into ASP.NET Core, Spring, Rails etc. No protocols to learn
  • Centralized control: Easy to revoke/end session in store (e.g., suspected compromise)
  • Security: Cookie contains no sensitive data (just ID), forgery-resistant (proper HttpOnly, Secure)
  • Perfect for SSR/traditional web: Browser auto-sends cookies

❌ Disadvantages & Microservices Issues

  • Scaling: Requires resilient session store → central point of failure
  • Redis outage = mass logout of ALL users
  • Sticky sessions limit flexibility: Can't freely scale/restart pods, hard multi-region migration
  • Performance: Every request = store read (network roundtrip), accumulates across services
  • Poor API/mobile/SPA fit: Cookie issues (CORS, cross-domain, native apps)
  • CSRF/XSS: Requires extra measures (CSRF tokens, SameSite)

When to Use

✅ Classic SSR web apps without complex API architecture
✅ Internal admin panels where simplicity > scale
✅ Monolith-to-microservices transition period
❌ Pure REST APIs, SPAs, mobile clients

2026 status: Rarely used in pure microservices, survives in legacy/BFF for web UI.

2️⃣ JWT (JSON Web Token)

How It Works

JWT consists of three parts:

  • Header – signing algorithm
  • Payload – claims (sub, roles, scopes, etc.)
  • Signature – signed with a private key

Flow:

  1. After login, the Auth service generates a JWT with the required claims and signs it with a private key.
  2. The client attaches the JWT to every request in the header: Authorization: Bearer <jwt_token>
  3. Each microservice validates locally:
    • Signature (using IdP public key from JWKS endpoint)
    • exp (expiration), iss (issuer), aud (audience), nbf (not before)
    • Claims (roles, scopes, etc.)
  4. If everything is valid, the service uses claims for authorization.

Signing options:

  • Symmetric (shared secret) — simpler, but worse for distributed systems
  • Asymmetric (RS256/ECDSA) — standard; public key exposed via JWKS

✅ Advantages

  • Stateless: No central session store, each service verifies tokens independently — perfect for microservices
  • Scalable: To add a new service, you just give it the IdP public key
  • Flexible claims: You can embed roles, tenant, permissions, feature flags — the service sees everything it needs immediately
  • Standard: Native support in all major frameworks (ASP.NET Core JwtBearer, Spring Security, etc.)
  • Cross-platform: Works equally well for browsers, mobile apps, CLI tools, partners

❌ Disadvantages

  • Revocation is hard: Until the token expires (usually 15–60 minutes), it remains valid
    • Workarounds: short TTL + refresh tokens, blacklists, introspection endpoints (but this hurts statelessness)
  • Token size: Can be large with many claims, increasing HTTP header size
  • Claims leakage: If a token is captured, the attacker can read roles/rights (though cannot forge signature)
  • Crypto pitfalls: Incorrect validation (not checking iss/aud/exp) is a common vulnerability

When to Use

  • Primary format for access tokens in microservice architectures
  • Always in combination with OAuth2/OIDC
  • Standalone — for simple internal APIs

2026 status: De facto standard access token format in OAuth2/OIDC, typically with short-lived access tokens + refresh tokens.

3️⃣ OAuth2 / OIDC

How It Works

  • OAuth2 is an authorization protocol (how a client obtains a token with specific access rights).
  • OIDC (OpenID Connect) is an identity layer on top of OAuth2 (who is the user).

Roles:

  • Resource Owner — the user
  • Client — SPA, mobile app, BFF
  • Authorization Server — IdP (Auth server)
  • Resource Server — microservice (API)

Main Flows Used with Microservices

  1. Authorization Code Flow + PKCE (for SPA/mobile):

    • Client redirects the user to the IdP
    • After login, IdP returns an authorization code
    • Client exchanges the code for access and refresh tokens
  2. Client Credentials Flow (service-to-service):

    • Service uses client_id/client_secret to obtain a token on its own behalf
  3. Implicit Flow (legacy, for old SPA apps — considered obsolete)

  4. Device Code Flow (for TV/CLI and limited-input devices)

Token Types

  • Access Token — carries access rights (what the client can do)
  • Refresh Token — used to obtain new access tokens
  • ID Token (OIDC) — contains user identity information (who the user is)

✅ Advantages

  • Standardized: Works with any modern IdP (Keycloak, Auth0, Azure AD, Google, etc.)
  • SSO & federation: One login for all services, easy integration with external identity providers
  • Supports many client types: SPA, native apps, backend services, partners
  • Scopes & delegated access: Clients receive only the permissions they actually need (read:orders, write:payments, etc.)
  • Security features: PKCE against code injection, short-lived tokens, audit trails

❌ Disadvantages

  • Complexity: Many moving parts (redirect_uri, scopes, client_id, PKCE), easy to misconfigure
  • IdP dependency: IdP must be highly available and secure
  • Frontend complexity: SPA/mobile must correctly handle redirects, token storage, and silent refresh

When to Use

  • Any public REST APIs with external clients
  • Microservice systems that require SSO
  • B2B integrations and partner access

2026 status: The mandatory standard for enterprise and public-facing APIs.

4️⃣ API Keys

How It Works

  1. A unique key is generated (typically a 32–64 character string, sometimes with a prefix).
  2. The client (service, script, partner) sends the key in a header, for example:
    • X-API-Key: abc123...
    • or Authorization: ApiKey abc123...
  3. The gateway/microservice validates the key against a database or cache (e.g., Redis).
  4. The key is usually associated with:
    • IP restrictions
    • Rate limits
    • Scopes
    • Expiration time

✅ Advantages

  • Maximum simplicity: No complex protocols, redirects, or crypto handshakes
  • Fast to implement: Great for internal tools, CI/CD pipelines, partner systems
  • Flexible policies: Rate limiting, IP whitelists, daily/monthly quotas
  • Easy revocation: Just remove or disable the key in the database

❌ Disadvantages

  • No user identity: The key is tied to an application/service, not to an individual user
  • Coarse-grained access: Often “all or nothing” or very rough scopes
  • Key management overhead: Secure issuing, rotation, and audit are required
  • No inherent encryption: Keys can appear in logs/traffic — TLS is mandatory

When to Use

  • Internal services and scripts
  • Partner integrations without complex per-user permissions
  • Prototypes and proof-of-concept APIs

2026 status: Commonly used in combination with API gateways (rate limiting + IP checks), but not the primary method for user-facing authorization.

5️⃣ mTLS (Mutual TLS)

How It Works

With regular TLS, only the server presents a certificate to the client.

With mTLS, the verification is mutual:

  1. The client service presents a client certificate (with SAN/pod name).
  2. The server service verifies the client certificate (trusted CA, not expired, correct subject).
  3. The server also presents its own certificate.
  4. The connection is encrypted, and both sides know exactly who is on the other end.

mTLS in a Service Mesh (e.g., Istio)

  • A sidecar proxy (Envoy) automatically handles certificates (via Citadel/SPIRE).
  • Policies in AuthorizationPolicy / PeerAuthentication define rules like: > "OrderService is allowed to call PaymentService, but not the other way around."

✅ Advantages

  • Zero trust: You do not trust the network; every piece of traffic is authenticated
  • Automatic encryption inside the cluster
  • Observability: Easy to audit which service talked to which
  • Policy enforcement: Mesh-level RBAC based on certificates/identities

❌ Disadvantages

  • PKI complexity: Certificate rotation, CA management, revocation
  • Config sensitivity: One bad policy can break all traffic
  • Overhead: TLS handshakes on connections (though modern hardware handles this well)
  • Not about users: mTLS identifies services, not users — user permissions still need JWT/OAuth2/etc.

When to Use

  • Service-to-service communication inside a cluster
  • Zero-trust, enterprise environments, regulated industries
  • Always together with a service mesh

2026 status: The standard for production microservices running in a mesh, often as part of the “golden combo”:

API Gateway + OAuth2/JWT + mTLS.

High-level comparison of common authentication and authorization methods in microservices

🔄 Authorization Flows: Client → Gateway → Microservices

End-to-end OAuth2/OIDC + JWT authorization flow through API Gateway to microservices

1️⃣ Client → IdP → API Gateway → Microservices (OAuth2/OIDC + JWT)

A typical modern setup: browser/SPA/mobile client → API Gateway/BFF → dozens of microservices.

🔐 Authentication at IdP (SSO)

  1. The client opens the frontend, which redirects the user to the IdP using Authorization Code Flow (OIDC).
  2. The user logs in (password, MFA, etc.).
  3. The IdP creates a session and returns an authorization code to the client.
  4. The client exchanges this code for an access token (often JWT) and a refresh token.

📥 Client → API Gateway

  • The client sends requests to the API Gateway with: Authorization: Bearer <access_token>
  • The gateway validates:
    • JWT signature
    • exp, iss, aud
    • scopes / roles using the IdP’s JWKS endpoint.

📡 Gateway → Microservices

The gateway can either:

  1. Proxy the original JWT downstream as-is, or
  2. Issue a lightweight internal token for services (token exchange to an internal JWT).

Each microservice then either:

  • Validates the token itself; or
  • Trusts the gateway’s validation and uses the provided claims for authorization.

🧩 Authorization Inside Microservices

  • Basic (RBAC): Check roles/scopes from the token (admin, user, order.read, order.write, etc.).
  • Advanced (ABAC / policy-based):
    • Fetch additional data from other services (fetch/replicate patterns).
    • Delegate the decision to a centralized Authorization service.

2️⃣ Service-to-Service (S2S) Authorization

Pattern 1: Propagation (User Token Forwarding)

  • Service A receives a request with the user’s JWT.
  • When calling Service B, A forwards the same JWT.
  • Service B validates the token and checks the user’s permissions.

Use when you need end-to-end user identity across services.


Pattern 2: Service Account / Client Credentials

  • Each service gets its own client_id / client_secret / certificate.
  • A service uses Client Credentials Flow to obtain a machine-to-machine token.
  • It then calls other services on its own behalf, not the user’s.

Use when:

  • There is no end-user (batch jobs, schedulers), or
  • You need service-level permissions (e.g. “BillingService can call PaymentService”).

Pattern 3: mTLS in the Mesh

  • Inside the mesh, each pod/SAN is identified by a certificate.
  • Mesh policies (AuthorizationPolicy, etc.) define which service can call which.

User identity can:

  • Be propagated in JWT alongside mTLS, or
  • Be omitted completely for batch/technical processes where no end-user is involved.

⚙️ Resilience: How Painful Are Failures?

Let’s look at failure points for each approach. Key chain links:

  • IdP/Auth service
  • API Gateway
  • Session store / PKI / mesh control plane
  • Individual microservice

1️⃣ OAuth2/OIDC + JWT

IdP/Auth service down:

  • New logins and refresh token flows stop working
  • Existing access tokens continue to be accepted until they expire
  • Pain is delayed — the system degrades as tokens expire

API Gateway down:

  • External clients almost always lose access completely
  • Pain is acute, so the gateway must be scaled and protected aggressively

Single microservice down:

  • Only that service’s functionality breaks
  • The authorization mechanism as a whole still works

2️⃣ JWT Without a Central IdP

  • If each service validates JWT locally using cached keys:
    • Auth service outage only affects new logins / key rotation
  • If services call Auth service on every validation (fetch strategy):
    • Auth service outage makes all requests unauthenticated/unauthorized

2026 recommendation: avoid hard runtime dependency on the Auth service; always validate JWT locally.


3️⃣ Sessions

Session store (Redis, SQL) down:

  • All sessions become unavailable → users are massively logged out
  • Pain is usually maximal, since sessions are central shared state

Auth service that creates sessions down:

  • New logins are impossible
  • Existing sessions keep working as long as the session store is alive

4️⃣ API Keys

Key management service down:

  • You can’t issue/revoke keys
  • Existing keys continue to work

Gateway down:

  • Same as other schemes: very painful, as it’s usually the single entry point

5️⃣ mTLS

Control plane / PKI / mesh down:

  • Existing connections may work for a while
  • New certificates aren’t issued, old ones aren’t rotated
  • As certificates expire, more and more S2S calls start failing with TLS errors

Mesh policy misconfig / incompatible mTLS config:

  • Can instantly break a large portion of internal traffic ### 6️⃣ Resilience Summary Table
Approach Central Component Failure (IdP/Auth/Session/PKI) Pain Level
OAuth2 + JWT No new logins/refresh, existing tokens work until exp Delayed, manageable
JWT (local validation) No new tokens issued, existing tokens keep working Delayed
JWT (fetch to Auth) Auth service unavailable → all validations fail Acute, often critical
Sessions Session store down → mass logout Very painful
API Keys Cannot manage keys, but existing keys still work Usually moderate
mTLS PKI/mesh down → gradual degradation of S2S traffic as certs expire Growing, depends on DevOps quality

🧩 Authentication & Authorization Patterns in Microservices

Patterns are proven architectural solutions to recurring authorization problems. They help deal with distributed data, scale, and complexity.

As of 2026, the focus is on combining:

  • OAuth2/JWT for users
  • mTLS for service-to-service traffic
  • Delegated authorization for complex permissions

1️⃣ API Gateway / BFF as the Authentication Entry Point

Description:

An API Gateway (or Backend for Frontend, BFF) is the single entry point where:

  • Authentication happens
  • Coarse-grained authorization is enforced (basic checks: roles, scopes)

The token is then forwarded to microservices.

How it works:

  1. Client → Gateway (with credentials or an existing token)
  2. The gateway:
    • Validates the token
    • Checks roles/scopes
    • Enriches the request with user context (headers/claims)
  3. Gateway → Microservices (with token or extracted claims)
  4. Microservices perform fine-grained authorization using their own data

Pros:

  • Single, well-defined entry point for external clients
  • Per-tenant isolation, rate limiting, traffic management
  • Minimizes duplicated auth logic across services

Cons:

  • Gateway becomes a single point of failure → must be highly available
  • Potential tight coupling if services depend heavily on the gateway’s claim format

Examples:

  • Ocelot / YARP (for ASP.NET)
  • Kong, KrakenD, NGINX, Traefik

2️⃣ Token Propagation

Description:

The user’s token (JWT) is passed from the client through the gateway to every microservice in the call chain. Each service validates the token and enforces its own access rules.

Variants:

  • End-user propagation:

    The original user JWT is propagated end-to-end through all services.

  • Service token exchange:

    The gateway exchanges the user JWT for a service token, which is then used internally.

Pros:

  • End-to-end user identity available in every microservice
  • Each service can independently enforce its own authorization rules (RBAC/ABAC)

Cons:

  • Token is validated in every service (CPU overhead)
  • Large tokens (many claims) increase request size across the entire call chain

When to use:

  • User-centric flows where you need full user context in downstream services
  • Scenarios requiring per-resource checks (RBAC/ABAC) based on user identity and attributes

3️⃣ Service Account / Client Credentials

Description:

For service-to-service (S2S) calls, each service has its own client_id / client_secret and obtains a token via the Client Credentials Flow.

How it works:

  1. Service A → IdP with client_id / client_secret → receives a service JWT
  2. Service A → Service B using this token in the Authorization header
  3. Service B validates the token and checks the permissions of Service A (not the end-user)

Pros:

  • Permissions are assigned to services, not users
  • Well-suited for batch jobs, background workers, daemons

Cons:

  • No end-user context (often needs to be combined with token propagation)
  • Storing secrets in services introduces secrets management risk

When to use:

  • Background jobs, schedulers, cron
  • Integrations between services where no end-user is directly involved

4️⃣ Authorization Patterns with Distributed Data (4 Strategies)

From Chris Richardson (microservices.io): used for fine-grained authorization when permissions are spread across multiple services.

Strategy Description Pros Cons
Provide All required claims (roles, tenant, relationships) are embedded into the JWT by IdP Fast, fully stateless JWT becomes huge, revocation is hard
Fetch Service fetches permissions from other services on each request (REST/gRPC) Always up-to-date data Latency (N+1 calls), coupling to others
Replicate Service keeps a local replica of foreign data (events/CQRS-based sync) Fast local checks Eventual consistency, complex replication
Delegate Authorization is delegated to an external Authorization Service (OPA, Cerbos, Oso) Centralized policies, handles complex logic Dependency on AuthZ service, added latency

Delegate example:

A microservice calls the AuthZ service:

isAllowed(user=alice, action=delete, resource=order123) → YES/NO
The AuthZ service returns yes or no, and the microservice enforces the result.

5️⃣ Edge vs Service-Level Authorization
Description:
Two-layer authorization model:

  • Edge (Gateway): Coarse-grained checks: roles, scopes, rate limiting, tenant isolation
  • Service Level:Fine-grained checks: concrete permissions on specific resources

Pros:

  • Defense in depth — multiple layers of protection
  • The gateway filters out most invalid/unauthorized requests early

Cons:

  • Duplication of checks between gateway and services

When to use:

  • For all public APIs, Internet-facing systems, and multi-tenant platforms

6️⃣ Centralized Authorization Service
Description:
A separate service stores policies (e.g. Rego/OPA, Cedar/Cerbos) and answers questions like: allow?(user, action, resource) → true/false

Pros:

  • Can handle complex logic (ABAC, ReBAC, tenant isolation) in one place
  • Supports auditing, A/B testing of policies, GitOps workflows

Cons:

  • Adds latency to each authorization decision
  • Another single point of failure if not deployed in HA

When to use:

  • Enterprise systems with thousands of rules and users
  • Regulated industries where auditability and consistency of policies are critical

🔁 Delegate Pattern (Authorization Delegation) + OPA

Delegate Pattern (Authorization Delegation) + OPA

Theory: The Distributed Permissions Problem

In a microservices architecture, permissions for a single resource are often spread across multiple services. For example, user Alice might be allowed to:

  • ✅ Read her own orders (Order Service)
  • ✅ Create payments for her own orders (Payment Service)
  • ❌ Modify other users’ orders (Order Service)
  • ✅ As a manager, see her team’s orders (User Service + Order Service combined)

A JWT can carry only static claims (roles, groups, basic attributes) — it does not know about:

  • Concrete relationships (owner of this order, member of this team)
  • Dynamic state (order status, project membership, org structure changes)

Solution: The microservice delegates the authorization decision to an external Authorization Service that knows all the rules and data needed.


How the Delegate Pattern Works

Flow:

  1. Client → Order Service

DELETE /orders/123
Authorization: Bearer

  1. Order Service extracts key claims from the JWT: { "userId": "alice", "roles": ["user"], "tenant": "acme" } 3.Order Service → AuthZ Service (delegated check): POST /authorize Content-Type: application/json

{
"principal": { "id": "alice", "roles": ["user"] },
"action": "delete",
"resource": { "type": "order", "id": "123" },
"context": { "tenant": "acme" }
}

  1. AuthZ Service → response: { "allow": true, "reason": "user is owner of order" } or { "allow": false, "reason": "user is not owner and not manager" }
  2. Order Service either performs or rejects the operation based on the AuthZ decision.

OPA (Open Policy Agent) as a Delegate Implementation

OPA is a Policy-as-Code engine that evaluates authorization policies written in the Rego language.

✅ Benefits of Delegate + OPA

  • Complex logic: Handles ABAC, ReBAC, tenant isolation, workflow-dependent rules
  • Centralized policies: One Rego policy set used by many services
  • Auditability: You can log every authorization decision
  • A/B testing of policies: Roll out policy changes without redeploying services
  • GitOps-friendly: Policies live in Git and are deployed via CI/CD
  • Polyglot support: OPA can be used from Go, Java, .NET, Node and more

❌ Drawbacks

  • Latency: Adds ~50–200 ms per authorization call over the network
  • Single point of failure: OPA must run in a highly available setup
  • Complexity: Rego is a new language the team has to learn
  • Data synchronization: Business data must be synced or exposed to OPA for decisions

🔁 Alternatives to OPA

  • Cerbos — managed/embedded policy engine, often simpler to integrate
  • OpenFGA (Google Zanzibar model) — ReBAC (relationships: user → document → viewer)
  • Permify — Zanzibar-style auth on PostgreSQL
  • Oso — policy language and engine with Python/Rust SDKs

🧭 When to Use the Delegate Pattern

Use Delegate + Policy-as-Code when:

  • ✅ You have 10+ microservices with non-trivial permissions
  • ✅ You are building a multi-tenant platform
  • ✅ You work in regulated industries (fintech, healthcare, gov)
  • ✅ You need strong audit/compliance guarantees

Avoid it when:

  • ❌ You have simple CRUD apps with basic role-based access only

2026 status: Policy-as-Code (OPA/Cerbos) is considered an enterprise standard, especially in environments using GitOps and service meshes.

7️⃣ 2026 “Golden Standard”: Layered Approach

High-Level Architecture

External Clients

API Gateway / BFF
(OAuth2/OIDC + JWT validation, coarse-grained auth)

Microservices
(token propagation or service tokens)

Microservice ↔ Microservice
(mTLS in a service mesh + fine-grained auth)
External clients → API Gateway

Gateway handles OAuth2/OIDC, validates JWTs, and enforces coarse-grained authorization (roles, scopes, tenant).

Gateway → Microservices

Uses token propagation (forwarding user JWT) or service tokens (Client Credentials) for internal calls.

Microservice ↔ Microservice

Runs over mTLS inside a service mesh, with fine-grained authorization:

Local RBAC/ABAC in each service, or

Delegated checks via a centralized AuthZ service (OPA/Cerbos/etc.).

✅ Pros

  • Covers all major scenarios (user-facing, S2S, batch, partners)
  • Enables zero-trust networking inside the cluster
  • Scales well horizontally with stateless tokens and mesh-based security

❌ Cons

  • Increased infrastructure complexity
  • Requires a mature DevOps/SRE culture and good observability This is effectively what companies like Netflix, Uber, DoorDash and other large-scale players run in production today.

💻 Minimal C# Example (ASP.NET Core + JWT)
Below is a minimal JWT authentication setup for a microservice built with ASP.NET Core 8/10 Web API (acting as an OAuth2 resource server behind a gateway).

Minimal JWT Setup in ASP.NET Core (8/10)

1️⃣ Program.cs — JWT Bearer Configuration

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        // URL of your IdP (e.g. https://idp.example.com)
        options.Authority = configuration["Jwt:Authority"];
        // Audience = this API's identifier (aud claim)
        options.Audience = configuration["Jwt:Audience"];

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,

            // If you are not using Authority/JWKS and rely on a symmetric key:
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(configuration["Jwt:Key"]!))
        };
    });

builder.Services.AddAuthorization();
builder.Services.AddControllers();

var app = builder.Build();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
Enter fullscreen mode Exit fullscreen mode

2️⃣ Controller with Role-Based Authorization

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    // Any authenticated user
    [HttpGet]
    [Authorize]
    public IActionResult GetMyOrders()
    {
        var userId = User.FindFirst("sub")?.Value;
        // Load orders by userId...
        return Ok(/* orders */);
    }

    // Only users with the "admin" role
    [HttpDelete("{id:int}")]
    [Authorize(Roles = "admin")]
    public IActionResult DeleteOrder(int id)
    {
        // Delete order...
        return NoContent();
    }
}
Enter fullscreen mode Exit fullscreen mode

In a real production system, these APIs would typically sit behind an API Gateway, which:

Validates tokens at the edge

Optionally enriches requests with user context

Forwards the token (or an internal token) to microservices

The tokens themselves are issued by an IdP such as Duende IdentityServer, Auth0, Azure AD B2C, or Keycloak.

🎯 Which Approach to Use When (Practical Guidance)

Decision Table for 2026

Scenario Recommended Approach
SPA / mobile client + many microservices OAuth2/OIDC + JWT via IdP and API Gateway
Public REST API for external clients OAuth2 (auth code / client credentials) + JWT or opaque tokens
Internal service-to-service calls mTLS in a service mesh + either user JWT propagation or service tokens
Small web app / semi-monolith Sessions (cookies + session store), or JWT if microservices migration is planned
Simple batch / CLI integrations API Keys or OAuth2 Client Credentials
Fintech, government, critical infrastructure Combo: OIDC+JWT for users, mTLS for services, short-lived tokens, MFA, audit logging
  • For modern microservices with external clients, your default choice should be:

    IdP + OAuth2/OIDC + JWT + API Gateway, with mTLS and short-lived tokens inside the cluster.

  • Keep sessions for simple / legacy web apps and internal admin panels, where microservices aren’t really needed yet.

  • Use API keys sparingly, for those services and scripts where a full OAuth2 setup would be overkill.

When designing your solution, consider not only security, but also resilience:

how painful will it be if IdP, gateway, session store, or PKI/mesh go down?

In 2026, this is just as important a design criterion as developer convenience.

Top comments (1)

Collapse
 
sohan26 profile image
Sohan

Also check out SpiceDB for centralized authorization using ReBAC. It's open source and has an official .net library github.com/authzed/authzed-dotnet