DEV Community

Cover image for Securing APIs with OAuth2 and JWT in .NET Core
Paulo Torres
Paulo Torres

Posted on

Securing APIs with OAuth2 and JWT in .NET Core

In today's digital ecosystem, securing APIs is more critical than ever. As applications become increasingly interconnected, ensuring that your APIs are protected against unauthorized access is paramount. In this article, we'll explore how to secure APIs using OAuth2 and JSON Web Tokens (JWT) in .NET Core, providing a step-by-step guide along with real-world examples.

Understanding OAuth2 and JWT

OAuth2 is an industry-standard protocol for authorization that enables third-party applications to access user data without exposing credentials. It provides a secure way to delegate access, allowing users to grant limited permissions to apps.

JSON Web Tokens (JWT) are a compact and self-contained way for securely transmitting information between parties as a JSON object. They're widely used for authentication and authorization in web applications due to their efficiency and security.

Why Use OAuth2 and JWT in .NET Core?

  • Scalability: Stateless authentication with JWT reduces server overhead.
  • Security: Robust encryption and signature validation protect against tampering.
  • Interoperability: JWTs are language-agnostic and can be used across different platforms.
  • Performance: Reduced need for database lookups enhances API responsiveness.

Setting Up Your .NET Core API

Step 1: Create a New Web API Project

dotnet new webapi -n SecureApiExample
cd SecureApiExample
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Necessary NuGet Packages

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Enter fullscreen mode Exit fullscreen mode

Implementing OAuth2 and JWT Authentication

Step 1: Configure Authentication Services

In your Program.cs or Startup.cs (depending on your .NET Core version), configure the authentication services.

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

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Configure JWT Authentication
var key = Encoding.ASCII.GetBytes("YourSuperSecretKey");
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false; // Should be true in production
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false, // Set to true in production
        ValidateAudience = false, // Set to true in production
        ClockSkew = TimeSpan.Zero
    };
});

var app = builder.Build();

// Enable authentication middleware
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

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

Step 2: Create a Token Generator

Create a controller AuthController to handle user login and token generation.

using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] UserCredentials credentials)
    {
        // Validate the user credentials (this is just a demo, use a user store in production)
        if (credentials.Username != "user" || credentials.Password != "password")
            return Unauthorized();

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes("YourSuperSecretKey");
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.Name, credentials.Username),
                new Claim(ClaimTypes.Role, "Admin")
            }),
            Expires = DateTime.UtcNow.AddHours(1),
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);

        return Ok(new { token = jwtToken });
    }
}

public class UserCredentials
{
    public string Username { get; set; }
    public string Password { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Secure Your API Endpoints

Apply the [Authorize] attribute to secure your controllers or actions.

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

[Authorize]
[ApiController]
[Route("[controller]")]
public class ValuesController : ControllerBase
{
    [HttpGet]
    public IActionResult GetValues()
    {
        return Ok(new string[] { "Value1", "Value2" });
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Test Your Secured API

Obtain a JWT Token

Use Postman or a similar tool to send a POST request to /auth/login with the following JSON body:

{
    "username": "user",
    "password": "password"
}
Enter fullscreen mode Exit fullscreen mode

Access Protected Endpoint

With the token received, set the Authorization header in your GET request to /values:

Authorization: Bearer your_jwt_token_here
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Securing a Multi-Tier Application

Imagine a financial application where sensitive customer data is accessed via APIs. By implementing OAuth2 and JWT:

  • Clients: Mobile apps and web clients authenticate once and receive a token.
  • API Gateway: Validates JWTs before routing requests to microservices.
  • Microservices: Each service verifies the token and checks user roles and permissions embedded within.

Best Practices

  • Use HTTPS: Always encrypt communication to prevent token interception.
  • Keep Tokens Short-Lived: Reduce risk by limiting token lifespan.
  • Store Secrets Securely: Use environment variables or Azure Key Vault.
  • Validate Token Claims: Always check issuer, audience, and expiration.
  • Implement Refresh Tokens: Allow users to renew tokens without re-authenticating.

Conclusion

Securing your APIs with OAuth2 and JWT in .NET Core is a robust solution for modern applications. It not only enhances security but also streamlines authentication and authorization processes. By following the steps and best practices outlined above, you can protect your APIs from unauthorized access while providing a seamless experience for legitimate users.

Top comments (0)