Introduction
In this article we'll go through how to implement Basic HTTP authentication in a .NET 3.1 API with C#.
API has just two endpoints/routes to demonstrate authenticating with basic http authentication and accessing a restricted route.
/users/authenticate - public route that accepts HTTP POST requests containing the username and password in the body. If the username and password are correct, then the user details are returned.
/Users - secure route that accepts HTTP GET requests and returns a list of all the users in the application if the HTTP Authorization header contains valid basic authentication credentials. If there are no basic auth credentials or the credentials are invalid, then a 401 Unauthorized response is returned.
Prerequisites
- .NET 3.1
- Visual Studio 2019
Step 1. Open Visual Studio 2019 and create a new project.
Step 2. Select the “Asp.Net Core Web API” template and click on Next.
Step 3. Provide Project name and location.
Step 4. Select Target Framework and click on Create button.
Step 5. Once project is created you can be able to see only basic view in solution explore. Please refer below screens.
Step 6. Need to install dependency from Genet packages.
Step 7. Please add below lines of code in startup.cs.
services.AddAuthentication("BasicAuthentication")
AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
services.AddScoped<IUserService, UserService>();
Step 8. Next, we need to create UsersController.cs and add below code in controller class.
The controller actions are secured with basic authentication using the [Authorize] attribute, except for the Authenticate method which allows public access by overriding the [Authorize] attribute on the controller with the [AllowAnonymous] attribute on the action method.
Path: /Controllers/UsersController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Stripe;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Page Load logic goes here
}
protected void Button1_Click(object sender, EventArgs e)
{
StripeCustomer current = GetCustomer();
// Uncomment the following lines if you have a method getaTraildays()
// int? days = getaTraildays();
// if (days != null)
// {
int chargetotal = 100; // Convert.ToInt32((3.33 Convert.ToInt32(days) 100));
var mycharge = new StripeChargeCreateOptions();
mycharge.AmountInCents = chargetotal;
mycharge.Currency = "USD";
mycharge.CustomerId = current.Id;
string key = "sk_test_Uvk2cHkpYRTC2Rl4ZUfs4Fvs";
var chargeservice = new StripeChargeService(key);
StripeCharge currentcharge = chargeservice.Create(mycharge);
// }
}
private StripeCustomer GetCustomer()
{
var mycust = new StripeCustomerCreateOptions();
mycust.Email = "mailto:[email protected]";
mycust.Description = "Rahul mailto:pandey([email protected])";
mycust.CardNumber = "4242424242424242";
mycust.CardExpirationMonth = "10";
mycust.CardExpirationYear = "2014";
// mycust.PlanId = "100";
mycust.CardName = "Rahul Pandey";
mycust.CardAddressCity = "ABC";
mycust.CardAddressCountry = "USA";
mycust.CardAddressLine1 = "asbcd";
// mycust.TrialEnd = getrialend();
var customerservice = new StripeCustomerService("sk_test_Uvk2cHkpYRTC2Rl4ZUfs4Fvs");
return customerservice.Create(mycust);
}
}
c
Step 9. Next create User.cs to get and set fields.
The user entity class represents the data for a user in the application. Entity classes are used to pass data between different parts of the application.
Path: /Modules /User.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CEEAWebAPI.Models
{
public class User
{
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string EmailId { get; set; }
}
}
Step 10. Basic Authentication Handler.
Path: / Modules /BasicAuthenticationHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using CEEAWebAPI.Models;
namespace CEEAWebAPI.Models
{
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IUserService _userService;
public BasicAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IUserService userService)
: base(options, logger, encoder, clock)
{
_userService = userService;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if(!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header");
User user = null;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
var username = credentials[0];
var password = credentials[1];
user = await _userService.Authenticate(username, password);
}
catch
{
return AuthenticateResult.Fail("Invalid Authorization Header");
}
if (user == null)
return AuthenticateResult.Fail("Invalid Username or Password");
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
new Claim(ClaimTypes.Name, user.Username),
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
//throw new NotImplementedException();
}
}
}
The basic authentication handler is .NET middleware that handles request authentication by inheriting from the .NET AuthenticationHandler base class and overriding the HandleAuthenticateAsync() method.
Step 11. User Service
Path: / Modules /UserService.cs
The user service contains a method for authenticating user credentials, and a method for getting all users in the application.
The top of the file contains an interface that defines the user service, below that is the concrete user service class that implements the interface.
On successful authentication the Authenticate method returns the user details, the client application should then include the base64 encoded user credentials in the HTTP Authorization header of subsequent api requests to access secure endpoints.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace CEEAWebAPI.Models
{
public interface IUserService
{
Task<User> Authenticate(string username, string password);
Task<IEnumerable<User>> GetAll();
}
public class UserService : IUserService
{
private readonly IConfiguration _configuration;
public UserService(IConfiguration configuration)
{
_configuration = configuration;
}
List<User> _users = new List<User>();
//private List<User> _users = new List<User>
//{
// new User { UserId = "1", FirstName = "Test", LastName = "User", Username = "test1", Password = "test1" }
// //var a = new Getproducts();
// };
public async Task<User> Authenticate(string username, string password)
{
GetUserTable();
var genpassword= GenerateMySQLHash(password);
var user = await Task.Run(() => _users.SingleOrDefault(x => x.Username == username && x.Password == genpassword));
// return null if user not found
if (user == null)
return null;
// authentication successful so return user details without password
user.Password = null;
return user;
}
public async Task<IEnumerable<User>> GetAll()
{
GetUserTable();
//return users without passwords
return await Task.Run(() => _users.Select(x =>
{
x.Password = null;
return x;
}));
}
// Getting data from user tabel from database
private List<User> GetUserTable()
{
string query = @"select * from restapi.user;";
DataTable table = new DataTable();
string sqlDataSource = _configuration.GetConnectionString("EmployeeAppcon");
MySqlDataReader myReader;
using (MySqlConnection mycon = new MySqlConnection(sqlDataSource))
{
mycon.Open();
using (MySqlCommand mycommond = new MySqlCommand(query, mycon))
{
myReader = mycommond.ExecuteReader();
table.Load(myReader);
myReader.Close();
mycon.Close();
}
}
for (int i = 0; i < table.Rows.Count; i++)
{
User usr = new User();
usr.UserId = Convert.ToInt32(table.Rows[i]["userid"]);
//usr.UserId = table.Rows[i]["userid"].ToString();
usr.Username = table.Rows[i]["userName"].ToString();
usr.Password = table.Rows[i]["password"].ToString();
usr.FirstName = table.Rows[i]["firstName"].ToString();
usr.LastName = table.Rows[i]["lastName"].ToString();
usr.EmailId = table.Rows[i]["emailid"].ToString();
_users.Add(usr);
}
return (_users);
}
// password converstion to Hash key
public string GenerateMySQLHash(string key)
{
byte[] keyArray = Encoding.UTF8.GetBytes(key);
SHA1Managed enc = new SHA1Managed();
byte[] encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray));
StringBuilder myBuilder = new StringBuilder(encodedKey.Length);
foreach (byte b in encodedKey)
myBuilder.Append(b.ToString("X2"));
return "*" + myBuilder.ToString();
}
}
}
Finally we the help post man we can check our API Request.