HMAC Authentication in ASP.NET Core Web API

How to Implement HMAC Authentication in ASP.NET Core Web API

In this article, I will discuss how to Implement HMAC Authentication in an ASP.NET Core Web API Application with an Example. Please read our previous article on how to Store a Password in Hash Format in the ASP.NET Core Web API Application.

What is HMAC Authentication in ASP.NET Core Web API?

HMAC stands for Hash-based Message Authentication Code. From the full form of HMAC, we need to understand two things: one is a Message Authentication Code, and the other is Hash-Based. So, HMAC is a mechanism for creating a Message Authentication Code using a Hash Function.

The most important thing to keep in mind is that while generating the Message Authentication Code using the Hash Function, we need to use a Shared Secret Key. Moreover, that Shared Secret Key must be shared between the Client and the Server involved in sending and receiving the data.

HMAC (Hash-based Message Authentication Code) is a mechanism that provides data integrity and authentication for messages. It involves using a secret key combined with a hash function (such as SHA-256) to produce a unique code for each message. This code can be verified by the recipient to ensure the message has not been altered and is from a trusted source.

How HMAC Authentication Works?

HMAC ensures that the data has not been tampered with during transmission by creating a unique hash for each message based on its content and a secret key. This hash is then sent along with the message. Upon receipt, the receiver can generate their hash from the received message and the secret key. If the received hash matches the generated hash, then it will consider it as a Valid Request. For a better understanding, please have a look at the following diagram:

How HMAC Authentication Works

What are the Components Involved in HMAC Authentication?

HMAC Authentication in ASP.NET Core Web API involves several key components that work together to ensure the integrity and authenticity of messages. The following are the main components involved in HMAC Authentication:

  • Message: The message is the data that needs to be authenticated. In the context of an API, this typically includes parts of the HTTP request such as the HTTP Method, URL, Route Data, Query Parameters, Headers, and Body of the request in case of POST or PUT Request.
  • Secret Key: The secret key is a shared secret between the client and the server. It is used in the HMAC Algorithm to create a hash that is unique to the message. This key must be kept confidential and secure.
  • Hash Function: The hash function is a cryptographic algorithm used to generate the hash. Common hash functions used in HMAC include SHA-256, SHA-512, and MD5.
  • HMAC Algorithm: The HMAC (Hash-based Message Authentication Code) algorithm combines the message and the secret key using the hash function to produce a unique hash code.
  • Timestamp: Messages often include a timestamp to prevent replay attacks. This ensures that the message is valid only for a certain period, making it difficult for attackers to reuse captured messages.
  • Nonce (Optional): A nonce is a unique value added to the message to prevent replay attacks further. It ensures each message has a unique HMAC, even if the other components are the same.
  • Authorization Header: The resulting HMAC is included in the HTTP request’s Authorization header. This header might look like this: Authorization: HMAC <computed-hmac>
Implementing HMAC Authentication in ASP.NET Core Web API

Let us first understand the implementation steps of HMAC Authentication in ASP.NET Core Web API:

  • Create Middleware: Implement a Middleware component to handle the HMAC authentication logic. This middleware will extract the HMAC from the request, reconstruct the message, compute the HMAC, and compare it with the received HMAC.
  • Register Middleware: Register the HMAC middleware Component to the Request Processing Pipeline (within the Main method of the Program class) to ensure it runs on every request.
  • Client-Side HMAC Generation: Ensure that the client applications generate the HMAC using the same method, logic, and secret key and include it in the request headers.
  • Send Secure Requests: The requests that store the HMAC authentication signature should include necessary headers, such as Authorization.

Creating the Server Application:

First, Let us create the server application to validate the HMAC Authentication. Create a new ASP.NET Core Web API application named HMACServerApp.

Managing the Client Secret:

To manage different secret keys for different clients (Mobile, Desktop, Web), we need a strategy to securely store and retrieve these keys based on the client making the request.

  • We need to store the secret keys for each client in a secure storage solution such as a database or a secrets manager. Each client should have a unique identifier (e.g., client ID) that maps to their secret key.
  • When a request is made, extract the client identifier from the request. Retrieve the corresponding secret key from your storage based on the client identifier.

For simplicity, let us use a dictionary to store the client IDs and their corresponding secret keys. Let us assume our services are going to be consumed by three different types of clients: Client1, Client2, and Client3. So, create a class file named ClientSecrets.cs and then copy and paste the following code:

namespace HMACServerApp.Models
{
    public static class ClientSecrets
    {
        public static Dictionary<string, string> Secrets = new Dictionary<string, string>
        {
            { "ClientId1", "SecretKey1" },
            { "ClientId2", "SecretKey2" },
            { "ClientId3", "SecretKey3" },
            // Add more clients as needed
        };
    }
}
Middleware for Validating HMAC Authentication in ASP.NET Core Web API:

Let us create a custom middleware component to extract and validate the HMAC from incoming requests. So, create a class file named HMACAuthenticationMiddleware.cs and copy and paste the following code. The following code is self-explained, so please go through the comment lines for a better understanding.

using Microsoft.Extensions.Caching.Memory;
using System.Security.Cryptography;
using System.Text;

namespace HMACServerApp.Models
{
    // Middleware class for handling HMAC authentication
    public class HMACAuthenticationMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IMemoryCache _memoryCache;

        // Nonce expiry time to prevent reuse of nonces
        private static readonly TimeSpan NonceExpiry = TimeSpan.FromMinutes(5);

        // Timestamp tolerance to allow slight time differences between client and server
        private static readonly TimeSpan TimestampTolerance = TimeSpan.FromMinutes(5);

        // Constructor to initialize the middleware with the next request delegate and memory cache
        public HMACAuthenticationMiddleware(RequestDelegate next, IMemoryCache memoryCache)
        {
            _next = next;
            _memoryCache = memoryCache;
        }

        // Main method to handle each request and perform HMAC validation
        public async Task Invoke(HttpContext context)
        {
            // Check if the Authorization header is present
            if (!context.Request.Headers.TryGetValue("Authorization", out var authHeader))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Authorization header missing");
                return;
            }

            // Check if the Authorization header starts with "HMAC "
            if (!authHeader.ToString().StartsWith("HMAC ", StringComparison.OrdinalIgnoreCase))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid Authorization header");
                return;
            }

            // Extract token parts from the Authorization header
            var tokenParts = authHeader.ToString().Substring("HMAC ".Length).Trim().Split('|');
            if (tokenParts.Length != 4)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid HMAC format");
                return;
            }

            var clientId = tokenParts[0]; // Extract client ID
            var token = tokenParts[1];    // Extract HMAC token
            var nonce = tokenParts[2];    // Extract nonce
            var timestamp = tokenParts[3]; // Extract timestamp

            // Validate the client ID and get the secret key
            if (!ClientSecrets.Secrets.TryGetValue(clientId, out var secretKey))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid client ID");
                return;
            }

            // Validate the timestamp to prevent replay attacks
            if (!DateTimeOffset.TryParse(timestamp, out var requestTime) || Math.Abs((DateTimeOffset.UtcNow - requestTime).TotalMinutes) > TimestampTolerance.TotalMinutes)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid or expired timestamp");
                return;
            }

            // Add the nonce to the cache to prevent reuse
            if (!AddNonce(nonce))
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Nonce already used");
                return;
            }

            // Read the request body for POST and PUT requests
            var requestBody = string.Empty;
            if (context.Request.Method == HttpMethod.Post.Method || context.Request.Method == HttpMethod.Put.Method)
            {
                //The context.Request.EnableBuffering(); method is used to allow the HTTP request body to be read multiple times.
                //In the context of HMAC authentication middleware, it is necessary because the request body needs to be read to compute the HMAC for validation,
                //but it must also be available to any downstream middleware or the actual API controller.
                context.Request.EnableBuffering();

                //Read the request body
                //Using a StreamReader, we can read the entire request body into a string.
                using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true))
                {
                    //This reads the request body stream to the end.
                    requestBody = await reader.ReadToEndAsync();

                    //The statement context.Request.Body.Position = 0; is used to reset the position of the request body stream to the beginning.
                    //This is important because, by default, the request body stream in ASP.NET Core is a forward-only stream,
                    //meaning once you read it, it cannot be read again unless you explicitly reset its position.
                    context.Request.Body.Position = 0;
                }
            }

            // Validate the HMAC token
            var isValid = ValidateToken(token, nonce, timestamp, context.Request, requestBody, secretKey);

            if (!isValid)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid HMAC token");
                return;
            }

            // Call the next middleware in the pipeline
            await _next(context);
        }

        // Method to add a nonce to the cache
        private bool AddNonce(string nonce)
        {
            if (_memoryCache.TryGetValue(nonce, out _))
            {
                return false; // Nonce already used
            }

            var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(NonceExpiry);
            _memoryCache.Set(nonce, true, cacheEntryOptions);
            return true;
        }

        // Method to validate the HMAC token
        private bool ValidateToken(string token, string nonce, string timestamp, HttpRequest request, string requestBody, string secretKey)
        {
            var path = Convert.ToString(request.Path);
            var requestContent = new StringBuilder()
                .Append(request.Method.ToUpper())
                .Append(path.ToUpper())
                .Append(nonce)
                .Append(timestamp);

            // Include the request body for POST and PUT methods
            if (request.Method == HttpMethod.Post.Method || request.Method == HttpMethod.Put.Method)
            {
                requestContent.Append(requestBody);
            }

            var secretBytes = Encoding.UTF8.GetBytes(secretKey);
            var requestBytes = Encoding.UTF8.GetBytes(requestContent.ToString());

            // Compute the HMAC hash
            using var hmac = new HMACSHA256(secretBytes);
            var computedHash = hmac.ComputeHash(requestBytes);
            var computedToken = Convert.ToBase64String(computedHash);
            return token == computedToken;
        }
    }
}
Understanding the HMAC Middleware Components in ASP.NET Core Web API
  • RequestDelegate: This delegate represents the next middleware in the pipeline. It allows the current middleware to call the next middleware after it has completed its own processing.
  • IMemoryCache: This is used to temporarily store nonces in memory to prevent replay attacks. The cache helps keep track of used nonces and their expiry.
  • NonceExpiry: Defines the time duration for which a nonce is considered valid. After this time, the nonce is expired and removed from the cache.
  • TimestampTolerance: Specifies the allowable time difference between the client’s timestamp and the server’s current time to account for slight variations in clock times. This helps in preventing replay attacks.
  • Constructor: Initializes the middleware with the next request delegate and memory cache.
Invoke Method:

This is the core method that gets called for each HTTP request. It performs the following tasks:

  • Validates the presence and format of the Authorization header.
  • Extracts and validates the client ID, HMAC token, nonce, and timestamp.
  • Checks for nonce reuse and timestamp validity.
  • Reads the request body for POST and PUT methods.
  • Validates the HMAC token.
  • Calls the next middleware in the pipeline to see if the HMAC token is valid.

AddNonce Method: This method adds a nonce to the cache to prevent reuse. It returns false if the nonce is already used, indicating a potential replay attack.

ValidateToken Method: Constructs the request content string, including the HTTP method, path, nonce, timestamp, and request body (if applicable). Computes the HMAC hash using the secret key and compares it with the token provided in the request.

Why is EnableBuffering Needed?

When an HTTP request with a body (such as a POST or PUT request) is received, the request body is typically a stream that can only be read once. After reading the body, the stream position is at the end, and subsequent reads will return nothing unless the stream is reset. By calling EnableBuffering(), we enable multiple reads of the request body.

Why Resetting the Stream Position is Necessary?

When we read the request body in our middleware to compute the HMAC for validation, the stream’s position moves to the end of the stream. If the position is not reset, any subsequent attempts to read the stream (for example, by downstream middleware or the controller handling the request) will return no data because the stream has already been read.

Registering Memory Cache and HMAC Middleware Components:

Register the IMemoryCache service in the built-in dependency injection container and HMAC Middleware Components for the Application Request Processing Pipeline. So, please modify the Program class as follows:

using HMACServerApp.Models;
namespace HMACServerApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            //Adding In-Memory Caching
            builder.Services.AddMemoryCache();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();

            app.UseAuthorization();

            //Registering the HmacAuthenticationMiddleware
            app.UseMiddleware<HMACAuthenticationMiddleware>();

            app.MapControllers();

            app.Run();
        }
    }
}
Creating Employee Model:

Let us create the employee model, which we will use to perform the database CRUD operations. So, add a class file named Employee.cs and then copy and paste the following code:

namespace HMACServerApp.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Position { get; set; }
        public decimal Salary { get; set; }
    }
}
Creating Employees Controller

Next, we need to create the Employees Controller, which will expose the endpoints to perform the Database CRUD Operations using the Employee model. So, create an API Empty Controller named EmployeesController within the Controllers folder and then copy and paste the following code:

using HMACServerApp.Models;
using Microsoft.AspNetCore.Mvc;

namespace HMACServerApp.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeesController : ControllerBase
    {
        private static List<Employee> _employees = new List<Employee>
        {
            new Employee { Id = 1, Name = "Alice Smith", Position = "Manager", Salary = 75000 },
            new Employee { Id = 2, Name = "Bob Johnson", Position = "Developer", Salary = 60000 },
            new Employee { Id = 3, Name = "Carol White", Position = "Designer", Salary = 55000 }
        };

        // GET: api/Employees
        [HttpGet]
        public ActionResult<IEnumerable<Employee>> GetEmployees()
        {
            return _employees;
        }

        // GET: api/Employees/5
        [HttpGet("{id}")]
        public ActionResult<Employee> GetEmployee(int id)
        {
            var employee = _employees.FirstOrDefault(e => e.Id == id);

            if (employee == null)
            {
                return NotFound();
            }

            return employee;
        }

        // POST: api/Employees
        [HttpPost]
        public ActionResult<Employee> PostEmployee(Employee employee)
        {
            employee.Id = _employees.Max(e => e.Id) + 1;
            _employees.Add(employee);

            return CreatedAtAction(nameof(GetEmployee), new { id = employee.Id }, employee);
        }

        // PUT: api/Employees/5
        [HttpPut("{id}")]
        public IActionResult PutEmployee(int id, Employee employee)
        {
            var existingEmployee = _employees.FirstOrDefault(e => e.Id == id);
            if (existingEmployee == null)
            {
                return NotFound();
            }

            existingEmployee.Name = employee.Name;
            existingEmployee.Position = employee.Position;
            existingEmployee.Salary = employee.Salary;

            return NoContent();
        }

        // DELETE: api/Employees/5
        [HttpDelete("{id}")]
        public IActionResult DeleteEmployee(int id)
        {
            var employee = _employees.FirstOrDefault(e => e.Id == id);
            if (employee == null)
            {
                return NotFound();
            }

            _employees.Remove(employee);
            return NoContent();
        }
    }
}

Creating the Client Application:

Let us create the Client Application and Consume the above services using HMAC Authentication. For the Client Application, we are going to create a Console Application. So, create a .NET Core Console Application named HMACClientApp.

Creating HMAC Helper Class:

Create a class file named HMACHelper.cs within the Client Console Application, and then copy and paste the following code. This class contains one static method called GenerateHmacToken, which will generate the HMAC token. Here, we need to implement the same logic to generate the token we used in the server application to generate the HMAC token.

using System.Security.Cryptography;
using System.Text;

namespace HMACClientApp
{
    public class HMACHelper
    {
        // Method to generate HMAC token
        public static string GenerateHmacToken(string method, string path, string clientId, string secretKey, string requestBody = "")
        {
            // Generate a unique nonce
            var nonce = Guid.NewGuid().ToString();

            // Get the current UTC timestamp in ISO 8601 format
            var timestamp = DateTime.UtcNow.ToString("o");

            // Build the request content by concatenating method, path, nonce, and timestamp
            var requestContent = new StringBuilder()
                .Append(method.ToUpper())
                .Append(path.ToUpper())
                .Append(nonce)
                .Append(timestamp);

            // If the HTTP method is POST or PUT, append the request body to the request content
            if (method == HttpMethod.Post.Method || method == HttpMethod.Put.Method)
            {
                requestContent.Append(requestBody);
            }

            // Convert secret key and request content to bytes
            var secretBytes = Encoding.UTF8.GetBytes(secretKey);
            var requestBytes = Encoding.UTF8.GetBytes(requestContent.ToString());

            // Create HMACSHA256 instance with the secret key
            using var hmac = new HMACSHA256(secretBytes);

            // Compute the hash of the request content
            var computedHash = hmac.ComputeHash(requestBytes);

            // Convert the computed hash to base64 string (token)
            var computedToken = Convert.ToBase64String(computedHash);

            // Concatenate clientId, computedToken, nonce, and timestamp to form the final token
            return $"{clientId}|{computedToken}|{nonce}|{timestamp}";
        }
    }
}

The above class generates HMAC (Hash-based Message Authentication Code) tokens to secure API requests in a .NET application. The HMAC token is generated based on the HTTP method, request path, client ID, secret key, optional request body, nonce (a unique identifier), and timestamp.

Consuming the Services in Client Application:

Next, modify the Main method of the Program class as follows. Here, we consume the API services by calling the respective endpoints. We also pass the HMAC token using the Authorization header.

using System.Text;
namespace HMACClientApp
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            //You need to use proper Client Id, Secret and Base URL of the API
            var clientId = "ClientId1";
            var secretKey = "SecretKey1";
            var baseUrl = "https://localhost:7010/api/Employees";

            using var client = new HttpClient();

            // Create an Employee
            var employee = new
            {
                Name = "Pranaya Rout",
                Position = "Developer",
                Salary = 60000
            };

            var requestBody = System.Text.Json.JsonSerializer.Serialize(employee);
            var token = HMACHelper.GenerateHmacToken("POST", "/api/employees", clientId, secretKey, requestBody);

            var requestMessage = new HttpRequestMessage(HttpMethod.Post, baseUrl)
            {
                Content = new StringContent(requestBody, Encoding.UTF8, "application/json")
            };
            requestMessage.Headers.Add("Authorization", $"HMAC {token}");

            var response = await client.SendAsync(requestMessage);
            var responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"POST Response: {responseContent}");

            // Get all Employees
            token = HMACHelper.GenerateHmacToken("GET", "/api/employees", clientId, secretKey);

            requestMessage = new HttpRequestMessage(HttpMethod.Get, baseUrl);
            requestMessage.Headers.Add("Authorization", $"HMAC {token}");

            response = await client.SendAsync(requestMessage);
            responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"GET Response: {responseContent}");

            // Assuming the created employee has an ID of 1 (adjust as necessary)

            // Get Employee by ID
            var employeeId = 1;
            var getByIdUrl = $"{baseUrl}/{employeeId}";

            token = HMACHelper.GenerateHmacToken("GET", $"/api/employees/{employeeId}", clientId, secretKey);

            requestMessage = new HttpRequestMessage(HttpMethod.Get, getByIdUrl);
            requestMessage.Headers.Add("Authorization", $"HMAC {token}");

            response = await client.SendAsync(requestMessage);
            responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"GET by ID Response: {responseContent}");

            // Update Employee
            var updatedEmployee = new
            {
                Id = employeeId,
                Name = "Rakesh Sharma",
                Position = "Senior Developer",
                Salary = 80000
            };
            requestBody = System.Text.Json.JsonSerializer.Serialize(updatedEmployee);
            token = HMACHelper.GenerateHmacToken("PUT", $"/api/employees/{employeeId}", clientId, secretKey, requestBody);

            requestMessage = new HttpRequestMessage(HttpMethod.Put, getByIdUrl)
            {
                Content = new StringContent(requestBody, Encoding.UTF8, "application/json")
            };
            requestMessage.Headers.Add("Authorization", $"HMAC {token}");

            response = await client.SendAsync(requestMessage);
            responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"PUT Response: {responseContent}");

            // Delete Employee
            token = HMACHelper.GenerateHmacToken("DELETE", $"/api/employees/{employeeId}", clientId, secretKey);

            requestMessage = new HttpRequestMessage(HttpMethod.Delete, getByIdUrl);
            requestMessage.Headers.Add("Authorization", $"HMAC {token}");

            response = await client.SendAsync(requestMessage);
            responseContent = await response.Content.ReadAsStringAsync();
            Console.WriteLine($"DELETE Response: {responseContent}");

            Console.ReadKey();
        }
    }
}

With the above changes in place, first, run the Server application and then run the client application, and you should see it is working as expected. If everything works as expected, then you will see the following output:

How to Implement HMAC Authentication in ASP.NET Core Web API Application with an Example

What is a Replay Attack in HMAC Authentication?

A replay attack is a form of network attack in which a valid data transmission is maliciously or fraudulently repeated or delayed. In the context of HMAC (Hash-based Message Authentication Code) authentication, this can occur when an attacker intercepts a legitimate request with its HMAC signature and then resends it to the server. Since the HMAC signature is valid, if the server does not have mechanisms to recognize that the request has been replayed, it might process the request as if it is a new one, potentially leading to unauthorized actions.

How do Replay Attacks Work?
  • Interception: An attacker captures a legitimate HTTP request that includes the HMAC signature and all the relevant data (like API keys, timestamps, and the request body).
  • Replay: The attacker sends this intercepted request to the server one or more times.
  • Processing: If the server does not properly verify the uniqueness or timeliness of each request, it processes the replayed request just as it would a new, legitimate request.
How Do We Prevent Replay Attacks?

To prevent replay attacks using HMAC authentication, we need to implement the following strategies:

  • Timestamps: Include a timestamp in the request header, which the server checks to ensure the request is not too old. This limits the time window during which a captured request can be replayed.
  • Nonce: A nonce (number used once) is a unique number the client includes in each request. The server keeps track of all used nonces to process each request only once.
When Should We Use HMAC Authentication in ASP.NET Core Web API?

HMAC is useful in scenarios where we need to ensure that a message has not been tampered with and is from a trusted source. The following are some specific use cases of HMAC Authentication in ASP.NET Core Web API Application:

  • API Security: When building APIs (Restful Services), especially those exposed over the internet, HMAC ensures that requests are from authorized clients and have not been altered during transmission.
  • Sensitive Data Transmission: When transmitting sensitive data, such as financial or personal information, HMAC provides an additional layer of security by ensuring the integrity and authenticity of the data.
  • Preventing Replay Attacks: Including a timestamp or nonce in the HMAC Signature calculation helps prevent replay attacks, in which an attacker could resend a captured valid request.
Real-time Example Scenario of HMAC Authentication:

Imagine you have an e-commerce application where clients (mobile apps, web apps) need to access sensitive endpoints like payment processing. You want to ensure that only authorized clients can access these endpoints and that the requests are not tampered with. By implementing HMAC, we add an additional layer of security that verifies the authenticity and integrity of the requests, thus protecting sensitive operations from potential attacks.

In the next article, I will discuss how to Implement Encryption and Decryption in ASP.NET Core Web API Applications. In this article, I explain How to Implement HMAC Authentication in ASP.NET Core Web API Application with an Example. I hope you enjoy this HMAC Authentication in ASP.NET Core Web API article.

Leave a Reply

Your email address will not be published. Required fields are marked *