Securing APIs with OAuth2 and OpenID Connect in .NET

OAuth2 handles delegated access; OpenID Connect layers identity on top. Together they power Google Sign-In, Microsoft 365, and every “Login with X” button. Here’s how to wire them into .NET like a pro.

Real-Life Analogy: Airport Boarding Pass + Passport

A boarding pass (OAuth2 access token) proves the airline authorised you onto flight 123. A passport (OIDC ID token) proves who you are to immigration. Same journey, different checkpoints.

Choosing a Flow

FlowUse CaseNotes
Auth Code + PKCESPAs / mobileNo client secret in browser.
Client CredentialsMachine-to-machineService calls backend.
Device CodeSmart-TV / CLIEnter code on phone.

Adding Authentication

builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer("Bearer", o =>
    {
        o.Authority = "https://login.microsoftonline.com/{tenantId}/v2.0";
        o.TokenValidationParameters.ValidAudience = "api://my-api-id";
    });

Adding Authorization Policies

services.AddAuthorization(o =>
{
    o.AddPolicy("CanWriteOrders", p =>
        p.RequireClaim("scope","orders.write"));
});

Protecting Endpoints

[Authorize(Policy = "CanWriteOrders")]
[HttpPost("orders")]
public IActionResult Create(OrderDto dto) { ... }

Exchanging Auth Code for Tokens

(SPA with MSAL)

const msal = new PublicClientApplication(cfg);
await msal.loginPopup({ scopes: ["api://my-api-id/orders.read"] });

Refreshing Access Tokens

Most OIDC providers return a refresh_token for confidential clients. SPAs use silent refresh with hidden iframe or token-renew endpoint.

Best-Practice Checklist

  • Always use PKCE even for confidential clients—prevents auth-code interception.
  • Validate nonce on ID tokens.
  • Never treat an access token as identity; use ID token or /userinfo.
  • Rotate client secrets annually.

Final Thoughts

OAuth2 grants permission; OpenID Connect proves identity. Master the flows, keep scopes tight, and your API will admit only ticketed, passport-holding travellers.