Authentication and Authorization

Chapter 4: Authentication and Authorization

4.1 Introduction to Authentication and Authorization
Authentication and authorization are essential components of building secure APIs. Authentication verifies the identity of a user or client, while authorization determines their access rights to specific resources or actions within the API.

4.2 JWT Authentication in ASP.NET Core
JWT (JSON Web Token) authentication is a widely used mechanism for securing APIs. It involves the use of digitally signed tokens to authenticate and authorize users. In this chapter, we will explore a practical code example of implementing JWT authentication with roles, expiry, and cookies in an ASP.NET Core API.

4.2.1 Installing Required Packages
Before we begin, ensure that you have the necessary NuGet packages installed. Open the NuGet Package Manager Console and execute the following command:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

4.2.2 Configuring JWT Authentication
Configure JWT authentication in the Startup.cs file. Open the file and modify the ConfigureServices method as follows:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = "your-issuer",
        ValidAudience = "your-audience",
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
    };
});

Make sure to replace "your-issuer", "your-audience", and "your-secret-key" with your own values. This configuration sets up JWT authentication and defines the validation parameters for the JWT token.

4.2.3 Generating a JWT Token with Roles and Expiry
To generate a JWT token with roles and an expiry date, you can modify the GenerateJwtToken method as follows:

private string GenerateJwtToken(string username, List<string> roles)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, username),
        new Claim(ClaimTypes.Role, string.Join(",", roles)),
        // Add additional claims as needed
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"));
    var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    var token = new JwtSecurityToken(
        issuer: "your-issuer",
        audience: "your-audience",
        claims: claims,
        expires: DateTime.UtcNow.AddHours(1),
        signingCredentials: credentials
    );

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

In this example, the GenerateJwtToken method accepts a list of roles as a parameter. The roles are added as claims using the ClaimTypes.Role claim type. Additionally, the token is set to expire after one hour (you can adjust the expiry time as per your requirements).

4.2.4 Creating a JWT Token and Storing it in a Cookie
To generate a JWT token and store it in a cookie upon successful authentication, you can modify the authentication logic as follows:

[HttpPost("login")]
public IActionResult Login(LoginViewModel model)
{
    // Authenticate the user
    var user = AuthenticateUser(model.Username, model.Password);
    if (user != null)
    {
        // Generate JWT token
        var token = GenerateJwtToken(user.Username, user.Roles);

        // Create a cookie and store the token
        var cookie

Options = new CookieOptions
        {
            HttpOnly = true,
            Secure = true,
            SameSite = SameSiteMode.Strict,
            Expires = DateTime.UtcNow.AddHours(1) // Set the expiry time of the cookie
        };
        HttpContext.Response.Cookies.Append("jwtToken", token, cookieOptions);

        return Ok(new { token });
    }

    return Unauthorized();
}

In this example, after authenticating the user, the GenerateJwtToken method is called to generate a JWT token. The token is then stored in a cookie named “jwtToken” using the HttpContext.Response.Cookies.Append method. The cookie is set to be HTTP-only, secure, and have a strict same-site policy. The expiry time of the cookie is also set to one hour, matching the token expiry.

In this example, the AuthenticateUser method accepts the username and password as parameters. It retrieves user information from a data source, such as a database. In this case, we are using a mock implementation with a list of User objects.

private User AuthenticateUser(string username, string password)
{
    // Retrieve user information from the database or any other data source
    // Here, we will use a mock implementation for demonstration purposes

    var users = new List<User>
    {
        new User { Id = 1, Username = "john", Password = "password123", Roles = new List<string> { "admin" } },
        new User { Id = 2, Username = "jane", Password = "password456", Roles = new List<string> { "user" } }
    };

    // Find the user with the matching username and password
    var user = users.FirstOrDefault(u => u.Username == username && u.Password == password);

    return user;
}

The method then searches for a user with the matching username and password. If a match is found, the corresponding User object is returned. Otherwise, null is returned, indicating that the authentication failed.

4.2.5 Protecting Endpoints with Role-Based Authorization and JWT Authentication
To protect your API endpoints based on user roles, you can use the [Authorize(Roles = "role")] attribute. Here’s an example of how to secure an endpoint with role-based authorization:

[Authorize(Roles = "admin")]
[HttpGet("adminOnly")]
public IActionResult AdminOnlyEndpoint()
{
    // Endpoint logic for admin users

    return Ok("This is an admin-only endpoint.");
}

[Authorize(Roles = "admin,user")]
[HttpGet("adminOrUser")]
public IActionResult AdminOrUserEndpoint()
{
    // Endpoint logic for admin and user roles

    return Ok("This endpoint is accessible to both admin and user roles.");
}

In this example, the AdminOnlyEndpoint endpoint is accessible only to users with the “admin” role, while the AdminOrUserEndpoint endpoint is accessible to users with either the “admin” or “user” role. The [Authorize(Roles = "role")] attribute ensures that only users with the specified roles can access the endpoints.

4.3 Real-Life Practical Use of JWT Authentication with Roles and Cookies
In a real-life scenario, after successful authentication, the API generates a JWT token containing the user’s roles. The token is then stored in a cookie. Clients can include this token in the Authorization header of subsequent requests, or the token will be automatically sent with each request via the cookie. This allows the API to authenticate the user and authorize access to specific endpoints based on their assigned roles.

By implementing JWT authentication with roles, expiry, and cookies in your ASP.NET Core API, you can enhance security, control access based on user roles, and provide a seamless authentication experience for your users.

In the next chapter, we will explore error handling and logging strategies in ASP.NET Core API, providing robust error handling mechanisms and logging capabilities.