#Code Web.Api user authentication using secure salted hash password

Use a web.api service to verify users credentials

Introduction

We have a few applications running on different devices (server, mobile, tablet) and we would like to provide a service able to verify the user credentials. In this story we are going to create a web Api which accepts a Json representation of a single user credential and returns a bool parameter “isValid” once checked.

Prerequisites

Asp.Net Core 2.1 , Sql Server, TSql

Implementation

Create a table to store the user credential and other stuff.

CREATE TABLE [dbo].[UsersPlatform]([UserPlatformID] [bigint] IDENTITY(1,1) NOT NULL,
[UserID] [bigint] NOT NULL,
[DominioID] [tinyint] NULL,
[RiskProfileID] [tinyint] NULL,
[UserName] [varchar](8) NOT NULL,
[Password] [varchar](80) NULL,

[LevelID] [tinyint] NOT NULL,
[ActivationDate] [datetime] NULL,
[CloseDate] [datetime] NULL,
[CreationDate] [datetime] NOT NULL
CONSTRAINT [PK_UsersPlatform] PRIMARY KEY CLUSTERED
(
[UserPlatformID] ASC
)
WITH
(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

To complete the database scripts we need to create a store procedure to retrive the password saved for a specific username

CREATE PROCEDURE [dbo].[GetAuthHash]@username varchar(8) //input parameterAS
BEGIN
SET NOCOUNT ON; SELECT
UsersPlatform.UserID,
UsersPlatform.Password

FROM UsersPlatform
WHERE
UsersPlatform.UserName = @username AND
UsersPlatform.CloseDate IS NULL
END
GO

Important : in this story we will use the integrated security authentication to login on our db. Remember to use different servers and set up specific firewall rules between them to improve your application safety.

In our web.api project, add the connect string in the appsetting.json file.

Connection string for Sql Server instance

In the Dal (Data Access Layer) directory we can create the classes involved in the sql data interaction.

I often use a base class per helper method and one derived class per each table with its specific method. Consequently, as you can imagine, we’ll not be using an ORM ;)

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using Microsoft.Extensions.Configuration;
namespace IT.Users.Dal
{
public class DBFunctions
{
..... #region Helper Methods public DataSet GetDataSet(SqlCommand cmd)
{
....
}
protected SqlParameter Param(string name, object value, SqlDbType type)
{
SqlParameter param = new SqlParameter(name, value);
param.SqlDbType = type;
return param;
}
protected SqlCommand NewCommand(SqlConnection connection, string text, CommandType type, params SqlParameter[] parameters)
{
var command = new SqlCommand(text, connection);
command.CommandType = type;
command.Parameters.AddRange(parameters);
return command;
}
protected SqlConnection NewConnection()
{
var connection = new SqlConnection(ConnectionString);
return connection;
}
private string ConnectionString
{
get {
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build(); return configuration.GetConnectionString("SqlServer");
}
}
#endregion
}
}

The DBFunction class provides a SqlConnection calling the NewConnection method and a SqlCommand calling the NewCommand. The ConnectionString method retrieves the connection string reading the appsetting.json file.

The next step concerns the DBAuthRequest class. In this class we have a method used to call the store procedure and read the data from our table.

public class DBAuthRequest : DBFunctions
{
public async Task<KeyValuePair<long,string>> GetHashCode(AuthRequest auth)
{
KeyValuePair<long, string> idHash; try
{
using (SqlConnection conn = NewConnection())
{
using (SqlCommand cmd = NewCommand(conn, "GetAuthHash", CommandType.StoredProcedure, Param("@username", auth.Username, SqlDbType.VarChar)))
{
await conn.OpenAsync();
cmd.CommandTimeout = DataBase.commandTimeout;
using (var dr = await cmd.ExecuteReaderAsync())
{
while (await dr.ReadAsync())
{
idHash = new KeyValuePair<long, string> ((long)dr["userID"], dr["password"].ToString());
}
dr.Close();
}
conn.Close();
}
}
}
catch (Exception ex)
{
throw ex;
}
return idHash;
}
}

The async Task opens a connection to the Sql Server instance, calls the “GetAuthHash” store procedure -passing the “username”- and returns a KeyValuePair containing the userID and the hash salted password.

Having in the DB the hash-salted password, we need to have a method that verifys if the password provided to the api, once encrypted, is equal to the saved one.

The “salt” uses the userID so the encrypt method should be the following:

public class Security
{
public class PasswordHash
{
public static string Create(long userID, string password)
{
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
// the salt used for derivation process
salt: BitConverter.GetBytes(userID),

// random function for key derivation using SHA256 hash function
prf: KeyDerivationPrf.HMACSHA256,
// number of iteractions of random function
iterationCount: 10000,
// desired length in bytes
numBytesRequested: 256 / 8));
return hashed;
}
}
}

The last 3 steps consist in the creation of the auth model, a controller and a business logic layer class.

public class AuthRequest
{
public string Username { get; set; }
public string Password { get; set; }
public bool IsValid { get; set; }
public static async Task<KeyValuePair<long, string>> GetHashCode (AuthRequest auth)
{
DBAuthRequest dbAuth = new DBAuthRequest();
KeyValuePair<long, string> idHash;
try
{
idHash = await Task.FromResult(result:dbAuth.GetHashCode(auth).Result);
}
catch (Exception)
{
throw;
}
finally
{
dbAuth = null;
}
return idHash;
}
}

The Model: it is basically an AuthRequest model with 3 properties and a method. It instances a DBAuthRequest class and returns the userID and the salt-hash password saved on the SqlServer.

The BLL: it is a simple class used to compare the data.

public class Authentication
{
public static async Task<AuthRequest> GetValidation(AuthRequest auth)
{
try {
KeyValuePair<long, string> idHash = await Task.FromResult(result: AuthRequest.GetHashCode(auth).Result);
if (idHash.Key > 0)
{
auth.IsValid = (Security.PasswordHash.Create(idHash.Key, auth.Password) == idHash.Value);
}
}
catch (Exception){throw;} return auth; }
}

The static async method compares the hash created with the saved one and, if it is equal, modifies the “isValid” property of the AuthRequest model to “true”.

This story finishes with the controller.

[Route("api/[controller]")]
public class AuthRequestController : Controller
{
....
// POST api/values[HttpPost]
public async Task<AuthRequest> PostAsync([FromBody]AuthRequest authRequest)
{
if (authRequest != null)
{
authRequest = await Task.FromResult(result: Authentication.GetValidation(authRequest).Result);
}

return authRequest;
}
}

The AuthRequestController receives a Json representation posted by an application (hopefully under a HTTPS connection) an returns the same modified representation in case the passwords match.

Here below the full project:

Important: in this case, the choice to have the password passed to the web api in clear is because we won’t have the hashing algorithm implemented in different solutions, and that is a good way to protect the whole repository.

Cheers ;)

Massimo M.

--

--

--

_fintech.developer

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

In the Main() function of C++

Rift Xtended Reality Technologies: The Manifesto

Goa v3.1 released: Primitive user types and HTTP streaming!

Laravel 7.x — P2 : Modifying the Welcome View

Dino Cajic on Laravel 7 Home Page View

Cake and Air!

Combining Market Timing Patterns With the Bollinger Bands—A Python Case Study.

24/7 Accelerometer Tracking with Apple Watch

PIPOW: A framework for communicating project issues to clients and stakeholders

Dawson from Dawson’s Creek crying

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Massimo Mannoni

Massimo Mannoni

_fintech.developer

More from Medium

Cake v2.2.0 released

Scan SonarQube branches and pull request branches without paid licenses

Solve SSL issue when host Identity Server 4 behind nginx

Setting up self-signed SSL certificates for local development in WSL2