How to Create Secure REST APIs in .NET Core Using OAuth 2.0 and JWT

  • Post author:Jignesh Darji
  • Reading time:123 mins read
Create Secure REST APIs in .NET Core
Create Secure REST APIs in .NET Core

How to Create Secure REST APIs in .NET Core Using OAuth 2.0 and JWT

Introduction

As organizations increasingly adopt web and mobile applications, security has become a critical concern. With APIs serving as the backbone of most applications, securing these APIs is essential to prevent unauthorized access and data breaches. OAuth 2.0 and JSON Web Tokens (JWT) have emerged as powerful technologies to secure REST APIs by providing robust authentication and authorization mechanisms. In this blog post, we’ll explore how to create secure REST APIs in .NET Core using OAuth 2.0 and JWT.

Our goal is to walk you through the entire process, from understanding the key concepts behind OAuth 2.0 and JWT to implementing them in a real-world .NET Core application. By the end of this guide, you’ll have the knowledge needed to create highly secure APIs, ensuring that your applications are both reliable and protected from unauthorized access.

Why Secure REST APIs?

Before diving into the implementation, it’s essential to understand why securing REST APIs is crucial:

  1. Prevent Unauthorized Access: APIs are often exposed to the internet, making them vulnerable to attacks. Securing your API ensures only authorized users can access sensitive data and functionality.
  2. Ensure Data Integrity: Unauthorized users or systems might attempt to tamper with the data being exchanged. Secure APIs help maintain data integrity by verifying that the data has not been altered.
  3. Comply with Industry Standards: Various industries such as healthcare and finance require secure communication channels to meet regulatory requirements like GDPR, HIPAA, and PCI DSS.
  4. Protect User Privacy: APIs often manage user information, and it’s critical to protect this data from being intercepted by malicious actors.

What is OAuth 2.0?

OAuth 2.0 is an open-standard authorization framework designed to grant third-party applications limited access to a user’s resources without exposing their credentials. Unlike traditional login mechanisms, OAuth 2.0 allows a user to authenticate via an authorization server (such as Google, Facebook, or a custom identity provider), which then provides access tokens for communication with the API.

Key Concepts in OAuth 2.0:

  • Resource Owner: The user who owns the data that third-party applications want to access.
  • Client: The third-party application requesting access to the resource owner’s data.
  • Resource Server: The API server that provides access to the protected resources.
  • Authorization Server: The server that issues access tokens after successfully authenticating and authorizing the client.

What is JWT (JSON Web Token)?

JWT (JSON Web Token) is an open-standard token format used to securely transmit information between two parties as a JSON object. The token is digitally signed, which means the recipient can verify the token’s authenticity and integrity.

Key Concepts in JWT:

  • Header: Contains metadata about the token, including the signing algorithm.
  • Payload: Contains the actual data (claims) that is being transmitted, such as the user ID or role.
  • Signature: A hashed value created by combining the header, payload, and a secret key. This ensures the token has not been tampered with.

Benefits of Using OAuth 2.0 and JWT for Securing APIs

  1. Stateless Authentication: JWT is stateless, meaning the token itself contains all the necessary information for authentication, reducing the need for server-side sessions.
  2. Scalability: OAuth 2.0 and JWT make it easier to scale applications, as the tokens can be validated without requiring persistent storage on the server.
  3. Decoupled Authentication: OAuth 2.0 allows you to decouple authentication from your API by relying on external authorization servers.
  4. Cross-Platform Support: JWTs are widely supported across various platforms, making it easier to integrate with different clients, including web, mobile, and IoT devices.

Setting Up .NET Core for Secure REST APIs

Let’s now dive into the practical implementation of secure REST APIs in .NET Core using OAuth 2.0 and JWT. We’ll create a simple API that uses JWT for authentication and OAuth 2.0 for authorization.

Prerequisites

To follow along with this tutorial, ensure that you have the following installed:

  1. .NET Core SDK (version 6.0 or higher)
  2. Visual Studio or Visual Studio Code
  3. Postman or any other API testing tool

Step 1: Create a New .NET Core API Project

First, we’ll create a new .NET Core Web API project.

  1. Open Visual Studio and create a new project.
  2. Select ASP.NET Core Web API from the template list and click Next.
  3. Name your project and select .NET 6.0 (or higher) as the target framework.

Once your project is created, you should see a default API setup with a WeatherForecastController. You can delete this controller as we won’t be using it.

Step 2: Install Required NuGet Packages

To implement OAuth 2.0 and JWT, we’ll need the following NuGet packages:

  1. Microsoft.AspNetCore.Authentication.JwtBearer
  2. Microsoft.AspNetCore.Authorization

You can install them by running the following commands in the Package Manager Console:

Bash
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package Microsoft.AspNetCore.Authorization

These packages will allow us to handle JWT authentication and implement authorization policies in our API.

Step 3: Configure JWT Authentication in Startup.cs

Now, let’s configure JWT-based authentication in the Startup.cs file.

  1. Add Authentication and JWT Bearer Configuration

In the ConfigureServices method of Startup.cs, add the following code to set up JWT authentication:

C#
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // Add Authentication services
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });
}

This configuration enables JWT authentication for the API and specifies the token validation parameters. Ensure that you add the JWT settings to the appsettings.json file as follows:

JSON
"Jwt": {
  "Key": "SuperSecretKey12345", 
  "Issuer": "YourIssuer",
  "Audience": "YourAudience"
}

Step 4: Create a Token Generation Endpoint

Next, we’ll create an endpoint that generates a JWT token for authenticated users. Create a new controller called AuthController.

C#
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private IConfiguration _config;

    public AuthController(IConfiguration config)
    {
        _config = config;
    }

    [HttpPost("login")]
    public IActionResult Login([FromBody] UserLogin userLogin)
    {
        var user = Authenticate(userLogin);

        if (user != null)
        {
            var token = GenerateToken(user);
            return Ok(new { token });
        }

        return Unauthorized();
    }

    private string GenerateToken(UserModel user)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Username),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim("role", user.Role),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(30),
            signingCredentials: credentials);

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    private UserModel Authenticate(UserLogin login)
    {
        // In a real application, you would validate the user's credentials against a database.
        if (login.Username == "testuser" && login.Password == "password")
        {
            return new UserModel { Username = "testuser", Email = "[email protected]", Role = "Admin" };
        }

        return null;
    }
}

Here’s what’s happening in the AuthController:

  • The Login method authenticates the user by checking the credentials (this is just a mock example; in real applications, you’d query the database).
  • Upon successful authentication, the GenerateToken method creates a JWT token, which includes claims like the username, email, and role.
  • The token is returned to the client.

Step 5: Protect Your API Endpoints

Now that we have authentication in place, let’s protect our API endpoints by applying JWT authentication. Create a new controller, SecureDataController, and use the [Authorize] attribute to secure the API.

C#
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class SecureDataController : ControllerBase
{
    [HttpGet("data")]
    public IActionResult GetSecureData()
    {
        var secureData = new
        {
            Data = "This is secure data.",
            User = User.Identity.Name,
            Claims = User.Claims.Select(c => new { c.Type, c.Value }).ToList()
        };

        return Ok(secureData);
    }
}

Here’s what’s happening:

  • The [Authorize] attribute ensures that only authenticated users with a valid JWT can access this endpoint.
  • The GetSecureData method returns some secure data, along with the user’s identity and claims, to demonstrate that JWT claims can be extracted in the API.

Step 6: Test the API with Postman

  1. Generate a JWT: Use the /api/auth/login endpoint to generate a JWT token by sending the following request:
  • URL: https://localhost:5001/api/auth/login
  • Method: POST
  • Body:
JSON
{
  "username": "testuser",
  "password": "password"
}

You should receive a token in the response.

  1. Access a Secure Endpoint: Use the token to access the secure API endpoint. In Postman, add the token to the Authorization header as a Bearer Token.
  • URL: https://localhost:5001/api/securedata/data
  • Method: GET
  • Authorization: Bearer your-jwt-token-here

If the token is valid, you should receive the secure data response.

Step 7: Implement OAuth 2.0 Authorization Flow

Now that we have JWT authentication in place, let’s integrate OAuth 2.0 for authorization. In a typical OAuth 2.0 flow, users authenticate with an external provider (such as Google or Azure AD), which issues a token for accessing resources.

To implement OAuth 2.0 in your .NET Core API:

  1. Register with an Identity Provider: Register your application with an identity provider like Google, Microsoft, or Auth0. These providers will manage user authentication and token generation.
  2. Add OAuth 2.0 Configuration: Configure OAuth 2.0 authentication in your Startup.cs file. For example, to use Google OAuth:
C#
services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogle(options =>
{
    options.ClientId = Configuration["Google:ClientId"];
    options.ClientSecret = Configuration["Google:ClientSecret"];
});
  1. Use OAuth Tokens: After a successful OAuth authentication, the identity provider will issue a token that you can use to authenticate with your API.

Conclusion

Securing your REST APIs in .NET Core using OAuth 2.0 and JWT is an essential step in modern application development. OAuth 2.0 provides a flexible and secure way to delegate access, while JWT ensures stateless, scalable, and secure communication between clients and servers.

By following this guide, you now have the knowledge and tools to create secure REST APIs in .NET Core using OAuth 2.0 and JWT, ensuring your application remains protected from unauthorized access while providing a seamless experience for your users.