Security is no longer an afterthought — it’s the foundation. In 2025, with APIs powering everything from mobile apps to AI agents, properly implementing OAuth2 and OpenID Connect (OIDC) in your ASP.NET Core APIs is essential. This guide helps you get it right.
Understanding the Difference
- OAuth2: A protocol for authorization (who can access what).
- OpenID Connect (OIDC): An identity layer on top of OAuth2 (who is the user).
They work together: OAuth2 handles permissions; OIDC authenticates the user.
Setup: Required Tools & Libraries
To get started, you'll need:
-
.NET 6+
or.NET 10
(recommended) Microsoft.AspNetCore.Authentication.JwtBearer
- An Identity Provider (IdP), e.g.,:Azure AD, Auth0, IdentityServer (self-hosted)
Install with:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Step 1: Configure JWT Authentication
In Program.cs
:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://your-idp.com/";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
Don’t forget to enable authorization middleware:
app.UseAuthentication();
app.UseAuthorization();
Step 2: Protect Your API Endpoints
Use the [Authorize] attribute to protect specific routes:
[Authorize]
[HttpGet("secure-data")]
public IActionResult GetSecureData()
{
return Ok("You are authenticated!");
}
For role-based access:
[Authorize(Roles = "Admin")]
Or policies:
[Authorize(Policy = "CanAccessReports")]
Step 3: Add Scopes and Policies
In Program.cs
:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CanAccessReports", policy =>
{
policy.RequireClaim("scope", "reports.read");
});
});
Make sure your client includes the reports.read scope when requesting the token.
Step 4: Test with Postman or Swagger
- Use the Authorization Code Flow in OAuth2
- Include the token in API calls:
Authorization: Bearer {access_token}
For testing with Postman:
- Go to “Authorization” tab
- Set type to OAuth 2.0
- Configure token URL, client ID, secret, scopes, etc.
Bonus: Token Introspection (Optional)
For APIs that don’t validate JWT locally (e.g., opaque tokens), use introspection:
.AddOAuth2Introspection("introspection", options =>
{
options.Authority = "https://your-idp.com";
options.ClientId = "api-client";
options.ClientSecret = "secret";
});
Refresh Tokens (via OIDC)
To keep users signed in, clients should:
- Use refresh tokens from the OIDC flow
- Store them securely
- Request new access tokens silently
Never expose refresh tokens in browser-based apps!
When to Use What
Scenario | Use OAuth2 | Use OIDC |
---|---|---|
Machine-to-Machine API | ✅ Yes | ❌ No |
User Authentication | ✅ Yes | ✅ Yes |
SPA or Mobile App | ✅ Yes | ✅ Yes |
Backend-to-Backend | ✅ Yes | ❌ No |
Real-World Providers You Can Use
Provider | Supports OAuth2 + OIDC? | Free Tier? |
---|---|---|
Azure AD | ✅ | ✅ (for dev/test) |
Auth0 | ✅ | ✅ |
Okta | ✅ | ✅ |
IdentityServer | ✅ | Self-hosted |
Final Checklist
✔️ JWT authentication enabled
✔️ Authorization middleware active
✔️ Scopes + roles configured
✔️ Secure token storage on client
✔️ HTTPS enforced on all endpoints
Conclusion
OAuth2 and OpenID Connect are not optional anymore — they're table stakes for modern API security. Whether you’re building public APIs or internal enterprise systems, this setup ensures your users and data stay protected.
Top comments (0)